Repository navigation
fix(dynamic-test): the recorded build-egress ruling — the falsifier's second disjunct, the accepted state pinned so it cannot drift (#710) - #875
Open
gadievron wants to merge 1 commit into
Conversation
… second disjunct, the accepted state pinned so it cannot drift (#710) #247 closed RUNTIME egress (--network none / internal compose nets) and named the build half as deferred: 'Build-time RUN egress stays OPEN... A build-egress allowlisting proxy is deferred.' The falsifier (authored at filing): 'a dynamic-test build whose RUN steps resolve only allowlisted hosts (or a recorded ruling that build egress is accepted by design).' The host-proxy alternative (the first disjunct) was IMPLEMENTED and then KILLED BY EXECUTED EVIDENCE (the adversarial review round's probes): (1) a host-side proxy pointed at via Docker's predefined proxy build-args is UNREACHABLE from the RUN steps — 127.0.0.1 inside a build container is the CONTAINER's own loopback, and the only routing that reaches the host (--network host) reopens the runtime posture this module exists to enforce — measured: every dependency build fails with ConnectionRefused; (2) even with routing, the proxy-env control is ADVISORY, not structural — the Dockerfile is LLM-authored from untrusted findings, and a single 'ENV http_proxy=' line or a raw socket egresses freely (both executed); (3) the real structural control (a build network-namespace with a proxy sidecar, or --network none with vendored deps) either breaks every dependency build or is a disproportionate re-architecture for an opt-in, defense-in-depth surface. This lands the SECOND disjunct — the recorded ruling, with the executed evidence named where the residual comment lived, plus the DRIFT PINS: the accepted state (the build invocations run with default networking, exactly as #247 left them; the runtime argv closed; the env allowlist in force) is pinned test-side so it cannot drift silently — any flag added to a build, any runtime cap removed, any allowlist entry loosened, is a deliberate diff that fires the pins.
gadievron
requested review from
dgeyshis,
shahar-davidson and
sounil
as code owners
October 27, 2026 00:01
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Closes #710 — #247 closed runtime egress for the dynamic tester and named the build half as an accepted, deferred residual ("Build-time RUN egress stays OPEN… A build-egress allowlisting proxy is deferred"), tracked nowhere but a code comment.
The falsifier (authored at filing): "a dynamic-test build whose RUN steps resolve only allowlisted hosts (or a recorded ruling that build egress is accepted by design)."
The decision this issue asked for — and how it was made
One implementation of the first disjunct (a loopback-bound host-side proxy routed via the build's proxy env) was built and then killed by executed evidence (the adversarial review round, with Docker available, running the production argv shape — the probe records live in the T1 review chain, runs/710/_t1_r1_review.txt):
http://127.0.0.1:<port>there is the build container's own loopback, not the host's: every dependency build fails withConnectionRefused— the generator's own templates (pip/npm/go) all break. The routing that would reach a host-side proxy (--network hoston the build) puts the RUN steps in the host's netns — the build-time posture this module exists to prevent — and binding the proxy on a non-loopback interface to make it bridge-reachable exposes it to containers on the host's bridge networks (a bind/topology exposure — not firewall-measured here).ENV http_proxy=line (measured) or a raw socket (measured) egresses freely. A cooperating-client control cannot close a channel the content author controls.--network nonewith vendored deps — either breaks every dependency build or is a disproportionate re-architecture for an opt-in, defense-in-depth surface.Structural proxy architectures (a build netns with a proxy sidecar, or
--network nonewith vendored deps) were declined by design judgment — honestly labelled as such in the record — not disproved by measurement. So this lands the second disjunct — the recorded ruling, with the evidence named where the residual comment lived, plus drift pins holding the enumerated protections below.The recorded ruling
docker_executor.py(the single-build site) now carries the decision, its evidence honestly labelled (two executed findings + one design judgment), and the accepted posture: the RUNTIME sandbox is the hard boundary (--network none), the env allowlist limits the inherited environment variables available to build-time interpolation (a name-agnostic scrub — the pin holds the provider-secret-dropped / docker-essentials-kept contract), and the build context is minimal.The drift pins (
tests/test_issue710_build_egress_ruling.py, 7 tests)The pins hold the enumerated protections — the argv exact-equality, the
_safe_builddrop, the flag presence, and the--tmpfscount — not a full drift-proof — the enumerated protections only (the resource-cap values are held by the #732 suite; the env-allowlist membership by the #247 hardening suite):["docker","build","-t",tag,"."]) — any added flag (e.g.--network host) is a deliberate diff the pin names.--network none+ every cap,--tmpfscounted ×2)._safe_builddropsbuild.network/extra_hosts/entitlements— the review round's catch: no pin held this drop; a one-line drift would silently reopen the--network hostposture. Verified load-bearing: the pin fires on a simulated passthrough drift).--build-arg/HTTP_PROXYin the executor; the proxy module is gone).The executable AST is unchanged from base (checked with docstrings stripped — a normalization-identity, not a byte comparison) — the deliverable is the record + the pins, never a behavior change to the #247 machinery.
Evidence (runs/710)
RED gate at
9143061f..a053fa18: 8 nodes / 1 witness, assertion-red at base (the ruling's presence pin); the docker/egress regression suites green (the wider scope 588 passed); ruff clean. # proc-okC4: the one prod hunk (the docstring's residual-rewrite) covered by the green prose revert + its claim (the A multi-language scan's pipeline_stats.reachable_units reports one language's kept count against every language's total — the merged dataset keeps only the first parser's reachability record #707 grammar) — the executable-code delta is empty by design.
fable adversarial review: four rounds — r1 the hunt (review-reported execution with Docker: the unreachability measured on every dependency build; the advisory channel measured two ways — the probe outputs recorded in the T1 chain; the portless-URL bug; the no-test-executes-a-build finding); r2 the pivot review (complete; four findings — the record's executed/judgement labelling, the finding-(1) accuracy, the missing
_safe_buildpin, the--tmpfsgap); r3 TERMINAL DRY (both new pins verified load-bearing by simulated drifts); r4 TERMINAL DRY (the wording nits).T8 standing-promises table (4 rows: 3 HIT + 1 disclosed MISS — the executed evidence lives in the review record, not the test suite: no test executes a docker build, so the unreachability/advisory measurements cannot be pinned test-side), C2 anchor queries (consumed==total: 6/6, 3/3, 8/8, 3/3), A4 census (master unmoved at
9143061f; the open PRs fix(rust): the macro-body call scan runs on a tree-sitter token-tree walk — both regexes and the byte budget retired to the differential oracle (#708) #871/chore(deps): update dependency marked to v18.1.2 #872/chore(deps): update dependency openai to v3.40.0 #873/fix(prompts): the Untrusted choke-point — prompt-untrusted content refuses raw interpolation, unwrapped only at the fence single home (#709) #874 disjoint), reconciliation (4 rows).Parity
The legs' own terminal lines, pasted verbatim (the full logs in the bundle —
base-parity-full.log,head-parity-settled.log[the WARM run],head-parity-settled-cold.log,head-parity-go.log,collect-diff.txt):base (at
9143061f, the full core suite):head, warm (at
a053fa18, the same suite): the standing trio identity-matched; the +8 passing over base = the 7-test ruling suite + the 1 parametrizedno-hardcoded-pathsguard item; the skips identical (38 = 38):the go module (at the head):
head-parity-go.logendsEXIT:0— all packages ok.collect-diff: base 13,324 collected → head 13,332 —
+8 added / 0 removed, exactly the ruling suite's 7 tests + the parametrized guard item (collect-diff.txt).the cold-run disclosure: the head worktree's FIRST full run showed
3 failed, 13146 passed, 180 skipped— the +142 skips over base are the first-run provisioning class, fully decoded by the warm run above (38 = 38); the cold log retained ashead-parity-settled-cold.log.