Skip to content

fix(deps): bump python-dotenv to 1.2.2 for VULN-88817 - #151

Open
shuningc wants to merge 10 commits into
mainfrom
VULN-88817-python-dotenv-upgrade
Open

fix(deps): bump python-dotenv to 1.2.2 for VULN-88817#151
shuningc wants to merge 10 commits into
mainfrom
VULN-88817-python-dotenv-upgrade

Conversation

@shuningc

@shuningc shuningc commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes VULN-88817 by upgrading python-dotenv to >=1.2.2.

Prior to 1.2.2, set_key() and unset_key() follow symbolic links when rewriting .env files. A local attacker can overwrite arbitrary files via a crafted symlink when a cross-device rename fallback is triggered.

Changes

  • Bump python-dotenv constraint to >=1.2.2
  • Regenerate poetry.lock
  • Update example dependency pins where applicable

Vulnerability

  • Ticket: VULN-88817
  • Package: python-dotenv
  • Fixed in: 1.2.2

shuningc and others added 3 commits July 27, 2026 13:15
Lock-only bump for transitive python-dotenv (1.1.1 → 1.2.2) to fix
symlink-following in set_key()/unset_key(). Example pins set to 1.2.2.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add direct python-dotenv floor and regenerate poetry.lock (1.1.1 → 1.2.2).
Fixes symlink-following in set_key()/unset_key(). Reverts accidental
requests bump from prior lock edit.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shuningc
shuningc marked this pull request as ready for review July 27, 2026 21:04
…env-upgrade

Regenerate poetry.lock after conflict with merged dependency PRs.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shuningc

shuningc commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

Resolve poetry.lock conflict and regenerate lock with python-dotenv 1.2.2
on latest main.

Co-authored-by: Cursor <cursoragent@cursor.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cdc6b7d634

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pyproject.toml
opentelemetry-exporter-otlp-proto-http = "^1.38.0"
filelock = ">=3.20.1"
idna = ">=3.15,<4"
python-dotenv = ">=1.2.2"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Constrain dotenv in the a2a package too

This lower bound only fixes the root splunk-ao package; repo-wide search still shows the separately packaged splunk-ao-a2a manifest allows python-dotenv>=1.1.0 (splunk-ao-a2a/pyproject.toml:52). In environments that install splunk-ao-a2a standalone or use constraints that keep dotenv below 1.2.2, the vulnerable versions remain admissible, so the security bump does not actually cover all published package manifests in this repo.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already addressed in the current head (d5cc6f7) — splunk-ao-a2a/pyproject.toml:53 now reads "python-dotenv>=1.2.2" in the examples extra. The comment was authored against cdc6b7d, before that bump landed.

The underlying concern about non-root manifests is valid, though, and is still live for other paths — most importantly examples/agent/langgraph-fsi-agent/before/poetry.lock, which pins 1.1.1 exactly. Flagged separately.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming this is resolved at the current head: splunk-ao-a2a/pyproject.toml:53 reads "python-dotenv>=1.2.2" in the examples extra. The comment was authored against cdc6b7d, before that bump landed.

The follow-up concern about non-root manifests is also resolved for the path called out: examples/agent/langgraph-fsi-agent/before/poetry.lock:3597 now locks 1.2.2, not 1.1.1. I checked the remaining lock files repo-wide — poetry.lock and examples/agent/langgraph-fsi-agent/before/poetry.lock are the only two, and both are at 1.2.2. This thread can be resolved.

@shuningc
shuningc marked this pull request as draft August 4, 2026 22:08
shuningc and others added 2 commits August 4, 2026 16:30
Align splunk-ao-a2a dependency group with root >=1.2.2 so standalone
installs and constraint resolution cannot admit vulnerable dotenv versions.

Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve conflicts after requests>=2.33.0 landed on main; keep both
python-dotenv>=1.2.2 and requests floors with lock at 1.2.2/2.34.2.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shuningc
shuningc marked this pull request as ready for review August 5, 2026 06:25

@fercor-cisco fercor-cisco left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This review was generated by the Astra agent (claude-opus-5). It may contain mistakes.

Verdict: request_changes — The sweep is incomplete: a committed lockfile in the repo still pins the exact vulnerable python-dotenv 1.1.1, and several example manifests still allow pre-1.2.2, so the vulnerability remains reachable from this repo on main.

