Skip to content

runtime-cf: a run's clone target is unconstrained — the facade pins it, the container path does not #107

Description

@debuggingfuture

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:

  1. 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.)
  2. Allowlist clone targets per run definition — a run declares which repos it may reach, checked against the resolved slug before the lookup.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions