Skip to content

fix(scripts): pin self-update + SPM dependency to immutable revision (DEVA11Y-475,478,477)#30

Open
Crash0v3rrid3 wants to merge 5 commits into
mainfrom
fix/DEVA11Y-475-script-selfupdate-pinning
Open

fix(scripts): pin self-update + SPM dependency to immutable revision (DEVA11Y-475,478,477)#30
Crash0v3rrid3 wants to merge 5 commits into
mainfrom
fix/DEVA11Y-475-script-selfupdate-pinning

Conversation

@Crash0v3rrid3

@Crash0v3rrid3 Crash0v3rrid3 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Integrity / robustness hardening for the six shell installers under scripts/{bash,zsh,fish}/{cli,spm}.sh, plus an SPM dependency pin. Umbrella: APPSEC-415 (DEVA11Y-475 / 477 / 478).

Scope note (updated after review): self-update intentionally continues to track the latest main and to run automatically on every invocation — this is the maintainer's chosen behaviour (always run the newest launcher). This PR therefore hardens the self-update mechanism in place; it does not make the source immutable or gate it behind an opt-in subcommand. The security improvement is in how the update is downloaded, verified, and applied — not in pinning the source.

What changed

Self-update mechanism (script_self_update, all six scripts) — DEVA11Y-475 / 478

Previously every invocation curled the script from refs/heads/main, accepted it if it merely started with #!, and overwrote the currently-running file in place — no integrity check, non-atomic.

Now (still auto-running, still from main, best-effort via script_self_update || true so failures never block the tool):

  • SHA-256 integrity check — downloads a committed <script>.sha256 sidecar and verifies the digest before applying; refuses on mismatch/missing checksum (fails closed).
  • Skip-when-current — fetches the sidecar first and no-ops when the on-disk copy already matches, avoiding a redundant download/replace each run.
  • Atomic, no in-place overwrite — stages into the target directory and mvs into place (same-filesystem atomic), so the running script is never half-written.
  • Portable hashing — prefers sha256sum, falls back to shasum -a 256; guards empty output.
  • Network timeoutscurl --connect-timeout 10 --max-time 30 on both fetches.
  • Shebang sanity check runs after integrity verification.

The six <script>.sha256 sidecars are committed in this PR.

SPM dependency pin — DEVA11Y-477 / F-005

The generated Package.swift heredoc in the three spm.sh files pinned the dependency to branch: "main" (mutable). Now pinned to .revision("db817c37cf74cba47e2fef535f53a35bfc88ec6a") — the current origin/main SHA.

CI — keep sidecars in sync (review follow-up)

.github/workflows/verify-selfupdate-checksums.yml runs on every PR/main push touching scripts/ and fails when a script is missing its .sha256 sidecar or when a sidecar is out of date — so a future script edit can't silently break self-update.

Known limitations (accepted)

  • Same-origin checksum is integrity, not authenticity. The script and its .sha256 are served from the same raw.githubusercontent.com/.../main origin, so the checksum guards against truncated/corrupted downloads, not a main compromise (an attacker with push access updates both together). True supply-chain protection (detached signature / cosign / GPG) is out of scope here and tracked separately.
  • SPM .revision() pin will go stale. Bump SELF_UPDATE source and the SPM revision to a release tag (.exact("x.y.z")) once tagging is introduced.

Validation

  • bash -n passes on all six scripts; the self-update logic was traced for the apply / skip-when-current / checksum-mismatch / offline paths.
  • All six .sha256 sidecars verified against their scripts (shasum -a 256 -c); the new workflow enforces this going forward.
  • Live --self-update against main can only be exercised once this merges (inherent to self-update-from-main).

🤖 Generated with Claude Code

…(DEVA11Y-475,478,477)

Supply-chain / integrity hardening for the shell installers (APPSEC-415).

