The container path resolves a GitHub App installation for whatever repo a run names, and mints an installation token for it. Nothing constrains that repo to the one the dispatch was authorized for.
The substrate path already refuses this. packages/runtime-cf/src/sandbox-facade.ts pins the clone to the recipe:
if (repo !== opts.repo || sha !== opts.sha)
throw new Error(`substrate clones from the recipe: this execution is pinned to ${opts.repo}@${opts.sha}, not ${repo}@${sha}`);
packages/runtime-cf/src/sandbox-cf.ts has no equivalent. A dispatch that is legitimately authorized for repo A can pass inputs.repo = "owner/private-B" to offload-test and get B cloned, with a token minted for B's installation.
This is not new logic, but #90 is what makes it work: before it, a cross-account clone went out unauthenticated and 404'd, so the App's other installations were unreachable in practice. After it, any repo the App is installed on is reachable from any dispatch.
Why it was not fixed in #90. Pinning the clone target the way the facade does would defeat the change: an estate sweep (spec-drift-pr, ci-triage-pr, finops-audit) clones repos the dispatch never named, and resolving per clone target is the entire point. The right boundary is a policy decision, not a one-line guard.
Options, roughly in increasing order of cost:
- Accept it, and document the posture: a valid HMAC dispatch is trusted with read access to every repo the App is installed on. (Note that a dispatch can already run arbitrary commands in the container, so the marginal escalation is repo reach, not code execution.)
- Allowlist clone targets per run definition — a run declares which repos it may reach, checked against the resolved slug before the lookup.
- Constrain by installation — a clone target must share the dispatch's installation unless the run is explicitly marked as an estate sweep.
Raised by review on #90; wants an explicit decision before it is treated as settled.
The container path resolves a GitHub App installation for whatever
repoa run names, and mints an installation token for it. Nothing constrains that repo to the one the dispatch was authorized for.The substrate path already refuses this.
packages/runtime-cf/src/sandbox-facade.tspins the clone to the recipe:packages/runtime-cf/src/sandbox-cf.tshas no equivalent. A dispatch that is legitimately authorized for repo A can passinputs.repo = "owner/private-B"tooffload-testand get B cloned, with a token minted for B's installation.This is not new logic, but #90 is what makes it work: before it, a cross-account clone went out unauthenticated and 404'd, so the App's other installations were unreachable in practice. After it, any repo the App is installed on is reachable from any dispatch.
Why it was not fixed in #90. Pinning the clone target the way the facade does would defeat the change: an estate sweep (
spec-drift-pr,ci-triage-pr,finops-audit) clones repos the dispatch never named, and resolving per clone target is the entire point. The right boundary is a policy decision, not a one-line guard.Options, roughly in increasing order of cost:
Raised by review on #90; wants an explicit decision before it is treated as settled.