fix(deps): bump python-dotenv to 1.2.2 for VULN-88817 - #151
Conversation
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>
…env-upgrade Regenerate poetry.lock after conflict with merged dependency PRs. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@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>
There was a problem hiding this comment.
💡 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".
| opentelemetry-exporter-otlp-proto-http = "^1.38.0" | ||
| filelock = ">=3.20.1" | ||
| idna = ">=3.15,<4" | ||
| python-dotenv = ">=1.2.2" |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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>
fercor-cisco
left a comment
There was a problem hiding this comment.
🤖 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:
examples/agent/langgraph-fsi-agent/before/poetry.lock:3590-3599lockspython-dotenv1.1.1 exactly, with hashes, ingroups = ["main"]. This is the precise version and artifact type (poetry.lock) that VULN-88817 flagged. Anyone runningpoetry installin that example installs the vulnerable release, and a FOSSA rescan of the branch is likely to re-open the ticket on this path.- Floors that still admit <1.2.2:
examples/agent/langgraph-fsi-agent/before/pyproject.toml:16—>=1.1.0examples/agent/langgraph-fsi-agent/after/pyproject.toml:17—>=1.1.0examples/agent/langgraph-otel/pyproject.toml:17—>=1.1.0examples/agent/langgraph-telecom-agent/pyproject.toml:17—>=1.1.0examples/agent/langgraph-traceloop/pyproject.toml:13—>=1.1.0examples/agent/langgraph-open-telemetry/pyproject.toml:11—>=1.1.0examples/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 movingmain. Sincepoetry.lockis one of the changed files and carries acontent-hashbound topyproject.toml, please rebase and re-runpoetry lockrather than hand-resolving the conflict — a manually merged lock can end up with acontent-hashthat no longer matchespyproject.toml, which makespoetry installfail 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.lockis 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 documentingpoetry installfrom 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 andexamples/logging-samples/openai-responses/requirements.txtare 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.tomlsources over time.
| pyjwt==2.13.0 | ||
| python-dateutil==2.9.0.post0 | ||
| python-dotenv==1.1.0 | ||
| python-dotenv==1.2.2 |
There was a problem hiding this comment.
🟡 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:17—required_version="3.8", exits only below 3.8start.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
There was a problem hiding this comment.
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.
| opentelemetry-exporter-otlp-proto-http = "^1.38.0" | ||
| filelock = ">=3.20.1" | ||
| idna = ">=3.15,<4" | ||
| python-dotenv = ">=1.2.2" |
There was a problem hiding this comment.
🟡 minor (question): splunk_ao never imports dotenv — rg -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
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
🤖 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:5—requires-python = ">=3.9", now withpython-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:1doesfrom dotenv import load_dotenv, butpython-dotenvis 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. Declaringpython-dotenv>=1.2.2here 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 asexamples/agent/langchain-agent:app.py:14,app-simple.py:5, andexperiment_compare_two_models.py:11all importdotenv, butpython-dotenvis undeclared in this requirements file, so it fell outside this PR's sweep. Worth declaring it explicitly with the>=1.2.2floor.examples/agent/langchain-agent/00-tutorial/00-Getting-Started.md:8-8: Advertises "Python 3.8+" and the tutorial body (line 56) usesfrom 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.
| 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+") |
There was a problem hiding this comment.
🟠 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.
| 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
| requires-python = ">=3.9" | ||
| dependencies = [ | ||
| "python-dotenv>=1.0.0", | ||
| "python-dotenv>=1.2.2", |
There was a problem hiding this comment.
🟠 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.)
| 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
| python-dotenv==1.2.2 | ||
| # via pydantic-settings |
There was a problem hiding this comment.
🟡 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.
| 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 |
There was a problem hiding this comment.
🟡 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 |
There was a problem hiding this comment.
🟡 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>
Summary
Fixes VULN-88817 by upgrading
python-dotenvto>=1.2.2.Prior to 1.2.2,
set_key()andunset_key()follow symbolic links when rewriting.envfiles. A local attacker can overwrite arbitrary files via a crafted symlink when a cross-device rename fallback is triggered.Changes
python-dotenvconstraint to>=1.2.2poetry.lockVulnerability