DEVA11Y-475 / F-003 + DEVA11Y-478 / F-006 — self-update from mutable branch HEAD:
- Self-update is now OPT-IN via an explicit `--self-update` subcommand; it no
  longer runs automatically on every invocation.
- Fetches from a pinned, immutable revision instead of refs/heads/main.
- Verifies a SHA-256 checksum (published `.sha256` sidecar) before use and
  refuses to apply on mismatch or missing checksum.
- Downloads to a temp file and atomically replaces the script via `mv` instead
  of overwriting the currently-running file in place.
- Applied consistently to all six variants (bash/zsh/fish × cli.sh/spm.sh).

DEVA11Y-477 / F-005 — SPM dependency pinned to branch "main":
- Generated Package.swift heredoc now pins to
  .revision("db817c37cf74cba47e2fef535f53a35bfc88ec6a") (current origin/main
  SHA; no release tags exist yet) instead of branch: "main".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Crash0v3rrid3 Crash0v3rrid3 requested a review from a team as a code owner June 17, 2026 08:23

@Crash0v3rrid3 Crash0v3rrid3 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude Code Review (automated) — 1 inline finding(s). Full report in the PR comment below. Verdict: Failed - see PR comment.

Comment thread scripts/bash/cli.sh Outdated
# Pinned, immutable git revision the self-update is allowed to fetch from.
# DEVA11Y-475: never fetch executable code from a mutable branch HEAD.
# Bump this (and the published .sha256 sidecars) on every release.
SELF_UPDATE_REF="db817c37cf74cba47e2fef535f53a35bfc88ec6a"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[High] Self-update is non-functional as committed

Two problems converge here:

  1. script_self_update fetches ${base_url}.sha256, but no .sha256 sidecar files exist anywhere in the repo — so --self-update always aborts at the checksum-download step.
  2. This pinned SELF_UPDATE_REF (db817c37…) is the parent commit of this PR's head, which does not contain the hardened script — so the pin references the old, pre-hardening code rather than the commit that introduces these changes.

It fails safe (aborts rather than rolling back), but the feature can't actually run.

Suggestion: Before merge, (1) generate and commit the six *.sha256 sidecars next to each script, and (2) bump SELF_UPDATE_REF in all 6 scripts to the commit that actually contains the hardened scripts + sidecars (e.g. the merge commit), not the parent.

Reviewer: stack:code-reviewer

@Crash0v3rrid3

Copy link
Copy Markdown
Collaborator Author

Claude Code PR Review

PR: #30Head: fc2a1efReviewers: stack:code-reviewer (+ orchestrator verification)

Summary

Hardens script_self_update across all 6 launcher scripts (scripts/{bash,fish,zsh}/{cli,spm}.sh) and pins the SPM Package.swift dependency to an immutable revision. Self-update changes from unconditional, fetch-from-mutable-main-HEAD, overwrite-in-place to opt-in (--self-update), fetch-from-pinned-revision, SHA-256-verified, atomic mv replace. Direction is a clear security improvement. All 6 files share #!/usr/bin/env bash -il; the fish//zsh/ directories only configure those shells' PATH, so the bash syntax is correct (no shell-mismatch).

Review Table