General Comments

  • 🟠 major (security): The sweep over in-repo manifests is incomplete, and inconsistently so.

This PR updates 4 example dependency files, which establishes that examples are in scope for the fix. But repo-wide search shows the vulnerable range is still admissible — and in one case still pinned — elsewhere:

  1. examples/agent/langgraph-fsi-agent/before/poetry.lock:3590-3599 locks python-dotenv 1.1.1 exactly, with hashes, in groups = ["main"]. This is the precise version and artifact type (poetry.lock) that VULN-88817 flagged. Anyone running poetry install in that example installs the vulnerable release, and a FOSSA rescan of the branch is likely to re-open the ticket on this path.
  2. Floors that still admit <1.2.2:
    • examples/agent/langgraph-fsi-agent/before/pyproject.toml:16>=1.1.0
    • examples/agent/langgraph-fsi-agent/after/pyproject.toml:17>=1.1.0
    • examples/agent/langgraph-otel/pyproject.toml:17>=1.1.0
    • examples/agent/langgraph-telecom-agent/pyproject.toml:17>=1.1.0
    • examples/agent/langgraph-traceloop/pyproject.toml:13>=1.1.0
    • examples/agent/langgraph-open-telemetry/pyproject.toml:11>=1.1.0
    • examples/chatbot/elevenlabs-chatbot/{pyproject.toml:7,requirements.txt:1}, examples/rag/cli-rag-demo/requirements.txt:25, examples/agent/minimal-agent-example/requirements.txt:20, examples/experiments/upload_experiment/requirements.txt:2>=1.0.0

Item 1 is the one I'd consider blocking — it is a concrete vulnerable pin committed to the repo, not merely a permissive range. Please regenerate that lockfile (or, if that example is dead and intentionally frozen, say so on the ticket so ProdSec can VEX-scope it rather than leaving it silently behind).

For item 2, either raise the floors for consistency with the 4 files you did touch, or explain in the PR body why unpinned/loose example manifests are considered out of scope — right now a reader can't tell which files were deliberately skipped versus missed. (Entries that are bare python-dotenv with no specifier will resolve to the latest release and are genuinely fine.)

  • 🟡 minor (other): The PR is currently in a conflicted state (mergeable_state: dirty, rebaseable: false) after 7 commits against a moving main. Since poetry.lock is one of the changed files and carries a content-hash bound to pyproject.toml, please rebase and re-run poetry lock rather than hand-resolving the conflict — a manually merged lock can end up with a content-hash that no longer matches pyproject.toml, which makes poetry install fail in CI (or worse, silently drift).

Follow-ups

Suggested follow-up work that could be tracked as Jira tickets:

  • examples/agent/langgraph-fsi-agent/before/poetry.lock:1-1: Committed example lockfiles (examples/agent/langgraph-fsi-agent/before/poetry.lock is the only one) are not covered by any automated dependency-update or scan job, so they drift and silently reintroduce fixed CVEs. Consider either adding them to Dependabot/Renovate coverage or deleting them and documenting poetry install from the manifest instead, so future VULN tickets don't need a manual repo-wide grep to find every stale pin.
  • examples/rag/elastic-chatbot-rag-app/requirements.txt:381-381: This file and examples/logging-samples/openai-responses/requirements.txt are machine-generated (pip-compile / uv pip compile, per their headers) but were hand-edited here. The edits are consistent with what a regeneration would produce today, so no action is needed for this PR — but a repo convention or CI check that these are regenerated rather than edited would prevent the generated files from diverging from their .in/pyproject.toml sources over time.

pyjwt==2.13.0
python-dateutil==2.9.0.post0
python-dotenv==1.1.0
python-dotenv==1.2.2

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 minor (bug): python-dotenv 1.2.x raises its floor to Requires-Python >=3.10 (the root poetry.lock diff shows this: python-versions went >=3.9>=3.10). This example advertises and gates on older interpreters:

  • README.md:222 — "Ensure you're using Python 3.8+"
  • TUTORIAL.md:31 — "Check Python version (should be 3.8+)"
  • start.sh:17required_version="3.8", exits only below 3.8
  • start.bat:11 — "Please install Python 3.8+"

