security: gate verify.spec child modules at the execution chokepoint (GHSA-wmwj-g59x-c8px) - #82
Merged
Merged
Conversation
verify.spec picks its child modules out of the caller's own ruleset — rules[].source.module / rules[].target.module with free-form params — and dispatched them with instance.execute(). Both locks live in BaseModule.run(), so a caller restricted to verify.spec (FLYTO_MODULE_ALLOWLIST=verify.spec, no FLYTO_GRANTED_PERMISSIONS) could name shell.exec in a rule and run host commands as the service account, outside FLYTO_SANDBOX_DIR. Same omission as the nested Warroom/test steps fixed in testing/runner.py; verify.spec was the sibling call site that was missed. GHSA-wmwj-g59x-c8px. The dispatcher now calls the policy-gated run(), and a ModulePolicyError from a child is re-raised rather than folded into a per-rule error, so a denied module fails the call instead of reading like a failed verification. _execute_with_resilience stops retrying and repackaging ModulePolicyError — blocked must not read as broken. POST /v1/execute gained the nested-module pre-flight the MCP transport already had, reusing the same two helpers so the boundaries cannot drift. A registry-wide AST test fails on any future function that resolves a module by a non-constant id and then awaits <obj>.execute(). Verified against the built artifact, not the source tree: the reporter's PoC reproduces on flyto-core[api]==2.31.0 from PyPI and is refused by the 2.31.1 wheel built here in all three reported configurations. Offline suite 3,175 passed, 11 skipped, 63.67% coverage; build + twine, npm audit, documentation, brand, project-memory and strict Indexer (20/20) all pass.
ChesterHsu
force-pushed
the
claude/verify-spec-policy-gate
branch
from
August 26, 2026 08:40
eaa00ae to
e4d48c3
Compare
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.
Closes the reported
verify.specpolicy bypass.The defect
verify.specpicks its child modules out of the caller's own ruleset —rules[].source.module/rules[].target.module, with free-form params — anddispatched them with
instance.execute(). Both locks (the module filter andthe dangerous-permission grant) live in
BaseModule.run(), so a callerrestricted to
verify.spec(FLYTO_MODULE_ALLOWLIST=verify.spec, noFLYTO_GRANTED_PERMISSIONS) could nameshell.execin a rule and run hostcommands as the service account, outside
FLYTO_SANDBOX_DIR.Same omission as the nested Warroom/test steps fixed in
testing/runner.py(GHSA-675h-j4qg-m52x);
verify.specwas the sibling call site that was missed.The fix
spec_runner.execute_module_dynamic()calls the policy-gatedrun(). AModulePolicyErrorfrom a child is re-raised rather than folded into aper-rule error, so a denied module fails the call instead of reading like an
ordinary failed verification.
BaseModule._execute_with_resilience()no longer retries or repackages aModulePolicyError— blocked must not read as broken.POST /v1/executegained the nested-module pre-flight the MCP transportalready had, reusing the same two helpers so the two boundaries cannot drift.
by a non-constant id and then awaits
<obj>.execute().Verified against the built artifact
The reporter's PoC, three configurations (default denylist, strict allowlist,
targetrule branch):flyto-core[api]==2.31.0(PyPI)exploit_reproduced: true, marker written outside the sandbox2.31.1wheel built from this branchbypass_top_level_ok: false, no marker,Module 'verify.spec' declares nested module(s) blocked by security policy: shell.execThe negative control — a direct
shell.execrequest is denied — holds in everyrun, and the new tests were confirmed to fail on the unpatched dispatcher first.
Repository gates: offline suite 3,175 passed, 11 skipped, 63.67% coverage;
documentation, brand, project-memory, release-drift,
build+twine check,npm audit, and strict Indexer 20/20 all pass. Ruff: no new findings.After merge
v2.31.1still needs to be tagged and published before GHSA-wmwj-g59x-c8px isgiven
2.31.1as its patched version and published.