Priority Category Check Status Notes
High Security No hardcoded secrets or credentials Pass Only a public git SHA is embedded.
High Security Authentication/authorization checks present N/A No auth surface in these scripts.
High Security Input validation and sanitization Pass Shebang sanity-check + checksum gate added; mv target unvalidated (see F-3).
High Security No IDOR — resource ownership validated N/A Not applicable.
High Security No SQL injection (parameterized queries) N/A Not applicable.
High Correctness Logic is correct, handles edge cases Fail Feature is non-functional as committed: no .sha256 sidecars exist and the pinned ref is the PR's parent (F-1).
High Correctness Error handling is explicit, no swallowed exceptions Pass Every failure path echos to stderr and return 1; opt-in dispatch propagates via exit $?.
High Correctness No race conditions or concurrency issues Pass mv replace avoids clobbering the running file; cross-FS atomicity caveat (F-5).
Medium Testing New code has corresponding tests N/A Repo has no shell-script test harness; none added.
Medium Testing Error paths and edge cases tested N/A
Medium Testing Existing tests still pass (no regressions) N/A
Medium Performance No N+1 queries or unbounded data fetching Pass
Medium Performance Long-running tasks use background jobs Fail (minor) curl has no --max-time; can hang indefinitely (F-7).
Medium Quality Follows existing codebase patterns Pass Consistent across all 6 scripts.
Medium Quality Changes are focused (single concern) Pass Scoped to self-update + dependency pinning.
Low Quality Meaningful names, no dead code Pass Clear naming, well-commented.
Low Quality Comments explain why, not what Pass Comments cite Jira IDs and rationale.
Low Quality No unnecessary dependencies added Pass Adds reliance on shasum (F-4).

