feat(doctor): runner-health check (ADR-0005 AC4) — Closes #225 - #231
Merged
Conversation
forge:doctor now reports local self-hosted-runner health, gated on config runner.enabled — fully silent when the runner feature is off. When enabled: - private-only guard: FAIL on a public repo (fork-PR RCE), belt-and- suspenders with init's refusal (#224) - registered + online probe via `gh api {repos|orgs}/.../actions/runners` (org endpoint when sharing:"org"), matching the configured labels case-insensitively; degrades to a warn (never a crash) on 403/404/scope - secret-store assertion (decision 1): ~/.forge/runner.env gitignored + untracked (git ls-files / check-ignore) and no PAT-looking secret in a committed file (git grep); git-only, so it runs even when the gh probe can't (transient failure / public repo) All gh/git calls are argv-only (no shell); only file names are ever surfaced — a discovered secret's value is never echoed into output. 12 vitest cases map AC4: enabled+online->ok, offline/missing->warn, feature off->absent, secret tracked/committed->fail, public+enabled->fail, sharing:org endpoint, gh-scope degrade, case-insensitive labels. Closes #225 Refs #180 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SATRHKa6mDHDuirhP6QuwL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
forge:doctornow reports local self-hosted-runner health (ADR-0005 decision 1, AC4). The check is gated onrunner.enabledin the loaded config — when the runner feature is off (block absent orenabled:false), doctor emits nothing for it (no noise for the majority who don't run a local runner).Scope is only AC4 — no changes to the #224 scaffold or the #226
forge.jsonrunnerschema.AC4 → behaviour → test
tests/doctor.test.mjs)runner/runner-secretresultsfeature off (no runner block) → absent;runner.enabled:false → still silentrunner= ok, probed viagh api repos/{o}/{r}/actions/runnersmatching configured labelsenabled + a matching runner online → okrunner= warnenabled + matching runner OFFLINE → warnrunner= warnenabled + NO matching runner registered → warnsharing:"org"→ org endpointorgs/{owner}/actions/runnerssharing:org → queries the ORG runners endpointrunner= warn, no crashgh api lacks scope / 403 → degrades to warnrunner-secret= ok / warn (git ls-files+check-ignore)store present but runner.env NOT gitignored → warnrunner-secret= failsecret store TRACKED in git → FAILrunner-secret= fail (git grep, filenames only)PAT-looking secret in a committed file → FAILrunner= FAIL with fork-PR RCE messageenabled on a PUBLIC repo → FAILPlus hardening from review: secret-store scan is gh-independent and always runs even when the
gh repo viewprobe fails (secret-store scan is git-only and still runs when gh repo view fails), and case-insensitive label matching (FORGE-LOCALvsforge-local). 12 new cases; 21 in the file; 418 repo-wide.Security / injection
gh/gitcall is argv-only (shell:falsevialib/exec.mjs) — no shell-string interpolation;owner/name/endpoint are single argv tokens.git grep -llists filenames only; all messages are filenames/counts/fixed strings.RUNNER_PAT_REis assembled from string parts so it never self-matches doctor's own source.forge:reviewer+forge:securitypasses run over the diff. Security: pass. Reviewer majors addressed (secret-scan now unconditional; case-insensitive labels;per_page=100).Deferred (called out honestly)
ADR-0005 decision 1 also lists a chmod-600 assertion on
~/.forge/runner.env. It is not included here: the store is a POSIX home-dir file outside the repo, a no-op on the owner's native-Windows box, and typically absent at doctor time (it lives on the runner host). The AC4 contract for this ticket scopes the secret-store assertion to gitignored + untracked + PAT-scan. Suggest a small follow-up ticket for a POSIX-only, best-effort perms check if wanted.Verification
pnpm verify— 418 passed (local gate). ✅claude plugin validate ./plugin --strict— passed. ✅git(real repos/commits) on Windows in the test suite.Closes #225
Refs #180
🤖 Generated with Claude Code