Skip to content

fix(deps+security): consolidate six packages into the constraints SSOT and confine repo_path (#17304, #17300) - #17309

Merged
mrveiss merged 4 commits into
mainfrom
issue-17304-dependabot-sweep
Sep 23, 2026
Merged

mrveiss merged 4 commits into
mainfrom
issue-17304-dependabot-sweep

Conversation

@mrveiss

@mrveiss mrveiss commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Thinking Path

Two things that look unrelated turned out to be the same defect at different layers.

58 Dependabot alerts were six packages. The count was duplication: one advisory seen through several resolution paths, and 48 of the 58 attributed to pyproject.toml, which declares no dependencies at all — it is [tool.black], [tool.isort] and [tool.mypy].

Of the six, four were already patched in every manifest. What was genuinely exposed was aiohttp, and it was exposed by drift: raised to the patched 3.14.3 in five requirements files and left at 3.13.4 in two others. Nothing compares a pin against the floor its siblings already adopted, so a security bump applied to "the requirements file" missed two of the seven that declare it.

constraints/shared.txt has been the dependency SSOT since #10524, and constraint-drift already fails any PR re-declaring a constrained version. It constrained two packages. These six were never brought in, which is the whole reason they could drift. So this is adoption, not invention.

The CodeQL half is the same shape. Alerts 452/453 survived #17301 because my fix there removed the "Did you mean: path?" disclosure and left the sinks — repo_path still reached Path(...).is_dir() and .iterdir() unconfined. Quieter, not closed.

What Changed

Dependency SSOT. aiohttp, chromadb, cryptography, nltk, pypdf, transformers moved into constraints/shared.txt (guarded packages 2 → 8). Versions removed from 15 component files. Three files that declared a constrained package without referencing the constraints at all now do, as do the requirements-ci fragments — their own header documents them as individually installable, so a bare name there would have resolved unconstrained.

repo_path confinement. Goes through validate_path with roots from AUTOBOT_CODE_SOURCE_ALLOWED_ROOTS. Unset defers to path_validator's own default rather than restating /opt/autobot — a second copy is a value that can drift from the one enforced, which is this PR's own subject one layer down. The hardcoded-values guard caught the literal in my first attempt.

Confinement rather than an admin role, deliberately: these routes are get_current_user only, and no admin-role dependency exists anywhere in autobot-slm-backend/api/. Adding one is a new authorisation tier for that service — an architecture decision, not a security patch.

Verification

  • check_constraint_drift.py passes: aiohttp, chromadb, cryptography, nltk, numpy, openvino, pypdf, transformers.
  • No floor regressed. Each constraint equals the highest pin that existed anywhere on origin/main, compared package by package.
  • Every -c target resolves from the file that declares it — checked programmatically, not by eye.
  • 27 tests green across code_source_test.py and the new confinement suite; 12 of those are new, each parametrised set carrying a positive case so a guard that refused everything could not pass.

Three existing tests broke, correctly, because they use /tmp paths. They now declare their root via the env var rather than the guard being weakened for them. One needed more care: test_validate_local_path_not_exists used /nonexistent/path, which now trips confinement first and returns a different 400 — it would have silently stopped exercising the does-not-exist branch it is named for. Moved inside the allowed root.

What this does NOT do

14 alerts cannot be fixed by bumping. 11 chromadb and 3 nltk advisories have no patched release upstream. chromadb is constrained anyway so the version moves in one place the day a fix ships, but those 14 need a dismissal decision, not a version.

I also did not force the other 14 drifting packages into lockstep. Most of that divergence is legitimate — an NPU worker pins a different torch on purpose — and flattening it would break builds to satisfy a tidiness argument.

Model Used

Claude Opus 5 (1M context)

Refs #17304
Refs #17300

Summary by CodeRabbit

  • Security

    • Restricted code-source repository paths to approved filesystem roots. Attempts to access paths outside those roots are rejected, and refusal messages do not reveal the requested path.
    • Added a setting to configure the permitted roots; the default is /opt/autobot.
  • Dependency Management

    • Centralised shared dependency version constraints across installation and CI requirements, including minimum versions for security and compatibility fixes.

…#17304)

58 open Dependabot alerts were six packages. The count was duplication: one
advisory seen through several resolution paths, and 48 of the 58 attributed to
pyproject.toml, which declares no dependencies at all -- it is [tool.black],
[tool.isort] and [tool.mypy].

What was actually exposed was two manifests, and they were exposed by DRIFT:
aiohttp had been raised to the patched 3.14.3 in five requirements files and
left at 3.13.4 in two others. Nothing compares a pin against the floor its
siblings already adopted, so a security bump applied to "the requirements file"
missed two of the seven that declare it.

The fix is adoption, not invention. constraints/shared.txt has been the single
source of truth since #10524 and the constraint-drift workflow already fails any
PR that re-declares a constrained version -- it simply constrained two packages,
numpy and openvino. These six were never brought in, which is the whole reason
they could drift.

Now guarded: aiohttp, chromadb, cryptography, nltk, numpy, openvino, pypdf,
transformers. Versions removed from 15 component files; three files that
declared a constrained package without referencing the constraints at all
(requirements.txt, docs/guides/requirements-local.txt,
autobot-backend/requirements-media.txt) now reference it, as do the
requirements-ci fragments, which their own header documents as individually
installable.

Verified rather than assumed:
  * every -c target resolves from the file that declares it;
  * no floor regressed -- each package's constraint equals the highest pin that
    existed anywhere before, checked against origin/main;
  * check_constraint_drift.py passes.

chromadb stays at >=1.5.9,<2.0.0 with 11 advisories and NO patched release
upstream. Constrained anyway so the version moves in one place the day a fix
ships. Three nltk advisories are likewise unpatched. Those 14 cannot be resolved
by bumping and need a dismissal decision.

Refs #17304
…453)