Findings

  • File: scripts/{bash,fish,zsh}/{cli,spm}.sh (the SELF_UPDATE_REF line, e.g. scripts/bash/cli.sh:84)

  • Severity: High

  • Reviewer: stack:code-reviewer (verified by orchestrator)

  • Issue: The feature does not work as committed. script_self_update fetches ${base_url}.sha256, but no .sha256 sidecar files exist anywhere in the repo, so --self-update always aborts at the checksum-download step ("failed to download checksum"). Separately, SELF_UPDATE_REF=db817c37… is the parent commit of this PR's head — verified to NOT contain the hardened script. So the pin references the old, pre-hardening code, not the commit that introduces these changes. (It fails safe — it aborts rather than rolling back — but the feature is effectively dead-on-arrival.)

  • Suggestion: Before merge: (1) generate and commit the six *.sha256 sidecars next to each script; (2) bump SELF_UPDATE_REF in all 6 scripts to the commit that actually contains the hardened scripts + sidecars (the merge commit), not the parent. Since the ref must change after merge to be correct, document this as a required post-merge/release step or land it via a follow-up that pins to the now-immutable merge SHA.

  • File: scripts/*/*.shactual_sum=$(shasum -a 256 …)

  • Severity: Medium

  • Reviewer: stack:code-reviewer

  • Issue: shasum ships by default on macOS but is not guaranteed on minimal Linux images (needs Perl Digest::SHA). On such hosts actual_sum would be empty; the guard checks -z "$expected_sum" but not -z "$actual_sum". (These scripts are macOS-oriented — /opt/homebrew, bsdtar — so impact is limited, but CI/Linux use is plausible.)

  • Suggestion: Prefer sha256sum when present, fall back to shasum -a 256, and add a [[ -z "$actual_sum" ]] abort guard.

  • File: scripts/*/cli.sh & spm.shmv -f "$tmp_script" "$SCRIPT_PATH"

  • Severity: Medium

  • Reviewer: stack:code-reviewer (verified by orchestrator)

  • Issue: SCRIPT_PATH is computed as a relative path (realpath --relative-to="$GIT_ROOT" "$0"). mv resolves a relative target against the current working directory, not the script's location. Run from any dir other than GIT_ROOT (e.g. a subdirectory, or a pre-commit hook that changes CWD), the update writes to the wrong path or fails. (This path semantics predates the PR — the old > "$SCRIPT_PATH" had the same issue — but the new code retains it.)

  • Suggestion: For the self-update path, resolve absolutely: mv -f "$tmp_script" "${GIT_ROOT:+$GIT_ROOT/}$SCRIPT_PATH", or set SCRIPT_PATH=$(realpath "$0") unconditionally.

  • File: scripts/*/*.shscript_self_update checksum fetch

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The .sha256 is fetched from the same origin (raw.githubusercontent.com, same repo, same pinned ref) as the script. Because the ref is an immutable SHA, GitHub already content-addresses it, so the checksum mainly guards against truncated/corrupted downloads — it is not an authenticity signature and adds no defense against a repo/account compromise.

  • Suggestion: Add a one-line comment clarifying the checksum is an integrity (not authenticity) check; consider GPG-signed releases verified against a key baked into the script for true tamper-resistance.

  • File: scripts/*/*.sh — both curl -fsSL invocations

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: No --max-time/--connect-timeout; a stalled GitHub connection hangs the terminal indefinitely.

  • Suggestion: Add --connect-timeout 10 --max-time 30 to both curl calls.

  • File: scripts/*/*.shmv -f replace

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: mv across filesystems (TMPDIR on tmpfs vs. script on another FS) degrades to copy+unlink, which is not atomic — undercutting the "atomic replace" comment on Linux.

  • Suggestion: Stage into a temp file in the target directory (mktemp "$(dirname "$SCRIPT_PATH")/.XXXXXX"), then mv within the same filesystem.

  • File: scripts/*/*.sh — shebang sanity check ordering

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: head -c2 … | grep -q '^#!' runs before checksum verification; it's a corruption check, not a security gate (a malicious #!-prefixed file passes it).

  • Suggestion: Reorder after the checksum compare, or add a comment noting it's a sanity check only.

  • File: scripts/*/*.shSELF_UPDATE_REF / SELF_UPDATE_RELPATH declarations

  • Severity: Low (style)

  • Reviewer: stack:code-reviewer (corrected by orchestrator)

  • Issue: These are assigned literals at script-load global scope. Note: the reviewer's claim that an inherited env var could override SELF_UPDATE_REF is incorrect — the literal assignment overwrites any inherited value, so this is not an injection vector. Marking readonly is still good hygiene to prevent later in-script reassignment.

  • Suggestion: readonly SELF_UPDATE_REF=… ; readonly SELF_UPDATE_RELPATH=….

Note: the reviewer also raised findings on Package.swift (.all(ports:)) and an httphttps change in download_binary. Neither appears in this PR's diff — they are out of scope for PR #30 and were dropped.


Verdict: FAIL — strong security direction, but the self-update is non-functional as committed (missing .sha256 sidecars + pinned ref points at the parent/old script). Land the sidecars and correct the pin before merge.

…EVA11Y-475,477,478)

Addresses PR #30 review. Per maintainer intent, auto-update should always take the latest from main rather than pin to a commit, so:

- Revert self-update fetch to main HEAD and restore auto-update on every run (best-effort: script_self_update || true, so offline/integrity failures never block the tool).

- Keep SHA-256 verification and commit the 6 .sha256 sidecars so the integrity check actually works against main (regenerate on every script change to main).

- Fetch the sidecar first and skip when the on-disk copy already matches (avoids a redundant download/rewrite each run).

- Portable hashing: prefer sha256sum, fall back to shasum -a 256; guard empty actual_sum.

- Resolve SCRIPT_PATH absolutely so the replace never depends on CWD; stage within the target dir then mv so the replace is atomic on the same filesystem.

- Add curl --connect-timeout/--max-time; run the shebang sanity-check after checksum verification; mark the branch/relpath constants readonly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@sunny-se sunny-se left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code Review — browserstack/AccessibilityDevTools#30 (12fc1288)

Summary: PR adds SHA-256 sidecar verification and atomic temp-dir staging to the self-update mechanism across all six shell installers, and pins SPM dependency from branch:"main" to a specific .revision() SHA. However, the implementation diverges fundamentally from the stated DEVA11Y-475/478 fixes: auto-update still fires unconditionally on every invocation, still fetches from mutable refs/heads/main, and the co-located sidecar checksum provides only download-integrity — not supply-chain protection.

Findings (7)

🔴 Critical

  1. scripts/bash/cli.sh:178 — PR description states "self-update only runs via an explicit --self-update subcommand; it no longer runs automatically" as the fix for DEVA11Y-475/F-003 (Critical). Actual code: script_self_update || true still fires unconditionally on every invocation — no --self-update subcommand exists anywhere in the diff. The original automatic execution vector is not eliminated. The || true only ensures failures are non-fatal; it does not make the update opt-in. Either implement the opt-in subcommand gate as described, or update the PR/tickets to accurately reflect this is hardening-in-place rather than remediation.

  2. scripts/bash/cli.sh:91 — PR description claims "fetches from an immutable revision (SELF_UPDATE_REF)" as the DEVA11Y-478/F-006 fix. Actual code: SELF_UPDATE_BRANCH="main" constructs the fetch URL as refs/heads/main — still a mutable branch HEAD. No SELF_UPDATE_REF variable exists anywhere in the diff. An attacker with write access to main can push a malicious script and simultaneously push a matching .sha256 sidecar; the integrity check passes and the payload executes on every user's next run. The stated immutable-pin fix was not implemented.

🟠 High

  1. scripts/bash/cli.sh — SHA-256 sidecar co-located with the script on the same mutable origin provides no supply-chain guarantee. An attacker with push access to main satisfies the integrity check trivially by updating both files atomically. True supply-chain protection requires a detached signing key (e.g., cosign, GPG) not stored in the same repo.

  2. scripts/bash/cli.sh — No CI enforcement keeps .sha256 sidecars in sync with scripts. The committed sidecars will become stale on the first subsequent commit that modifies any script without regenerating its sidecar, silently breaking self-update for all users (checksum mismatch → update refused). A CI step running shasum -a 256 -c against each script must accompany this PR.

🟡 Medium

  1. scripts/bash/cli.sh:91SELF_UPDATE_BRANCH and SELF_UPDATE_RELPATH are declared readonly at global script scope. If these scripts are ever sourced (. scripts/bash/cli.sh) rather than executed, the readonly declarations persist in the parent shell; re-sourcing fails with "readonly variable". Move declarations inside script_self_update() as locals, or add a guard.

  2. scripts/bash/cli.sh — The full script_self_update() body and _self_update_sha256() helper are copied verbatim across all six scripts. Only SELF_UPDATE_RELPATH and filenames differ. Any future security fix must be applied six times without a mechanism to detect divergence. A sourced lib (scripts/lib/self_update.sh) or CI diff-check between the six copies would prevent silent drift.

🔵 Low

  1. scripts/bash/spm.sh:63 — SPM revision db817c37 is hardcoded in three separate spm.sh files with no date stamp, no CI pin-staleness check, and no automated bump path. A comment capturing the pin date and a Jira reference to the tag-automation follow-up would reduce future confusion.

Jira: DEVA11Y-475, DEVA11Y-477, DEVA11Y-478, APPSEC-415

sunny-se
sunny-se previously approved these changes Jun 19, 2026

@sunny-se sunny-se left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM — reviewed via harness stack:pr-review. Self-update hardening is a net security improvement.

Conditions (please address before merge):

  1. Stale PR body: Description still says "Pinned source: fetches from an immutable revision (SELF_UPDATE_REF)" but commit 2 reverted to SELF_UPDATE_BRANCH="main" (mutable). Please update the PR body to reflect final state — hardening is in the download/verify/replace mechanism, not source immutability.
  2. Strongly recommend: Add a CI check or pre-commit hook that validates sha256sum -c scripts/**/*.sha256 on every PR touching scripts/. Without this, the first post-merge script edit will silently break self-update.

Non-blocking notes:

  • Same-origin checksum acknowledged as not MITM-proof — acceptable for now.
  • SPM .revision() pin will go stale — tracked for future tagging.

…rs (DEVA11Y-475)

Addresses PR #30 review (Sunny, condition 2): adds a GitHub Actions workflow that runs on any PR/main push touching scripts/, failing when a launcher script is missing its .sha256 sidecar or when a sidecar is out of date. Self-update verifies each fetched script against its committed sidecar, so a script edit without a sidecar regen would otherwise silently break self-update (checksum mismatch -> update refused) for all users.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread .github/workflows/verify-selfupdate-checksums.yml Fixed
@Crash0v3rrid3

Copy link
Copy Markdown
Collaborator Author

Thanks for the review @sunny-se — both pre-merge conditions addressed in 870b4f7:

  1. PR body updated to reflect the final state — self-update intentionally tracks latest main (maintainer's choice), so the description now frames this as hardening the download/verify/atomic-replace mechanism rather than source immutability, and drops the stale "opt-in --self-update / SELF_UPDATE_REF" wording. The same-origin checksum (integrity, not authenticity) and the SPM .revision() staleness are both called out under "Known limitations".
  2. CI added.github/workflows/verify-selfupdate-checksums.yml runs on every PR/main push touching scripts/, failing if any launcher script is missing its .sha256 sidecar or if a sidecar is out of date (sha256sum -c). Verified green against the current six sidecars. This keeps a future script edit from silently breaking self-update.

Detached-signature supply-chain protection remains out of scope and tracked separately.

…ntain permissions'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

@Crash0v3rrid3 Crash0v3rrid3 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Claude Code Review (automated) — 3 inline finding(s). Full report in the PR comment below. Verdict: Passed.

verify-sidecars:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Low] Pin actions/checkout to a full SHA (repo convention)

The existing Semgrep.yml SHA-pins all actions (actions/checkout@c85c95e3… # v3.5.3). This @v4 floating tag breaks that convention and GitHub's supply-chain hardening guidance.

Suggestion:

Suggested change
- uses: actions/checkout@v4
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

Reviewer: stack:code-reviewer

status=0
# 1. Every self-updating script must have a sidecar.
for script in scripts/**/*.sh; do

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Low] Add a zero-scripts guard (defensive)

With nullglob, this loop is a no-op if no *.sh are found and status stays 0. Today a true "no scripts" state is still caught by check-2's empty-sidecar guard (exit 1), but a symmetric guard here makes check-1 self-sufficient.

Suggestion: Before the loop, collect into an array and exit 1 if empty, mirroring the existing ${#sidecars[@]} -eq 0 guard.

Reviewer: stack:code-reviewer

continue
fi
# Sidecars store "<sha256> <basename>", so verify from the script's dir.
if ( cd "$dir" && sha256sum -c "$(basename "$sidecar")" ); then

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Low] Silence sha256sum -c stdout for cleaner logs

sha256sum -c prints <file>: OK/FAILED to stdout, doubling the success line and emitting an un-annotated FAILED. Failures are still surfaced via the else-branch ::error::, so this is log clarity only.

Suggestion: Redirect the check's stdout to /dev/null (sha256sum -c "$(basename "$sidecar")" > /dev/null) and rely on the annotation.

Reviewer: stack:code-reviewer

@Crash0v3rrid3

Copy link
Copy Markdown
Collaborator Author

Claude Code PR Review

PR: #30Head: 4293136Reviewers: stack:code-reviewer (+ orchestrator verification)

Summary

Re-review after the self-update hardening landed: the 6 launcher-script changes + committed .sha256 sidecars, a new verify-selfupdate-checksums.yml CI gate (with a CodeQL-autofix permissions: block), and the SPM .revision() pin. This pass focuses on the new workflow; the six scripts were reviewed previously and are unchanged.

Review Table

Priority Category Check Status Notes
High Security No hardcoded secrets or credentials Pass Creds from env; none embedded.
High Security Authentication/authorization checks present N/A
High Security Input validation and sanitization Pass Workflow uses no untrusted ${{ github.event.* }} inputs (injection-safe); scripts validate shebang + checksum.
High Security No IDOR — resource ownership validated N/A
High Security No SQL injection (parameterized queries) N/A
High Correctness Logic is correct, handles edge cases Pass Workflow checks both directions (missing sidecar / drift); verified green on the 6 sidecars. Minor degenerate-case guard gap (F-2, Low).
High Correctness Error handling is explicit, no swallowed exceptions Pass status accumulates; cd && sha256sum short-circuit sets failure correctly.
High Correctness No race conditions or concurrency issues Pass
Medium Testing New code has corresponding tests Pass The workflow itself is the regression guard; logic verified locally.
Medium Testing Error paths and edge cases tested Pass Tamper case manually confirmed to fail; pass case green.
Medium Testing Existing tests still pass (no regressions) Pass Additive.
Medium Performance No N+1 queries or unbounded data fetching N/A
Medium Performance Long-running tasks use background jobs N/A
Medium Quality Follows existing codebase patterns Fail (Low) Existing Semgrep.yml SHA-pins its actions; this workflow uses the floating actions/checkout@v4 tag (F-3).
Medium Quality Changes are focused (single concern) Pass
Low Quality Meaningful names, no dead code Pass
Low Quality Comments explain why, not what Pass Workflow header explains the drift risk it guards.
Low Quality No unnecessary dependencies added Pass Only actions/checkout.

Findings

  • File: .github/workflows/verify-selfupdate-checksums.yml:27

  • Severity: Low

  • Reviewer: stack:code-reviewer (grounded by orchestrator)

  • Issue: actions/checkout@v4 is a mutable floating tag. The repo's existing Semgrep.yml SHA-pins all actions (actions/checkout@c85c95e3… # v3.5.3), so this breaks repo convention and GitHub's supply-chain hardening guidance.

  • Suggestion: Pin to a full SHA, e.g. actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2.

  • File: .github/workflows/verify-selfupdate-checksums.yml:36

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: With nullglob, the check-1 loop over scripts/**/*.sh is a no-op if no scripts are found, and status stays 0. (In practice a "no scripts" state is still caught by check-2's empty-sidecar guard, which exit 1s — so this is defensive, not an actual silent-pass today.)

  • Suggestion: Add a symmetric zero-length guard for the scripts glob, mirroring the existing ${#sidecars[@]} -eq 0 guard.

  • File: .github/workflows/verify-selfupdate-checksums.yml:58

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: sha256sum -c prints <file>: OK/FAILED to stdout, doubling the success log line and emitting an un-annotated FAILED. (Failures are still surfaced — the else branch emits a ::error:: — so this is log clarity, not a missed signal.)

  • Suggestion: Redirect the check's stdout to /dev/null and rely on the ::error:: annotation; optionally promote success to ::notice::.

Reviewer notes explicitly requiring no action (confirmations, not findings): the ( cd "$dir" && … ) subshell correctly propagates cd failure via && (F-4); both drift directions — missing sidecar and orphan sidecar — are covered, and the scripts/** path filter does fire on sidecar-only changes (F-5).

Severity note: the reviewer rated F-1/F-2 as Medium; the orchestrator recalibrated both to Low after confirming the failure path is annotated (F-1) and the degenerate case is already guarded by check 2 (F-2).

Verification performed

  • All six .sha256 sidecars verified against their scripts (shasum -a 256 -c) → all OK; the workflow YAML is valid and its logic was traced (both checks + exit propagation).
  • The CodeQL-autofix permissions: contents: read (least-privilege) is correct and present.

Verdict: PASS — the hardening PR plus its CI guard are correct and the workflow does catch sidecar drift. Only Low-severity polish remains: SHA-pin actions/checkout (matches repo convention, F-3), add a zero-scripts guard (F-2), and silence sha256sum -c stdout (F-1). None blocking.

…A11Y-477)

The generated Dummy Package.swift was pinned to revision db817c3 (DEVA11Y-477/F-005). Per maintainer intent, the SPM scan should always resolve the latest plugin from main — consistent with self-update now tracking main — so revert the dependency to branch: "main". Regenerated the three spm.sh.sha256 sidecars to match (cli.sh unchanged). Reopens the F-005 mutable-source concern by design; revisit with a release tag once tagging exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

3 participants