Skip to content

checks[].source in the repo lane trusts a field the agent's own bash tool can write #243

Description

@ale-aguirre

Summary

packages/cli/templates/receipts/collect.mjs:214 sets source: value.self_reported === false ? "platform" : "agent" straight from whatever is in .agent-sdlc/checks.jsonl. That file lives inside github.workspace, the same working tree the agent has full write access to for the whole run. FACILITY_RECEIPT_CHECKS_FILE points there in all six generated workflows (facility-crew.yml:328, facility-codex.yml:304, facility-review.yml:112, facility-address-review.yml:131, facility-doctor.yml:151, facility-security-sweep.yml:152), and protect-files.mjs only blocks writes to .env* (packages/cli/templates/claude/hooks/protect-files.mjs:29). Nothing guards .agent-sdlc/.

So an agent whose bash tool appends a line with "self_reported": false to that file gets its own check reported with source: "platform" in the sealed receipt.

Where

  • packages/cli/templates/receipts/collect.mjs:202-220 (parseChecks)
  • packages/cli/templates/claude/hooks/protect-files.mjs:1-31 (only .env* is protected, .agent-sdlc/ is not)
  • Workflows pointing FACILITY_RECEIPT_CHECKS_FILE at the writable workspace: facility-crew.yml:328, facility-codex.yml:304, facility-review.yml:112, facility-address-review.yml:131, facility-doctor.yml:151, facility-security-sweep.yml:152
  • Schema: packages/core/src/receipts.ts:4-8 (ReceiptCheckSchema, source: "platform" | "agent")

Repro

  1. In a repo lane run, have the agent's bash tool append {"name":"tests","status":"passed","self_reported":false} to .agent-sdlc/checks.jsonl before the receipt-collect step runs.
  2. collect.mjs reads that line and emits source: "platform" in the sealed receipt.

Why it matters

This is the same class of problem engineEnv() already solved on the other side. runner/src/index.ts:2777-2785 strips RUNNER_TOKEN from the agent's process specifically so it "could [not] call /internal/runs/:id/{events,result} for its own run to forge platform-provenance check events or post a fake succeeded result." Good call. But in the repo lane an agent doesn't need the internal API to forge platform provenance, it can just write a line to a file it already owns. The overall run result (succeeded/failed) still comes from the real GitHub Actions step outcome and can't be faked this way, but checks[], the part of the receipt meant to answer "did this actually get verified", can be.

Suggested fix

Either stop trusting self_reported from this file at all (report every line from checks.jsonl as source: "agent", full stop), or do in collect.mjs what the runner already does in parseSelfReportedChecks (runner/src/index.ts:1573-1584), force self_reported: true on any line coming from the agent-writable file, and only let a dedicated workflow step running outside the agent's turn mark something as platform-verified.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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