So on Python 3.8/3.9 start.sh now passes its version gate and then pip install -r requirements.txt fails to resolve. Either bump the documented/enforced floor to 3.10 in those four places, or leave this pin at a 1.1.x-compatible value and note the example as out of scope for the CVE. (README.md:49 and :283 already say 3.10+, so the docs are self-inconsistent today — worth settling on one number while you're here.)

🤖 Generated by the Astra agent

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This concern is valid and only partially addressed by the subsequent commits.

The four spots named here were fixed (README.md:222, TUTORIAL.md:31, start.sh:11,17,20, start.bat:11), and the author chose the "bump the floor to 3.10" option. But the actual enforcement logic in test_setup.py:18 was missed — only its message string was changed to "3.10+", while the comparison still reads py_version.minor < 8. So the specific failure mode described here still reproduces, just via a different entry point: on 3.9, test_setup.py reports ✅ Python 3.9.x is compatible and then blames a missing python-dotenv. Commented inline.

One refinement on the chosen number: 3.10 satisfies python-dotenv, but requirements.txt:8 pulls in splunk-ao, whose requires-python is >=3.11,<3.15. So this example is arguably 3.11+, not 3.10+, and picking 3.10 leaves a narrow band (exactly 3.10) that passes every gate and still fails to install. Worth settling on 3.11 across all five files.

Comment thread pyproject.toml
opentelemetry-exporter-otlp-proto-http = "^1.38.0"
filelock = ">=3.20.1"
idna = ">=3.15,<4"
python-dotenv = ">=1.2.2"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 minor (question): splunk_ao never imports dotenvrg -n dotenv src/ returns nothing. This adds python-dotenv to the published splunk-ao package's direct runtime requirements purely to raise a floor on a transitive dep (it arrives via galileo-core's python-dotenv = ">=1.1.1", pydantic-settings, and litellm).

I see this matches an existing convention here (filelock, idna are the same shape), so I'm not blocking on it — but it does mean every downstream consumer of splunk-ao now inherits a hard constraint on a package we don't use, and it will silently rot once galileo-core bumps its own floor past 1.2.2. Was bumping galileo-core (whose floor is the actual source of the 1.1.1 resolution) considered as an alternative? If the direct-floor approach is the deliberate house pattern for CVE remediation, a one-line comment above these entries marking them as vuln floors — rather than real dependencies — would keep a future reader from treating them as load-bearing.

Also worth a thought: unlike the adjacent idna = ">=3.15,<4", this has no upper bound, so a future python-dotenv 2.0 is admissible for a dependency we never exercise.

🤖 Generated by the Astra agent

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independently confirmed the factual premise: rg -n dotenv src/ returns nothing, so splunk_ao does not import dotenv, and poetry.lock:839 shows the resolution genuinely originates from galileo-core's python-dotenv = ">=1.1.1". The floor here is a pure vuln floor on a package the SDK never exercises.

On the concrete, non-blocking part of the ask — the explanatory comment — that suggestion has effectively been accepted elsewhere in this same PR but not here: examples/rag/elastic-chatbot-rag-app/requirements.in:1 now carries # security floor (VULN-88817 — python-dotenv symlink overwrite). Applying the identical one-liner above pyproject.toml:62 (and splunk-ao-a2a/pyproject.toml:53) would make the treatment consistent and costs nothing. I have raised that as a PR-level comment.

On the upper-bound point: I would not add one. A <2 cap on a floor-only constraint we never import converts a silent-rot problem into an active-blocker problem — it would force a release of splunk-ao the moment any real consumer of dotenv in the tree wants 2.x. The rot risk you describe (constraint becomes redundant once galileo-core moves past 1.2.2) is the cheaper failure mode, and the explanatory comment is the right mitigation for it.

Regenerate poetry.lock after merge, bump langgraph example dotenv floors
to >=1.2.2, and update langgraph-fsi-agent/before lock (1.1.1 -> 1.2.2).
Normalize every example requirements.txt/pyproject.toml to python-dotenv
>=1.2.2, add floor to elastic-chatbot requirements.in, align startup-
simulator Python docs with dotenv 1.2.x (>=3.10), and regenerate locks.

@shuningc shuningc left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This review was generated by the Astra agent (claude-opus-5). It may contain mistakes.

Verdict: request_changes — The dotenv 1.2.x bump raises the interpreter floor to Python 3.10, but the accompanying version-gate sweep is incomplete: test_setup.py updated only the message and not the comparison, and elevenlabs-chatbot still declares requires-python = ">=3.9" with a now-unresolvable dependency set.

General Comments

  • 🟠 major (bug): The Python-floor sweep triggered by this bump is incomplete.

python-dotenv 1.2.x raises Requires-Python to >=3.10 (visible in both lock diffs: python-versions >=3.9>=3.10). That makes every manifest that now pins >=1.2.2/==1.2.2 unresolvable on 3.8/3.9. The PR correctly chased this down for startup-simulator-3000's docs and shell scripts, but the same class of problem is left unfixed elsewhere:

  • examples/chatbot/elevenlabs-chatbot/pyproject.toml:5requires-python = ">=3.9", now with python-dotenv>=1.2.2. This is a declared, machine-readable floor that is now self-contradictory, not just prose.
  • examples/rag/cli-rag-demo/README.md:13 — "Python 3.8+"
  • examples/agent/weather-vibes-agent/00-tutorial/{01-weather-vibes-overview.md:9, 02-installation-instructions.md:9,261, weather-vibes-cookbook.md:24} — "Python 3.8+"

And within startup-simulator-3000, test_setup.py:18 had its message updated to "3.10+" while the comparison still tests < 8 (flagged inline).

A repo-wide rg -n '3\.8\+|3\.9|requires-python' over every directory whose manifest this PR touched would close this out. Suggest doing that sweep rather than fixing these four spots piecemeal, since the next dependency floor bump will hit the same set of files.

  • 🟡 minor (design): The remediation is applied inconsistently — some manifests document why the floor exists, most don't.

examples/rag/elastic-chatbot-rag-app/requirements.in:1 gets a genuinely useful marker:

# security floor (VULN-88817 — python-dotenv symlink overwrite)

None of the other ~30 edited manifests get this, including pyproject.toml:62 where a reviewer explicitly asked for it. This matters more than it looks: for the manifests where dotenv is not actually imported (root pyproject.toml, splunk-ao-a2a), a future reader has no way to distinguish "vuln floor, safe to drop once the transitive floor moves past it" from "load-bearing dependency". Adding the one-line comment to at least the two published package manifests would make these self-documenting and prevent them rotting into permanent unexplained constraints.

Follow-ups

Suggested follow-up work that could be tracked as Jira tickets:

  • examples/agent/langchain-agent/requirements.txt:1-7: main.py:1 does from dotenv import load_dotenv, but python-dotenv is not declared in this requirements file at all — it only works today by arriving transitively. Because it is undeclared, this PR's floor sweep necessarily skipped it. Declaring python-dotenv>=1.2.2 here would make the dependency explicit and bring it under the same security floor as its sibling examples. Pre-existing gap, not introduced by this PR.
  • examples/dataset-experiments/requirements.txt:1-3: Same pattern as examples/agent/langchain-agent: app.py:14, app-simple.py:5, and experiment_compare_two_models.py:11 all import dotenv, but python-dotenv is undeclared in this requirements file, so it fell outside this PR's sweep. Worth declaring it explicitly with the >=1.2.2 floor.
  • examples/agent/langchain-agent/00-tutorial/00-Getting-Started.md:8-8: Advertises "Python 3.8+" and the tutorial body (line 56) uses from dotenv import load_dotenv. Not strictly broken by this PR since this example's requirements file declares no dotenv pin, but it is the same stale-floor documentation issue and would be worth folding into a single repo-wide interpreter-floor cleanup.

Comment on lines +18 to +19
if py_version.major < 3 or (py_version.major == 3 and py_version.minor < 8):
print(f"❌ Python {py_version.major}.{py_version.minor} is too old. Need Python 3.8+")
print(f"❌ Python {py_version.major}.{py_version.minor} is too old. Need Python 3.10+")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 major (bug): The error message was updated to "3.10+" but the comparison still checks minor < 8. The two now disagree, and the branch is unreachable for exactly the versions the bump broke.

Concretely, on Python 3.9: py_version.minor < 8 is false, so this prints ✅ Python 3.9.x is compatible — then test_dependencies() reports python-dotenv missing (it cannot be installed at all on 3.9 now), and the script tells the user to run pip install -r requirements.txt, which is precisely the command that just failed to resolve. The setup-verification script actively misdiagnoses the one failure mode this PR introduced.

Note also that this example's requirements.txt:8 depends on splunk-ao, whose own requires-python is >=3.11 — so 3.10 is arguably the wrong number here and in start.sh:17. Worth confirming the intended floor is 3.10 and not 3.11 while settling this.

