Audit the rest of the codebase; fix a live traversal hole - #9
Merged
Conversation
Covers what the M0-M3 and control-layer audits did not: the queueing core, the adaptive controllers, the Pareto machinery, the price client and the deployed container. Four defects. The budget pacer was steering on a frozen capacity trace. adaptive.py kept its own copy of the actuation-delay rule so C5/C6 could pace against their own past requests, and fixing the clock-restart bug in apply_actuation_delay left that copy behind. On a plan that moves every step — which is what C5/C6 emit — the mirror reported a flat 13 replicas across 40 steps while the simulator provisioned 13 to 24, so the PI loop corrected against violations that never happened. Extracted ActuationTracker and implemented the batch form on top of it so the two cannot diverge; a test agrees them over 200 random plans and another fails if the rule is re-implemented anywhere outside simulator.py. Re-running the frontier moves only C5/C6 rows by small amounts in both directions. Q4 is unchanged at 9/3/6 with the below-19:1 pattern intact, and the GPU frontier verdict is unchanged at 0 of 8. The SPA catch-all could be walked out of the dist directory. Percent-encoded traversal survives URL normalisation and reached the handler intact, serving arbitrary container files — verified against the real deploy image under uvicorn, which also ran as root. No credentials exist to steal, the project being keyless, but the source and every system file were readable. Render's edge rejected the encoded forms with a 400, so the live site was not exploitable; that is an accident of the CDN, not a control, and the README's docker run had no such cover. Candidates are now resolved and confined to dist, which also closes symlink escapes and absolute-path injection, and the image drops to an unprivileged user. The deploy-image CI lane asserts both against the running container. dominated_hypervolume swept from cost zero and credited each strip with the dearer point's violation rate, when only the cheaper point is affordable over that interval. One point at cost 5 and 0.2 violations against a reference of 10 returned 8.0 where the rectangle is 4.0, and the error could reverse a ranking rather than merely inflate it. The old test compared two curves where it cancelled. Nothing published used the function. _synthesize_arrivals claimed the utilisation and queue fidelities described the same physical system. They do not — utilisation_target derates one and not the other — which is harmless because every published result uses the utilisation model and the Erlang-C validation sets the target to 1.0, but the docstring asserted something false. Verified sound: the ACI update matches the published BACC form including its sign, pareto_front is a correct two-objective sweep, cost_at_matched_violation reports unreachable targets rather than substituting the nearest point, and the price parser skips non-positive prices rather than defaulting them. The OData filter is now escaped; not a live injection path, but an apostrophe would have returned the wrong SKU's price.
Caught by CI, not locally, because I read ruff's trailing summary line as success instead of checking its exit code. The gate is a set of exit codes and should be verified as one.
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.
Covers what the M0-M3 and control-layer audits did not: the queueing core, the adaptive
controllers, the Pareto machinery, the price client, and the deployed container. Four
defects, one a security hole in the live image.
1. Path traversal in the deployed dashboard (security)
The SPA catch-all joined the request path onto
distand served whatever it found.Percent-encoded traversal survives URL normalisation and reaches the handler intact:
Verified against the real deploy image under uvicorn, which also ran as root — so this
was an arbitrary read of the whole container. No credentials exist to steal (the project is
keyless by design), but source, manifests and system files were readable.
Render's edge rejected the encoded forms with a 400, so the live site was not exploitable.
That is an accident of the CDN, not a control, and the README's
docker runhad no cover.Fixed: candidates are resolved and confined to
dist(closing symlink escapes andabsolute-path injection too), and the image drops to uid 10001. The deploy-image CI lane now
asserts both against the running container.
2. The budget pacer was steering on a frozen capacity trace
adaptive.pykept its own copy of the actuation-delay rule. Fixing the clock-restart bugin
apply_actuation_delayleft that copy behind. On a plan that moves every step — whatC5/C6 emit — the mirror reported a flat 13 replicas across 40 steps while the simulator
provisioned 13 → 24. 36 of 40 steps disagreed, so the PI loop corrected against
violations that never happened.
Fixed by extracting
ActuationTracker, with the batch form built on it so they cannotdiverge. Tests agree them over 200 random plans, and one fails if the rule is re-implemented
outside
simulator.py— this being the second drift of that same logic.No conclusion moves. Only C5/C6 rows shift, in both directions and by small amounts. Q4
is unchanged at 9 better / 3 tie / 6 worse of 18 with the below-19:1 pattern intact; the GPU
frontier verdict is unchanged at 0 of 8.
3. Hypervolume measured the wrong staircase
It swept from cost zero and credited each strip with the dearer point's violation rate,
when only the cheaper point is affordable there. One point at cost 5 with 0.2 violations
against a reference of 10 returned 8.0 where the rectangle is 4.0 — and the error can
reverse a ranking, not just inflate both sides. The old test compared two curves where it
cancelled. Nothing published used the function.
4. The two fidelities were documented as the same system
utilisation_targetderates the utilisation model but not the queue, so at 0.8 the queueruns at rho = 0.8 where the utilisation model reports a step at its limit. Harmless — every
published result uses the utilisation model, and Erlang-C validation sets the target to 1.0 —
but the docstring asserted something false about the code.
Verified sound
ACI matches the published BACC update including its sign under miscoverage;
pareto_frontisa correct two-objective sweep;
cost_at_matched_violationreports unreachable targets ratherthan substituting the nearest point; the price parser skips non-positive prices rather than
defaulting them. OData filters are now escaped — not a live injection path, but an apostrophe
would have returned the wrong SKU's price.
Gate
ruff clean; mypy strict clean; 201 tests;
make validate-simulatorGREEN.