The last two of the nine open CodeQL alerts. Folded in here per the owner rather
than opened as a third security PR.

Seven of the nine were already fixed in main by #17301 and cleared on the rescan
that has since run (9 -> 2 open). These two did not, and I should have seen that
when I wrote the first fix: I removed the "Did you mean: <path>?" DISCLOSURE and
left the sinks. repo_path still reached Path(...).is_dir() and .iterdir()
unconfined, so any authenticated user could still test whether an arbitrary path
exists on the host. The oracle was quieter, not closed.

repo_path now goes through validate_path with roots from
AUTOBOT_CODE_SOURCE_ALLOWED_ROOTS. Unset defers to path_validator's own default
rather than restating "/opt/autobot" here -- a second copy is a value that can
drift from the one enforced, which is this PR's subject one layer down. The
hardcoded-values guard caught the literal in my first attempt.

Why confinement rather than an admin role: these routes are gated by
get_current_user only, and NO admin-role dependency exists anywhere in
autobot-slm-backend/api/. Adding one is a new authorisation tier for that
service -- an architecture decision, not a security patch.

Three existing tests used /tmp paths and started failing, correctly. They now
declare their root via the env var rather than the guard being weakened for
them. test_validate_local_path_not_exists moved inside the allowed root on
purpose: "/nonexistent/path" trips confinement first and returns a different
400, so the test would have silently stopped exercising the does-not-exist
branch it is named for.

Helpers live in api/_code_source_paths.py because code_source.py is
grandfathered at 719 lines and may not grow; it is back at exactly 719.

Refs #17300
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

Next included review available in 6 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c22a29c1-a463-4625-887c-42089fd0676b

📥 Commits

Reviewing files that changed from the base of the PR and between e3af55e and 3348fb8.

📒 Files selected for processing (3)
  • autobot-npu-worker/resources/windows-npu-worker/requirements.txt
  • docs/guides/requirements-local.txt
  • repo_tests/hooks_path_override_15961_test.py
📝 Walkthrough

Walkthrough

This pull request centralises package version constraints across requirements files. It also adds allowed-root validation for code-source repository paths and registers the setting used to configure those roots.

Changes

Shared dependency constraints

