Skip to content

Commit 0850975

Browse files
gitcommit90claude
andauthored
Fix candidate runner hook filename (#68)
* fix: use a valid runner hook filename GitHub Runner requires job-started hooks to use a supported script extension. Rename the constrained Phase 2 hook to .sh and preserve the enforcement test. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Joseph Yaksich <gitcommit90@users.noreply.github.com> * fix: consume candidate inbox files safely Use one unlink invocation per fixed candidate payload and keep a regression assertion for the root boundary. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Joseph Yaksich <gitcommit90@users.noreply.github.com> --------- Signed-off-by: Joseph Yaksich <gitcommit90@users.noreply.github.com> Co-authored-by: Joseph Yaksich <gitcommit90@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 521d73d commit 0850975

5 files changed

Lines changed: 10 additions & 5 deletions

File tree

docs/dress-rehearsal.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ candidate as running after the failed attempt.
4141

4242
The runner service account has no Proxmox, Stable, production, website, or
4343
release credentials and is not a member of privileged container/runtime groups.
44-
Its start hook rejects every repository, workflow, job, and event except the
44+
Its root-owned `runner-job-started.sh` start hook rejects every repository,
45+
workflow, job, and event except the
4546
Phase 2 deployment job resulting from successful `CI` for `main`. Ordinary PR
4647
workflows do not carry the unique runner label. The runner is registered with
4748
`--no-default-labels`, so generic `self-hosted`, OS, or architecture selectors

ops/dress-rehearsal/1helm-candidate-install

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ if [[ "$LOCAL_PROOF" -eq 0 ]]; then
4343
fi
4444
# Consume the fixed inbox payloads after the root copy. The unprivileged runner
4545
# can then create a fresh set without owning retained candidate bytes.
46-
unlink "$INBOX/candidate.json" "$INBOX/candidate.tgz"
46+
unlink "$INBOX/candidate.json"
47+
unlink "$INBOX/candidate.tgz"
4748
[[ "$LOCAL_PROOF" -eq 1 ]] || unlink "$INBOX/provenance.bundle.json"
4849

4950
validate_args=(validate "$work/candidate.json" "$work/candidate.tgz" "$work/verified.json")

ops/dress-rehearsal/runner.service.override.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[Service]
2-
Environment=ACTIONS_RUNNER_HOOK_JOB_STARTED=/usr/local/lib/1helm-candidate/runner-job-started
2+
Environment=ACTIONS_RUNNER_HOOK_JOB_STARTED=/usr/local/lib/1helm-candidate/runner-job-started.sh
33
NoNewPrivileges=false
44
ProtectSystem=strict
55
ProtectHome=read-only

test/phase2-candidate.mjs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ test("rollback fixtures remain local-only and cannot satisfy normal candidate va
9393
test("candidate workflow and guest boundary exclude PR code and broad root access", () => {
9494
const workflow = read(".github/workflows/candidate.yml");
9595
const helper = read("ops/dress-rehearsal/1helm-candidate-install");
96-
const hook = read("ops/dress-rehearsal/runner-job-started");
96+
const hook = read("ops/dress-rehearsal/runner-job-started.sh");
9797
const sudoersExample = "%actions ALL=(root) NOPASSWD: /usr/local/sbin/1helm-candidate-install \"\"\n";
9898
assert.match(workflow, /workflow_run:[\s\S]*workflows: \[CI\][\s\S]*branches: \[main\]/);
9999
assert.match(workflow, /workflow_run\.event == 'push'/);
@@ -111,8 +111,11 @@ test("candidate workflow and guest boundary exclude PR code and broad root acces
111111
assert.doesNotMatch(helper, /--local-proof/);
112112
assert.match(helper, /awk -F\/.*!found.*found=1/, "large archive inspection consumes tar output instead of causing SIGPIPE under pipefail");
113113
assert.match(helper, /actions\\\.runner[\s\S]*systemd-run[\s\S]*\/usr\/local\/sbin\/1helm-candidate-install/);
114-
assert.match(helper, /unlink "\$INBOX\/candidate\.json" "\$INBOX\/candidate\.tgz"/);
114+
assert.match(helper, /^unlink "\$INBOX\/candidate\.json"$/m);
115+
assert.match(helper, /^unlink "\$INBOX\/candidate\.tgz"$/m);
116+
assert.doesNotMatch(helper, /unlink "\$INBOX\/candidate\.json" "\$INBOX\/candidate\.tgz"/);
115117
assert.match(read("ops/dress-rehearsal/runner.service.override.conf"), /ProtectSystem=strict[\s\S]*ReadWritePaths=.*candidate\/inbox/);
118+
assert.match(read("ops/dress-rehearsal/runner.service.override.conf"), /ACTIONS_RUNNER_HOOK_JOB_STARTED=\/usr\/local\/lib\/1helm-candidate\/runner-job-started\.sh/);
116119
assert.match(hook, /GITHUB_EVENT_NAME.*workflow_run/);
117120
assert.match(hook, /run\.get\("event"\) == "push"/);
118121
assert.doesNotMatch(sudoersExample, /NOPASSWD:\s*ALL/);

0 commit comments

Comments
 (0)