Skip to content

fix(self-update): make script self-update opt-in, not automatic (DEVA11Y-475) - #34

Open
Crash0v3rrid3 wants to merge 2 commits into
mainfrom
fix/DEVA11Y-475-secure-self-update
Open

fix(self-update): make script self-update opt-in, not automatic (DEVA11Y-475)#34
Crash0v3rrid3 wants to merge 2 commits into
mainfrom
fix/DEVA11Y-475-secure-self-update

Conversation

@Crash0v3rrid3

Copy link
Copy Markdown
Collaborator

What & why

Addresses DEVA11Y-475 (F-003, CWE-494, Critical) — self‑update fetched a mutable ref and applied it on every invocation.

Since the ticket was filed (against 520aef6), integrity verification has already landed on main: script_self_update now fetches a SHA‑256 sidecar, verifies it, stages atomically, and fails closed (rc 2) on mismatch. Two of the ticket's remediation points remained:

  1. Auto‑runs on every invocation — the ticket explicitly asks to "make self‑update an explicit opt‑in subcommand rather than an automatic side‑effect of every invocation." ✅ This PR.
  2. Still pinned to the mutable refs/heads/main rather than an immutable signed tag. ⏳ Deferred — depends on a release/tagging + per‑tag checksum pipeline that does not exist yet (no tags in the repo). Tracked under APPSEC-415.

Change

  • Remove the unconditional script_self_update call that ran before subcommand parsing.
  • Gate it behind an explicit self-update subcommand across all six launchers (bash/zsh/fish × cli/spm); the integrity/rc‑2 handling is preserved, and the command exits with the update's return code.
  • Document the self-update subcommand in the README.

Trade‑off for reviewers

The current design comment described an intentional "always‑run‑latest, never‑block" auto‑update. Making it opt‑in is a deliberate UX change requested by AppSec — devs will no longer silently get the newest launcher and must run self-update (or re‑install). Flagging for product + AppSec sign‑off; this is why it's a draft.

Testing

  • bash -n syntax check passes on all six scripts.

🤖 Generated with Claude Code

…11Y-475)

The launcher scripts fetched and applied a self-update on every invocation,
before subcommand parsing. Integrity verification (SHA-256 + atomic staging)
already landed on main, but the unconditional run meant a single compromise of
the fetched source would still silently replace the running script on every
developer machine with no way to opt out. Gate self-update behind an explicit
`self-update` subcommand across all six launchers (bash/zsh/fish x cli/spm)
and document it in the README.

Note: the update source is still pinned to the mutable refs/heads/main; pinning
to an immutable signed tag + per-tag checksum is follow-up work that depends on
a release/tagging pipeline (tracked under APPSEC-415).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Crash0v3rrid3
Crash0v3rrid3 marked this pull request as ready for review July 30, 2026 10:12
@Crash0v3rrid3
Crash0v3rrid3 requested a review from a team as a code owner July 30, 2026 10:12
…1Y-475)

Code review caught that the previous commit edited all six launchers but left
their .sha256 sidecars stale, which would make the new `self-update` command
fail the integrity check on every run (dead on arrival). Regenerate all six
sidecars to match the edited scripts.

Also: update the pre-existing header comment that still claimed self-update
runs automatically ('always run the newest version'), and soften the README to
describe the checksum as a transit-integrity check rather than an authenticity
signature (script and checksum share one origin on refs/heads/main).

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

Copy link
Copy Markdown
Collaborator Author

Claude Code PR Review

PR: #34Head: 0f881c4Reviewers: stack:pr-review (aggregated correctness/security/testing/perf/quality)

Summary

Makes launcher self-update opt-in (DEVA11Y-475): self-update runs only via an explicit self-update subcommand instead of automatically on every invocation, across all six launchers (scripts/{bash,zsh,fish}/{cli,spm}.sh). Updates the README, corrects a stale header comment, and regenerates the six .sha256 sidecars. Integrity verification (SHA-256 + atomic staging) already existed on main.

Review Table

Priority Category Check Status Notes
High Security No hardcoded secrets or credentials Pass
High Security Authentication/authorization checks present N/A
High Security Input validation and sanitization Pass
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 SUBCOMMAND ordering correct; exactly one gated self-update call site per file
High Correctness Error handling is explicit, no swallowed exceptions Pass rc 0/1/2 propagated via exit "$_self_update_rc"
High Correctness No race conditions or concurrency issues N/A
Medium Testing New code has corresponding tests N/A Launcher shell; no test harness
Medium Testing Error paths and edge cases tested N/A
Medium Testing Existing tests still pass (no regressions) Pass
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 Pass Mirrors register-pre-commit-hook dispatch
Medium Quality Changes are focused (single concern) Pass
Low Quality Meaningful names, no dead code Pass No residual auto-run
Low Quality Comments explain why, not what Pass Header comment corrected
Low Quality No unnecessary dependencies added Pass

Sidecar integrity verified: recomputed shasum -a 256 on all six scripts — all MATCH their regenerated .sha256 sidecars, so self-update is not dead-on-arrival. Dispatch verified correct in all six; no residual auto-run.

Findings

No Critical/High/Medium findings. Non-gating nits only:

  • File: README.md:212-228
  • Severity: Low
  • Reviewer: stack:pr-review
  • Issue: The self-update (and existing pre-commit-hook) examples document only the -spm- launcher names, not the CLI variant.
  • Suggestion: Add a CLI-launcher note if desired; consistent with the README's existing SPM-focused convention otherwise.

By-design / residual (non-gating): self-update still tracks the mutable main HEAD rather than an immutable tag; immutable-tag pinning is deferred to APPSEC-415. The SHA-256 sidecar is correctly framed as a corruption/integrity check, not an authenticity signature (script and checksum share one origin).


Verdict: PASS — sidecars match, dispatch is correct and side-effect-free on the normal path, docs accurate. No gating findings.

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.

1 participant