Layer / File(s) Summary
Define shared version floors
constraints/shared.txt
The shared constraints file defines version floors for six packages and includes advisory comments.
Apply shared constraints in requirements
requirements.txt, requirements-ci.txt, requirements-ci/*, autobot-backend/requirements*.txt, autobot-backend/code_analysis/requirements.txt, autobot-infrastructure/autobot-npu-worker/docker/requirements-npu.txt, autobot-infrastructure/shared/docker/ai-stack/requirements-ai.txt, autobot-npu-worker/resources/windows-npu-worker/requirements.txt, autobot-slm-backend/requirements.txt, autobot-tts-worker/requirements.txt, docs/guides/requirements-local.txt
Requirements files add constraints references or remove inline package versions. Several package versions now rely on the shared constraints file.

Code-source path confinement

Layer / File(s) Summary
Register allowed roots
autobot_shared/env_registry_backend_services.py, docs/developer/ENV_VARS.md
The environment registry and developer reference add AUTOBOT_CODE_SOURCE_ALLOWED_ROOTS, with a default of /opt/autobot.
Confine repository path checks
autobot-slm-backend/api/_code_source_paths.py, autobot-slm-backend/api/code_source.py, autobot-slm-backend/api/code_source_test.py, autobot-slm-backend/tests/api/code_source_path_confinement_17300_test.py
New helpers validate repository paths and similar-path scan parents against allowed roots. Code-source routes use the helpers, and tests cover configured roots and rejected paths.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: 🔵 Low · up to e3af5

Dependency floors now come from one shared constraints file, and the changed install paths still apply them. Local code-source repository paths are now confined to the allowed roots. One comment in the CI document requirements misdescribes where the NLTK version floor comes from and should be corrected. Remote-node path validation still passes the path unquoted to the node's shell. That problem existed before this change and should be fixed promptly, but it does not block this merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies both main changes: consolidating dependency constraints and confining repo_path. It is specific and related to the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 81.25% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 5 files. (18 skipped: 1…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No new hardcoded values of either class — ssot and other both block.

Known backlog in pipeline-scripts/hardcoded_values_baseline.txt is suppressed and tracked in #14371.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟠 Major · Quote and confine paths in remote SSH commands. · code_source.py:256

autobot-slm-backend/api/code_source.py:256
🔒 Security & Privacy | 🛡️ Detected with Advanced Tier | 🟠 Major | ⚡ Quick win

Injection

Reachability: External
Exploitability: Moderate
CWE: CWE-78 — Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

Quote and confine paths in remote SSH commands. Any authenticated user can call /assign; the route has no admin-role check, and CodeSourceAssign.repo_path has no validator. For an SSH-configured remote node, repo_path reaches the remote shell unquoted and commands run as node.ssh_user. create_subprocess_exec avoids a local shell, but does not stop SSH from passing its command to a remote shell. The remote similar-path command also interpolates parent_dir without quoting. The local allowed-roots check does not run for remote nodes.

Quote both values and enforce remote allowed-root containment before test or ls.

Quote the remote command arguments
 import os
+import shlex
 import tarfile
 import tempfile
@@
-        f"test -d {parent_dir} && ls -1 {parent_dir} 2>/dev/null || true",
+        f"test -d {shlex.quote(parent_dir)} && ls -1 {shlex.quote(parent_dir)} 2>/dev/null || true",
@@
-        f"test -d {repo_path} && echo exists",
+        f"test -d {shlex.quote(repo_path)} && echo exists",

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 42f43284-f93d-442f-95b2-251ce1d0e0c5

📥 Commits

Reviewing files that changed from the base of the PR and between 4df6237 and e3af55e.

📒 Files selected for processing (23)
  • autobot-backend/code_analysis/requirements.txt
  • autobot-backend/requirements-media.txt
  • autobot-backend/requirements.txt
  • autobot-infrastructure/autobot-npu-worker/docker/requirements-npu.txt
  • autobot-infrastructure/shared/docker/ai-stack/requirements-ai.txt
  • autobot-npu-worker/resources/windows-npu-worker/requirements.txt
  • autobot-slm-backend/api/_code_source_paths.py
  • autobot-slm-backend/api/code_source.py
  • autobot-slm-backend/api/code_source_test.py
  • autobot-slm-backend/requirements.txt
  • autobot-slm-backend/tests/api/code_source_path_confinement_17300_test.py
  • autobot-tts-worker/requirements.txt
  • autobot_shared/env_registry_backend_services.py
  • constraints/shared.txt
  • docs/developer/ENV_VARS.md
  • docs/guides/requirements-local.txt
  • requirements-ci.txt
  • requirements-ci/ai-ml.txt
  • requirements-ci/document.txt
  • requirements-ci/networking.txt
  • requirements-ci/security.txt
  • requirements-ci/storage.txt
  • requirements.txt

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread requirements-ci/document.txt Outdated
…ach ansible (#17304)

Three guards failed and two of them were right about something I had not
checked. Reverting the consolidation and keeping the security fix.

WHAT THE GUARDS FOUND

windows_npu_worker_transformers_bound_test: I turned `transformers==5.16.1`
into a bare name, which is exactly the regression #15298 fixed. That file is
pinned to an EXACT version on purpose -- an unbounded floor there is "one
dependency bump away from being affected, silently", in its own words. My change
would have let a fresh resolve land on 5.17+ on the one platform that was
deliberately held back.

ansible_requirements_parity_test: five ansible sites restate these versions, and
the guard's advice is to pass `-c <checkout>/constraints/shared.txt` instead,
"as roles/npu-worker and roles/tts-worker do". That advice does not transfer.
Neither dependency_patching nor redis/tasks/chromadb.yml references
code_source_dir or /opt/autobot at ALL: the first patches whatever venv
host_venv_path names on whatever host, the second builds a standalone venv on
the redis host. Adding a `-c` pointing into a checkout that may not exist there
would turn a security-patching role into a failing one -- strictly worse than
the duplication it removes.

THE ACTUAL LIMIT, which is worth recording

constraints/shared.txt works for components installed FROM the checkout. numpy
and openvino are in it because their consumers are exactly that. It structurally
cannot serve a role that builds a standalone venv on an arbitrary host, and all
five packages I moved have such a site. "Defined in a single place" is right as
a goal and is not reachable by this mechanism for these packages.

WHAT SURVIVES

The actual vulnerability: aiohttp raised from >=3.13.4 to >=3.14.3 in
docs/guides/requirements-local.txt and the windows npu worker -- the two of
seven manifests that a previous security bump missed. All seven now agree.

Plus the repo_path confinement (CodeQL 452/453) unchanged.

hooks-path-override floor 6527 -> 6529, thirteenth re-pin, fourth in two days.
@mrveiss

mrveiss commented Sep 23, 2026

Copy link
Copy Markdown
Owner Author

Resolved as stale — the state it describes no longer exists in this PR.

That comment was posted against the constraints consolidation, which I have since reverted (3348fb8ca). requirements-ci/document.txt is now byte-identical to origin/main:

$ git diff origin/main origin/issue-17304-dependabot-sweep -- requirements-ci/document.txt
(no output)

nltk==3.10.3 and the comment above it are untouched, and the comment is still accurate — the pin is what bounds the transitive version, which is exactly what it says.

Why the consolidation was reverted

The repo's own guards proved it cannot work for these packages, and one of them caught a regression I had introduced:

  • windows_npu_worker_transformers_bound_test — I turned transformers==5.16.1 into a bare name. That file is pinned to an exact version deliberately (chore(deps): pin an upper bound on the unbounded windows-npu-worker transformers>=4.36.0 #15298): an unbounded floor there is "one dependency bump away from being affected, silently", in its own words. My change would have let a fresh resolve land on 5.17+ on the one platform held back on purpose.
  • ansible_requirements_parity_test — five ansible sites restate these versions, and its advice is to pass -c <checkout>/constraints/shared.txt "as roles/npu-worker and roles/tts-worker do". That does not transfer: neither dependency_patching nor redis/tasks/chromadb.yml references code_source_dir or /opt/autobot at all. The first patches whatever venv host_venv_path names on whatever host; the second builds a standalone venv on the redis host. A -c into a checkout that may not exist there turns a security-patching role into a failing one — strictly worse than the duplication it removes.

The limit worth recording: constraints/shared.txt serves components installed from the checkout. numpy and openvino are in it because their consumers are exactly that. It structurally cannot serve a role that builds a standalone venv on an arbitrary host, and all six candidate packages have such a site.

What this PR still does

  • aiohttp >=3.13.4 → >=3.14.3 in the two of seven manifests a previous security bump missed. All seven now agree, which is the actual exposure behind the Dependabot count.
  • repo_path confinement closing CodeQL 452/453 — the last two open alerts on main.

@mrveiss
mrveiss merged commit 59f4be8 into main Sep 23, 2026
79 checks passed
@mrveiss
mrveiss deleted the issue-17304-dependabot-sweep branch September 23, 2026 10:33
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.

1 participant