Skip to content

docs(operations): record runner audit credential and assignment contract - #401

Closed
cursor[bot] wants to merge 23 commits into
mainfrom
cursor/bc-4a09cba8-1cd3-4c2d-9b3a-a9b274ba2469-18b3
Closed

docs(operations): record runner audit credential and assignment contract#401
cursor[bot] wants to merge 23 commits into
mainfrom
cursor/bc-4a09cba8-1cd3-4c2d-9b3a-a9b274ba2469-18b3

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Purpose

Land the #394 runner-assignment hardening together with the missing Unreleased operator contract. #394 at f4a4bc9 already implements capability-file ingress, exact token redaction, and started_at losing assignment authority, but CHANGELOG.md still described only the original audit addition. Operators reading Unreleased could not see the three production behavior changes or the next action (prepare NOEMA_MAINTAINER_TOKEN_PATH, do not treat PASS as merge authority).

What to do with this PR

Prefer this branch over merging #394 at f4a4bc9. This head is #394 plus one documentation/contract commit. Do not merge #394 once this PR exists unless that head is fast-forwarded to include the Unreleased bullet.

Test-first evidence

test/actions-runner-assignment-docs.test.ts now requires Unreleased to name NOEMA_MAINTAINER_TOKEN_PATH, [REDACTED], queued started_at, runner_id, and runner_name, while keeping the existing operations:runner-assignment / required Check / formal review phrases. Local Node 24.19.0 / npm 11.17.0: eight runner-assignment and changelog contract files, 44 tests, all passed.

Authority boundary

This does not close issue #30. It does not change GitHub permissions, reviewer/maintainer identities, workflow authority, release/deployment authority, or any central repository. Queued exact-head checks on this successor are not GREEN and are not merge authority.

Related: #394, #30.

Open in Web View Automation 

seonghobae and others added 23 commits August 16, 2026 18:11
Name capability-file ingress, exact token redaction, and started_at
losing assignment authority in Unreleased so operators can apply the
hardened production command without treating PASS as merge authority.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@seonghobae
seonghobae marked this pull request as ready for review August 16, 2026 16:10
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of #401 (64c6213)

This head is the right landing vehicle for the runner-assignment hardening that started in #394. Prefer this branch over merging #394 at f4a4bc9. Do not treat this COMMENT as approval, required-check success, or merge authority.

CodeRabbit CLI 0.7.3 is installed here, but coderabbit auth login --agent timed out on the browser callback (automatic_login_failed). This assessment is from the a634066...64c6213 range, not a CodeRabbit CLI result.

What this PR gets right

  • Production operations:runner-assignment reads GitHub transport authority only from the owner-only file named by NOEMA_MAINTAINER_TOKEN_PATH. Ambient GH_TOKEN alone fails closed.
  • gh spawn/stderr diagnostics exact-match redact the active token as [REDACTED].
  • Assignment authority is a positive runner_id or a non-empty runner_name. Queued started_at with runner_id=0 stays unassigned.
  • Unreleased now names the three production behavior changes and the next operator action: prepare the capability file; do not treat PASS as required Check, formal review, or merge authority.
  • Tests inject gh_api without a filesystem credential, and the production path uses a real capability file against an ambient decoy token.

Residual risk (not merge-blocking)

  1. redactExactSecret will explode a diagnostic if it is ever called with an empty secret. Unreachable today because createGhSubprocessEnvironment rejects empty GH_TOKEN first. Guard the empty-secret case before reuse.
  2. Test gh shims interpolate the expected token into a shell if test. Current literals are safe; a later $ / backtick / quote token would break the shim. Compare against a side file instead.
  3. test/actions-runner-assignment-cli.test.ts is missing a trailing newline.
  4. First-run operator trap: echo TOKEN > file writes a trailing newline, and the capability reader then fails closed on control characters. Doctoring already uses printf '%s', but it should say that echo / printf '%s ' will fail and that the token needs Actions workflow-run and workflow-job read.

Next action

Keep #401 as the review surface for this slice. Wait for exact-head verify and Security Scan (trivy-fs is a real finding, not a flake) to terminate on this SHA. Do not merge #394 once this PR exists unless that head is fast-forwarded to include the Unreleased bullet. This does not close issue #30.

Queued checks and this COMMENT are not GREEN and are not merge authority.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues


function redactExactSecret(value, secret) {
const text = typeof value === "string" ? value : String(value ?? "");
return text.split(secret).join("[REDACTED]");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

text.split(secret).join("[REDACTED]") is correct for a non-empty active token, and createGhSubprocessEnvironment currently rejects empty GH_TOKEN before this runs.

If this helper is reused with an empty secret, split("") inserts [REDACTED] between every character. Guard with if (typeof secret !== "string" || secret.length === 0) return text so a later caller cannot take that landmine.

const executable = join(directory, "gh");
const tokenGuard = expectedToken === undefined
? ""
: `if [ "$GH_TOKEN" != "${expectedToken}" ]; then\n printf '%s' 'unexpected delegated GH_TOKEN' >&2\n exit 91\nfi\n`;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shim interpolates expectedToken into a POSIX if test. The current literals (read-only-capability-token) are shell-safe, so this is not a production leak.

A later call with $, backticks, or " would break the comparison or write a real token into a script. Compare $GH_TOKEN to a side file, or keep the guard in JavaScript.

await expect(startCli({ execute: success, write_error: writeError, set_exit_code: setExitCode })).resolves.toEqual({ exit_code: 0 });
});
});
}); No newline at end of file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file lost its trailing newline in the range diff (\\ No newline at end of file). Restore the POSIX text-file terminator so later patches do not keep carrying a noisy EOF hunk.

Copy link
Copy Markdown
Contributor

Superseded by #402. Exact ancestry is preserved: #401 head 64c621323591205f0f2ef6c7ebf228a951f3e96e#402 head fddec97e11084d9e910f299036f9d58cac378cfd is ahead 1 / behind 0. #402 carries all #401 behavior plus the first-run credential-file hygiene, empty-secret redaction guard, and operator permission/write-path contract. Closing this predecessor avoids competing ownership; no unique #401 delta is discarded.

@seonghobae seonghobae closed this Aug 16, 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.

2 participants