Skip to content

Widen bypass closures: destructuring, alias imports, PR head clone_url and merge ref#21

Closed
Conalh wants to merge 3 commits into
close-detector-bypassesfrom
widen-bypass-closures
Closed

Widen bypass closures: destructuring, alias imports, PR head clone_url and merge ref#21
Conalh wants to merge 3 commits into
close-detector-bypassesfrom
widen-bypass-closures

Conversation

@Conalh
Copy link
Copy Markdown
Owner

@Conalh Conalh commented May 22, 2026

Summary

Extends #20 with the next wave of evasions surfaced by external inspection. Same shape as #20 — broaden regexes to catch syntactic variants agents use to slip past line-based detectors. Also picks up the agent-gov-core 0.7.0 bundle rebuild that was pre-positioned on this branch (21ab387).

  • JS destructuringjs-capability.ts: const { API_TOKEN } = process.env and the renamed form const { API_TOKEN: t } = process.env now track the secret variable (or alias). Single-line scope.
  • Python aliased env importspy-capability.ts: parses from os import getenv as g / environ as e aliases from each file's content, then generates the secret-variable regex dynamically from the alias union. Closes token = g(\"API_TOKEN\") bypass. Pairs naturally with Close detector bypasses: bracket env, unqualified getenv, custom-shell PR head #20's unaliased-import support.
  • Workflow PR-head extensionsworkflow-permissions.ts: referencesPullRequestHead now also matches github.event.pull_request.head.repo.clone_url and any refs/pull/<n>/merge reference (the merged-PR ref under elevated context is the same risk pattern as a head SHA checkout).
  • test/fixtures/bypasses/ corpus — Bypass fixtures are now a first-class pattern, with one fixture per closure and a CLI integration test per fixture. README inside the dir documents the ritual.
  • README "Detection limits" section — Honest disclosure of the same-line URL requirement, no cross-file taint today, and no Python dep manifests yet. Points readers to the bypass corpus.

Why stacked on #20

This PR's base is close-detector-bypasses because the changes layer on top of #20's regex broadenings (same files, neighboring functions). GitHub will retarget to main automatically when #20 merges.

Bundle update

agent-gov-core 0.5.0 → 0.7.0 was pre-positioned on this branch by 21ab387. My build picked it up and rebuilt dist/action-bundle/index.js accordingly (65kB → 73kB; reflects 0.7.0's added envelope/exceptions/OTel surface).

Deferred (carryover from the inspection)

  • Two-tier secret naming — broadening the TOKEN|SECRET|KEY|... heuristic needs design + real-world fixture corpus before tuning, to avoid regressing on FPs.
  • Whole-file taint — biggest architectural ceiling (a new added-line call site that uses a variable defined in an unchanged file). Needs a focused design PR.
  • Python dep manifests (requirements.txt, pyproject.toml) — belongs partly in agent-gov-core.

Test plan

  • npm test — 80 passing (70 from Close detector bypasses: bracket env, unqualified getenv, custom-shell PR head #20's baseline + 10 new):
    • JS detector: destructuring (3 tests — basic, rename, non-secret-shaped names)
    • Python detector: aliased getenv + aliased environ (2 tests, full file context)
    • Workflow detector: clone_url + refs/pull/N/merge (2 unit tests)
    • Bypass integration: one CLI test per fixture (3 tests)
  • npm run build — clean tsc + ncc bundle (73kB on agent-gov-core 0.7.0)
  • Reviewer: confirm the new referencesPullRequestHead doesn't fire on benign pull_request.head.repo.owner.login references (the regex still only matches sha|ref|repo.full_name|repo.clone_url)
  • Reviewer: skim test/fixtures/bypasses/README.md — comfortable with the convention?

🤖 Generated with Claude Code

Conalh and others added 3 commits May 22, 2026 16:25
…l PR head)

- JS: addSecretVariable and referencesEnvSecret now match bracket
  notation (process.env['KEY'] / ["KEY"]) alongside dot notation. An
  inline `Authorization: Bearer ${process.env['API_TOKEN']}` or a stored
  `const x = process.env["API_TOKEN"]` referenced later now flag the
  exfil pattern.
- Python: addSecretVariable and referencesPyEnvSecret make the `os.`
  prefix optional, so `from os import getenv; k = getenv("API_TOKEN")`
  and `from os import environ; k = environ.get("API_KEY")` are tracked
  as secret variables and trigger exfil on later external requests.
- Workflows: detectPullRequestHeadCheckoutOnTarget no longer requires a
  structured `ref:`/`repository:` key — any reference to
  github.event.pull_request.head.{sha,ref,repo.full_name} under a
  pull_request_target workflow is flagged. Closes a bypass where a
  custom `run:` step with `git checkout ${{ ... head.sha }}` skipped
  detection. Subject/message/recommendation updated from "checkout" to
  "reference" to reflect the broadened semantic; kind preserved for
  back-compat.

Adds 6 tests (70 total, all passing on this branch's baseline).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.7.0 adds report envelope, mergeFindings, OTel
gen_ai.conversation.id interop, matchSecret, applyExceptions.
Lockfile only on this branch — leaving the action-bundle rebuild
for when the widen-bypass-closures feature work lands.
…l and merge ref

Extends #20's bypass closures with the next wave of evasions surfaced
by external inspection.

- JS destructuring (`const { API_TOKEN } = process.env`) and renamed
  destructuring (`const { API_TOKEN: t } = process.env`) now track the
  secret variable. addSecretVariable handles both direct and
  destructured forms.
- Python aliased env imports: `from os import getenv as g` /
  `environ as e` (and the unaliased `from os import getenv` form
  already supported in #20) build a per-file alias map, then the
  secret-variable regex is generated dynamically from the alias union.
  Closes `token = g("API_TOKEN")` bypass.
- Workflow referencesPullRequestHead now covers
  github.event.pull_request.head.repo.clone_url and standalone
  refs/pull/<n>/merge references — the two custom-shell patterns
  agents use when actions/checkout would attract review attention.
- README: new "Detection limits" section documenting same-line URL
  requirement, no cross-file taint, no Python dep manifests yet, with
  a pointer to test/fixtures/bypasses/.
- test/fixtures/bypasses/ established as the bypass-corpus pattern,
  with one fixture per closure and a CLI integration test per fixture.

Adds 10 tests (80 total on this branch).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@Conalh Conalh force-pushed the close-detector-bypasses branch from afa1262 to da04278 Compare May 22, 2026 23:56
@Conalh Conalh deleted the branch close-detector-bypasses May 22, 2026 23:56
@Conalh Conalh closed this May 22, 2026
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