Skip to content

feat(code-editing): Step 1 — dependency pipeline (install-RCE control) - #60

Merged
WayforthOfficial merged 2 commits into
mainfrom
feat/agent-deps-pipeline-step1
Jun 27, 2026
Merged

WayforthOfficial merged 2 commits into
mainfrom
feat/agent-deps-pipeline-step1

Conversation

@WayforthOfficial

Copy link
Copy Markdown
Owner

Agent code-editing — Step 1 (the security core's install side). Pure-additive/inert (nothing imports it yet). Review at run-token-money-path intensity — this is the install-time-RCE control.

The gatekeeper (validate_requirements / resolve_install_set)

Fail-closed: only exact name==version from the curated allowlist passes. Rejects unpinned, ranges, markers, unknown package, disallowed version, duplicates, -e/-r/git+/url lines, and >20 deps. The base closure (wayforth-sdk + httpx + httpx's pinned deps) is baked into every image — the §3 prerequisite so Step 2 can lock run egress to gateway-only (no run-time pip).

The install command (pip_install_command)

pip install --only-binary=:all: --require-hashes --no-deps --index-url <mirror> …

  • --only-binary=:all: → no setup.py/build hooks execute at install
  • --require-hashes → exact artifacts
  • --no-deps + locked closure → no surprise transitive pulls
  • --index-url <mirror> → resolver can't reach public PyPI

The two-allowlist separation (build_egress / run_egress)

  • build = deny 0.0.0.0/0 + allow [mirror] (not the gateway)
  • run = allow [gateway] (not the mirror)
    Tested that build can't reach gateway and run can't reach the mirror.

Orchestration (build_agent_image)

validate → mirror-only-egress build sandbox → wheels-only hashed install (capped 180s / 500MB / ≤20 deps) → create_snapshot → per-version image_ref. Injectable sandbox factory (tests use a fake); the default uses the e2b snapshot mechanism proven live in §4. Invalid requirements are rejected before any sandbox is spun; the build sandbox is always killed; path traversal in file paths is rejected.

The lockfile (agent_deps_lock.json)

Seed allowlist with real sha256 hashes fetched inside the linux container (so wheels match E2B's platform): the base closure + requests/beautifulsoup4/urllib3/charset-normalizer/python-dateutil/six. Expanded later via the curation queue.

Tests (28)

Gatekeeper accept + every reject path; base-closure + conflict; lockfile hashes; wheels-only/hashed/mirror command; two-allowlist separation; orchestration via fake sandbox (egress=mirror-only, files+lock written, install ran, snapshot named per-version, killed); invalid reqs rejected before any sandbox; install-failure raises+kills; path-traversal rejected; lockfile loads with hashed base deps. Full suite green: 577 passed.

Explicitly NOT in this PR (tracked)

  • Private mirror server — infra; DEPS_MIRROR_URL is config the code consumes.
  • Live §6 ship-gate — malicious-package canary (wheel that POSTs externally → blocked at run egress), hash-mismatch / sdist-only / cap-trip — must-pass-before-user-visible, run live like the rotation proof.
  • E2B Firecracker-token confirmation — launch gate (note in flight to E2B).
  • Step 2 (run-egress lock) builds on the baked base deps here; Step 1 before Step 2 (§3).

No merge — your review.

🤖 Generated with Claude Code

The security core's install side. A user's requirements are validated against a
curated pinned+hashed allowlist, installed wheels-only in an isolated mirror-only-
egress build sandbox, and snapshotted into a per-version image. Pure-additive/inert.

services/agent_deps.py:
  • GATEKEEPER validate_requirements / resolve_install_set — fail-closed: only
    exact name==version from the allowlist passes; rejects unpinned, ranges, markers,
    unknown pkg, disallowed version, duplicates, -e/-r/git+/url lines, >20 deps. Base
    closure (wayforth-sdk + httpx + httpx's pinned deps) baked into every image (the
    §3 prerequisite for Step 2's gateway-only run egress).
  • pip_install_command — the install-RCE control: --only-binary=:all: (no setup.py
    execution) --require-hashes --no-deps --index-url <mirror>.
  • build_egress / run_egress — the TWO-allowlist separation: build = deny 0.0.0.0/0
    + allow [mirror] only (NOT gateway); run = allow [gateway] only (NOT mirror).
  • build_agent_image — validate → mirror-only build sandbox → wheels-only hashed
    install (capped 180s / 500MB) → create_snapshot → per-version image_ref. Injectable
    sandbox factory; default uses the e2b snapshot mechanism proven live in §4.
  • caps: MAX_DIRECT_DEPS=20, BUILD_TIMEOUT_S=180, MAX_IMAGE_DELTA_MB=500.

services/agent_deps_lock.json — seed allowlist with REAL sha256 hashes fetched in the
linux container (so wheels match E2B's platform): base closure + requests/bs4/urllib3/
charset-normalizer/python-dateutil/six. Expanded later via a curation queue.

Tests (28): gatekeeper accept + every reject path; base-closure + conflict; lock-file
hashes; wheels-only/hashed/mirror command; two-allowlist separation; build
orchestration via fake sandbox (egress=mirror-only, files+lock written, install ran,
snapshot named per-version, killed); invalid-reqs rejected BEFORE any sandbox spun;
install-failure raises+kills; path-traversal rejected. Full suite green (577 passed).

NOT in this PR (tracked): the private mirror server (infra; DEPS_MIRROR_URL config);
the live §6 ship-gate tests (malicious-package canary etc.) before user-visible; the
E2B Firecracker-token confirmation (launch gate). Step 2 (run-egress lock) builds on
the baked base deps here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to wayforth / wayforth-pr-60 June 27, 2026 16:18 Destroyed
Addresses the three pre-merge confirmations:

#1 Hash ENFORCEMENT (not just presence): added scripts/deps_live_proof.py — a gated
   (DEPS_LIVE_PROOF=1) §6 ship-gate that feeds a TAMPERED hash to the real pip command
   and asserts the install is rejected. Proven live: pip computed the real wheel hash
   (d909fccc...), compared to the lockfile's tampered 0000..., and failed the install
   ('do not match'). A swapped artifact IS caught.

#2 Parser bypass-resistance (unit tests): -r/recursive mixed with a valid line →
   rejected, resolve_install_set returns [] (no partial build, no sandbox spun);
   whitespace around '==' fail-closed (rejected); PEP-503 case+separator normalization
   consistent (Python_Dateutil / python.dateutil / PYTHON__DATEUTIL all → one key) so
   the allowlist can't be dodged by casing/spacing.

#3 Base-deps bake is REAL (live-proven, in the gated script): pipeline build →
   create_snapshot → boot a run sandbox (gateway-only egress) with httpx 0.28.1 +
   wayforth-sdk 0.9.0 importable and PyPI unreachable (000 ec=35). This is the §3
   prerequisite that unblocks Step 2's run-egress lock — not assumed.

The gated proof also re-checks the two-allowlist separation (build can't reach gateway;
run can't reach mirror). 35 agent_deps tests; full suite green (584 passed).
@railway-app
railway-app Bot temporarily deployed to wayforth / wayforth-pr-60 June 27, 2026 16:27 Destroyed
@railway-app

railway-app Bot commented Jun 27, 2026

Copy link
Copy Markdown

🚅 Deployed to the wayforth-pr-60 environment in wayforth

Service Status Web Updated (UTC)
api 🕗 Deploying (View Logs) Web Jun 27, 2026 at 4:28 pm
labs ⏭️ Skipped (View Logs) Web Jun 27, 2026 at 4:27 pm
mcp-server ⏭️ Skipped (View Logs) Web Jun 27, 2026 at 4:27 pm
crawler ⏭️ Skipped (View Logs) Jun 27, 2026 at 4:27 pm
8 services not affected by this PR
  • Postgres-DLNK
  • Redis
  • vault-api
  • Postgres
  • wayforth-rank
  • mcp-server
  • crawler
  • labs

@WayforthOfficial
WayforthOfficial merged commit f0df09c into main Jun 27, 2026
2 of 3 checks passed
@WayforthOfficial
WayforthOfficial deleted the feat/agent-deps-pipeline-step1 branch June 27, 2026 16:31

This branch was successfully deployed

No deployments
wayforth / wayforth-pr-60 — 8383dd73 Deployed Jun 27, 2026 by railway-app[bot]
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