Suggested change
if py_version.major < 3 or (py_version.major == 3 and py_version.minor < 8):
print(f"❌ Python {py_version.major}.{py_version.minor} is too old. Need Python 3.8+")
print(f"❌ Python {py_version.major}.{py_version.minor} is too old. Need Python 3.10+")
if py_version.major < 3 or (py_version.major == 3 and py_version.minor < 10):
print(f"❌ Python {py_version.major}.{py_version.minor} is too old. Need Python 3.10+")

🤖 Generated by the Astra agent

Comment on lines +5 to +7
requires-python = ">=3.9"
dependencies = [
"python-dotenv>=1.0.0",
"python-dotenv>=1.2.2",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 major (bug): This manifest declares requires-python = ">=3.9" (line 5) but now requires python-dotenv>=1.2.2, which is Requires-Python >=3.10. The dependency set is unsatisfiable on 3.9: a user on 3.9 passes the metadata gate and then gets a resolver error with no indication of the real cause.

Unlike the startup-simulator-3000 case — which was prose and shell scripts — this is declared metadata that packaging tools act on, so it should be corrected rather than left as a doc inconsistency. README.md:64 already says "Python 3.10+", so 3.10 looks like the intended value and the >=3.9 here is just stale.

(Same applies to requirements.txt:1 in this directory, which has no interpreter metadata to correct — fixing pyproject.toml is what makes the constraint discoverable.)

Suggested change
requires-python = ">=3.9"
dependencies = [
"python-dotenv>=1.0.0",
"python-dotenv>=1.2.2",
requires-python = ">=3.10"
dependencies = [
"python-dotenv>=1.2.2",

🤖 Generated by the Astra agent

Comment on lines +381 to 382
python-dotenv==1.2.2
# via pydantic-settings

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 minor (other): This compiled file appears to have been hand-edited rather than regenerated from the updated requirements.in, so the two are now out of sync.

requirements.in was changed to declare python-dotenv as a direct requirement, but the annotation here still lists only the transitive source:

python-dotenv==1.2.2
    # via pydantic-settings

If pip-compile had actually been re-run, this would read # via\n# -r requirements.in\n# pydantic-settings — compare flask==3.1.0 at line 77 and elastic-opentelemetry==0.8.0 at line 63, which both carry the -r requirements.in annotation. The version pin happens to be correct, so nothing is broken today, but the next genuine pip-compile run will produce a spurious diff and the file no longer demonstrates that the pin is reproducible from its source.

Suggest re-running pip-compile in that directory and committing the result.

Suggested change
python-dotenv==1.2.2
# via pydantic-settings
python-dotenv==1.2.2
# via
# -r requirements.in
# pydantic-settings

🤖 Generated by the Astra agent

sentence-transformers>=2.5.1
numpy>=1.24.0 # Required by FAISS and sentence-transformers
python-dotenv>=1.0.0 # Added for dotenv support
python-dotenv>=1.2.2 # Added for dotenv support

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 minor (documentation): This pin now requires Python 3.10+, but README.md:13 in this same directory still advertises "Python 3.8+". A user following the README on 3.8/3.9 gets an unexplained resolver failure at pip install -r requirements.txt (README step 2). Please bump the documented prerequisite to match — the same fix that was applied to startup-simulator-3000's docs in this PR.

🤖 Generated by the Astra agent

fastapi
uvicorn
python-dotenv
python-dotenv>=1.2.2

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 minor (documentation): As above: this pin requires Python 3.10+, but this example's tutorial still advertises 3.8 in four places — 00-tutorial/01-weather-vibes-overview.md:9, 00-tutorial/02-installation-instructions.md:9, 00-tutorial/02-installation-instructions.md:261 ("Check Python Version: Ensure you're using Python 3.8 or newer"), and 00-tutorial/weather-vibes-cookbook.md:24.

Worth noting 02-installation-instructions.md:239 has a troubleshooting entry for No module named 'dotenv' whose suggested fix is pip install python-dotenv — which is exactly what will fail on 3.9, so the troubleshooting guide would send a user in a circle.

🤖 Generated by the Astra agent

Raise elevenlabs-chatbot requires-python to >=3.11 so metadata matches
python-dotenv>=1.2.2 and splunk-ao. Fix startup-simulator version checks
and docs to use 3.11 consistently (test_setup.py was still gating on 3.8).

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants