Skip to content

Add numan update --self for CLI binary upgrades - #94

Merged
tonythethompson merged 9 commits into
masterfrom
cursor/update-self-flag-689b
Aug 6, 2026
Merged

Add numan update --self for CLI binary upgrades#94
tonythethompson merged 9 commits into
masterfrom
cursor/update-self-flag-689b

Conversation

@tonythethompson

@tonythethompson tonythethompson commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds numan update --self so users can upgrade the numan CLI itself (not just registry packages).

  • Standalone installs: fetch the latest GitHub Release, download the archive plus SHA256SUMS and SHA256SUMS.sig, verify the Ed25519 signature with a baked-in release public key, verify the archive digest, extract, and replace the running binary (Unix: stage bytes + mode on a temp file then persist; Windows: stage then rename-aside).
  • Homebrew / winget / cargo installs: do not self-replace. update --self prints the package-manager upgrade command. update --self --check queries GitHub Releases first and reports whether a newer version exists, then prints the upgrade hint only when an update is available.
  • Release workflow signs SHA256SUMS when NUMAN_RELEASE_SIGNING_KEY is set (scripts/sign-sha256sums.py).
  • Apply path rejects non-https URLs; download_file refuses HTTPS→HTTP redirects.

Review follow-ups

  • CodeRabbit / Greptile: Unix staging, signed checksums, HTTPS gates, expanded tests.
  • Codex P1: managed --check now compares release versions before printing the upgrade hint.

Maintainer note

Add repository secret NUMAN_RELEASE_SIGNING_KEY (base64 32-byte Ed25519 seed matching RELEASE_SUMS_PUBLIC_KEY_B64 in src/cmd/self_update.rs). See docs/RELEASING.md.

Test plan

  • cargo test --lib self_update (35 tests)
  • cargo clippy -- -D warnings
  • cargo fmt --check
  • Add NUMAN_RELEASE_SIGNING_KEY secret before the next tagged release
  • Manual: signed release then numan update --self --check / apply
  • Manual: brew/cargo path --check reports version status
Open in Web Open in Cursor 

Review in cubic

Standalone installs download the matching GitHub Release asset,
verify SHA256SUMS, and replace the binary. Homebrew, winget, and
cargo installs print the exact upgrade command instead.

Co-authored-by: Anthony Thompson <github@trackdub.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@cursor[bot], you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 88e56b4b-d055-4554-a0d6-d2efc86d4f6b

📥 Commits

Reviewing files that changed from the base of the PR and between 198d874 and bdc96bb.

📒 Files selected for processing (9)
  • .github/workflows/release.yml
  • AGENTS.md
  • CHANGELOG.md
  • README.md
  • docs/RELEASING.md
  • scripts/sign-sha256sums.py
  • src/cmd/self_update.rs
  • src/core/integrity.rs
  • src/install/download.rs
📝 Walkthrough

Walkthrough

Added numan update --self with installation-method detection, GitHub Release retrieval, version checks, SHA-256 verification, archive extraction, and platform-specific executable replacement. Added check-only support, streaming integrity checks, HTTPS redirect protection, tests, CLI wiring, and documentation.

Changes

Self-update flow

Layer / File(s) Summary
CLI self-update wiring
src/cmd/mod.rs, src/cmd/update.rs
The update command adds --self and delegates self-update execution before package lifecycle handling.
Installation and release resolution
src/cmd/self_update.rs
The module detects installation methods, selects platform assets, retrieves GitHub Release metadata, compares versions, and tests check-only behavior.
Verified binary installation
src/cmd/self_update.rs, src/install/download.rs
The flow downloads artifacts, enforces HTTPS redirects, verifies SHA-256 checksums, extracts bounded archives, discovers binaries, and replaces the executable with Unix and Windows handling.
Streaming integrity verification
src/core/integrity.rs
File hashing now uses buffered streaming reads while preserving public verification signatures.
Self-update documentation
AGENTS.md, CHANGELOG.md, README.md
Documentation describes self-update commands, package-manager fallbacks, checksum verification, binary replacement, and check-only behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Pipeline Stage Enum Ordering ✅ Passed The complete PR diff and tracked solution contain no SessionWorkflowStage enum, member references, or related comparisons; the ordering check is not applicable.
Gpu/Cpu Runtime Boundary ✅ Passed The PR changes only Rust CLI, documentation, and download/integrity files; no inference/, CPU/GPU requirements, main.py, or C# diarization files are modified.
Managed Host Restart Safety ✅ Passed PASS: The PR changes self-update, integrity, download, and documentation files; none of the four checked host components or restart/lease symbols appear in the source or PR diff.
Title check ✅ Passed The title clearly and concisely describes the main change: adding self-updates for the numan CLI binary.
Description check ✅ Passed The description directly explains self-update behavior, installation methods, verification, tests, and remaining manual release checks.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/update-self-flag-689b
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch cursor/update-self-flag-689b

Warning

Review ran into problems

🔥 Problems

Linked repositories: Public OSS repositories can only analyze public repositories installed in this organization. Analyzed tonythethompson/QuickShell, tonythethompson/numan, tonythethompson/dependency-chain-substrate, skipped Trackdubllc/Trackdub.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 286-287: Update the README self-update command descriptions to
clarify that --check only checks for newer binaries on standalone installations,
while Homebrew, winget, and cargo installations instead display the relevant
package-manager upgrade hint; preserve the existing command examples and explain
the managed-install behavior alongside them.

In `@src/cmd/self_update.rs`:
- Around line 592-632: Update src/cmd/self_update.rs#L566-L590 and `#L592-L632`:
add an exe: &Path parameter to execute_with_client, pass the executable from
execute, and use a fixed standalone path such as /usr/local/bin/numan in both
tests instead of skipping based on detect_install_method; populate
FakeClient.files and add apply-path tests covering checksum mismatch, missing
SHA256SUMS, an asset absent from SHA256SUMS, and a missing platform asset.
- Around line 365-371: Update replace_binary_unix to capture the existing
destination binary’s permission mode before write_bytes_atomic, then apply that
mode to the replacement while ensuring the owner-execute bit is set. Change
make_executable or its call path so it no longer unconditionally applies 0o755,
preserving group/other permissions and existing restrictive settings.
- Around line 566-590: Expand the self-update tests around FakeClient by
populating files and injecting the install target so failure paths execute
safely against temporary paths. Add coverage for checksum mismatch, missing or
incomplete SHA256SUMS, missing platform assets, and verify replace_binary
rejects empty bytes without modifying the target. Add tests for
locate_extracted_numan at root, one directory deep, and absent; parse_sha256sums
rejecting short/non-hex hashes; and parse_release_version rejecting malformed
tags.
- Around line 349-362: Update the non-Windows dispatch arm in replace_binary to
use cfg(unix) instead of cfg(not(windows)), matching the predicate on
replace_binary_unix. Keep the Windows branch unchanged.
- Around line 242-254: Move the release_asset_name call out of the initial setup
and place it immediately before the apply/install logic that consumes
asset_name, after the is_newer_than and check-mode early returns. Keep version
and status checks independent of platform asset support so --check reports
up-to-date or available status on every detected platform.
- Around line 374-410: Update replace_binary_windows so the new binary is fully
written and flushed to a temporary file in the destination parent before
renaming dest to the backup. Then install the staged file and preserve rollback
behavior; if restoration fails, include explicit recovery guidance in the
returned error message while retaining the existing stale-backup cleanup.
- Around line 27-34: Update the Cargo branch in InstallMethod::upgrade_hint to
return the command “cargo install --locked --force numan-cli”, preserving the
existing hints for all other installation methods.

In `@src/cmd/update.rs`:
- Around line 88-95: Add a unit test for the self-update validation in
execute_with_hooks that constructs UpdateArgs with self_update: true and
package: Some(...), then asserts the returned error matches "--self cannot be
combined with a package name". Keep the existing early return before
warn_stale_lifecycle_journal and state-lock handling so self-update does not
emit stale-journal warnings or mutate managed state.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 57537df5-47ab-4ec0-a877-e5d10f0bf7d5

📥 Commits

Reviewing files that changed from the base of the PR and between 5b186bc and 6bf16da.

📒 Files selected for processing (6)
  • AGENTS.md
  • CHANGELOG.md
  • README.md
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
  • src/cmd/update.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Real-Nu acceptance (windows-latest)
  • GitHub Check: Test (windows-latest)
  • GitHub Check: Analyze (rust)
🧰 Additional context used
📓 Path-based instructions (11)
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

**/*: Use Serena's semantic, symbol-aware tools as the primary tools for reading and editing code; use built-in Read, Glob, Grep, and Edit only under the stated exceptions.
Before editing a code file, inspect its symbol overview, read the specific symbols being changed, and edit them with Serena's symbol-aware tools.
Understand the existing code before changing it and make the smallest change that satisfies the request; avoid unrelated cleanup, premature abstractions, unnecessary error handling, feature flags, and compatibility shims.
Prefer editing existing files over creating new ones, and never create Markdown or README files unless explicitly requested.
For exploratory questions, provide a 2–3 sentence recommendation with the main tradeoff and do not implement until the user agrees.
For UI or frontend changes that cannot be tested in a browser, explicitly state that browser testing was not performed rather than claiming success.
Address security issues when discovered, including injection, XSS, SQL injection, path traversal, and secret leaks.
Pause and obtain confirmation before destructive, hard-to-reverse, externally visible, or third-party-upload actions, including deleting files or branches, dropping tables, force-pushing, modifying CI/CD, posting externally, or uploading content.
When blocked, investigate the root cause instead of bypassing it with --no-verify, --force, or deletion; investigate unfamiliar files, branches, and configuration before deleting them.
Only commit when explicitly asked; do not proactively update git configuration or push changes.
Do not skip Git hooks unless explicitly asked; if a pre-commit hook fails, fix the issue, re-stage, and create a new commit rather than amending.
Stage files by name rather than using git add -A or git add ., to avoid accidentally including secrets or large binaries.
Use a HEREDOC for commit messages, and add co-author attribution only when the user explicitly requests the exact trailer.
Do not force-pus...

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • CHANGELOG.md
  • README.md
  • AGENTS.md
  • src/cmd/self_update.rs
**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php,c,cpp,h,hpp,cs,swift,kt,kts}

📄 CodeRabbit inference engine (CLAUDE.md)

Add comments only when the WHY is non-obvious; do not narrate what the code does, reference the current task, or reference the PR in comments.

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
!**/.env,!**/credentials.json,!**/*.pem

📄 CodeRabbit inference engine (CLAUDE.md)

Do not commit files that appear to contain secrets, including .env, credentials.json, and PEM files; warn before doing so even if explicitly requested.

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • CHANGELOG.md
  • README.md
  • AGENTS.md
  • src/cmd/self_update.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the Rust 2021 edition.
Use anyhow::Result with .context(...) in application code; use thiserror for library error types that callers match on.
Use clap derive macros for CLI definitions.
Use serde with serde_json or toml for serialization.
Function parameters must use &Path, not &PathBuf.
Library code must not panic; error paths should return anyhow::Result with context where appropriate.
Add or update tests for behavior changes, including relevant failure paths.
New mutating code paths must acquire the mutation lock via acquire_mutation_lock(root) and snapshot the lockfile before writes.
Lockfile, journal, and state-file JSON writes must use write_json_atomic.
numan install must write only to $NUMAN_ROOT; it must not invoke Nu or register plugins/autoloads.
Only activate and deactivate may modify Nu integration state.
Treat the lockfile as the authoritative source of truth; derived projections such as autoload state must not be authoritative.
Install payloads under versioned, content-addressed paths and never overwrite them in place.
Never overwrite foreign autoload files; respect OWNERSHIP_MARKER.
Pass plugin paths through environment variables only; do not use runtime interpolation in Nu program strings.

**/*.rs: All CI gates must pass: cargo test, cargo clippy -- -D warnings, and cargo fmt --check.
Every mutating command—including install, remove, update, gc, and future nupm import—must call acquire_mutation_lock(root).
Lockfiles, journals, and state files must use write_json_atomic; partial writes are not allowed.
Pending activation, autoload, and lifecycle journals must be stored under $NUMAN_ROOT/state/.
Module autoload identity must match all four fields: Nu executable hash, Nu version, vendor autoload directory, and managed file path; the lockfile module_activation value is authoritative.
Never overwrite foreign autoload files; respect OWNERSHIP_MARKER.
Pass paths to Nu only throu...

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run and keep cargo fmt/rustfmt clean, and ensure cargo clippy -- -D warnings passes.

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
**/*.{rs,nu}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{rs,nu}: Real-Nu acceptance tests must be marked #[ignore] and should be run when changes affect activation or nupm import; unit tests must not spawn real nu and should use injectable seams such as FakeCandidateRunner or registrars.
The nupm integration must be read-only toward NUPM_HOME, must not execute build.nu, and must not perform bidirectional synchronization.

Unit tests must use FakeCandidateRunner or injectable registrars and must not spawn a real nu process.

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Match existing naming, module layout, and documentation level in the file being edited; update AGENTS.md, docs/, or command help when structure, conventions, or user-visible behavior changes.

Tests must cover failure modes, not only successful execution.

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • CHANGELOG.md
  • README.md
  • AGENTS.md
  • src/cmd/self_update.rs
**/*.{rs,md,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use the repository's established serialization and module conventions rather than introducing unrelated refactors.

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • CHANGELOG.md
  • README.md
  • AGENTS.md
  • src/cmd/self_update.rs
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Include failure context with .context("what failed") or equivalent anyhow context when propagating application errors.

Files:

  • src/cmd/update.rs
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
src/cmd/update.rs

📄 CodeRabbit inference engine (AGENTS.md)

Active-plugin update orchestration must be disabled by default and enabled only when NUMAN_ENABLE_ACTIVE_PLUGIN_MUTATION equals exactly 1; the flow is deactivate → upgrade → activate.

Files:

  • src/cmd/update.rs
**/*.md

📄 CodeRabbit inference engine (REVIEW.md)

Update documentation and AGENTS.md when project structure or conventions change.

Files:

  • CHANGELOG.md
  • README.md
  • AGENTS.md
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-06T02:18:09.860Z
Learning: Run CI-equivalent validation: `cargo test`, `cargo clippy -- -D warnings`, and `cargo fmt --check`; ignored acceptance tests require Nu 0.113 on PATH.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-06T02:18:09.860Z
Learning: Do not force-push to `master`; use imperative commit messages under 72 characters and feature/fix branch naming.
🔍 Remote MCP GitHub Copilot

Review-relevant context

  • PR #94 is a draft with one commit. Format, clippy, Linux/macOS tests, MSRV, and packaging checks passed; Windows/macOS acceptance and Rust CodeQL were still running, while CodeRabbit status remained pending.
  • The release workflow publishes exactly three supported targets: Linux GNU x86_64, Windows MSVC x86_64, and macOS ARM64. It packages the binary one directory deep and generates SHA256SUMS from all numan-* archives.
  • The current v0.2.0 release contains the three expected archive names plus SHA256SUMS, matching the new asset-selection logic.
  • Platform::detect() also recognizes Linux musl and macOS Intel, but the PR intentionally rejects those targets because no corresponding release assets are built.
  • --self rejects a package argument and bypasses package-state warnings and the mutation lock; this is intentional because it replaces the CLI executable rather than package data.
  • The PR’s remaining validation explicitly includes manual published-release --check testing and package-manager detection checks. The added tests cover helper logic and check-mode download avoidance, but not an end-to-end archive extraction or binary replacement.
  • Related PR #93 is closed and unmerged, so this PR’s install-method detection is independent of the proposed cross-channel install-guard implementation.
🔇 Additional comments (13)
AGENTS.md (1)

61-62: LGTM!

CHANGELOG.md (1)

10-13: LGTM!

README.md (1)

353-353: LGTM!

Also applies to: 383-383

src/cmd/mod.rs (1)

16-16: LGTM!

src/cmd/update.rs (1)

33-37: LGTM!

src/cmd/self_update.rs (8)

13-15: LGTM!


85-108: LGTM!


174-199: LGTM!


266-295: LGTM!


301-347: LGTM!


429-564: LGTM!


111-141: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Security Misconfiguration (CWE-347)

Reachability: External

Reachability path
● Entry
  src/cmd/mod.rs
│
▼
● Hop
  src/cmd/update.rs:70
  execute
│
▼
● Sink
  src/cmd/self_update.rs

Parser is solid; be honest about what the checksum actually proves.

The parsing is correct for GNU, BSD-star, and tab-separated forms, and the 64-hex-digit guard rejects junk lines cleanly.

One readiness-honesty point. SHA256SUMS and the archive come from the same origin with the same trust anchor, and SHA256SUMS is not signed. So this verification protects against a truncated or corrupted download. It does not protect against a compromised release. The PR description says the asset is "verified", which reads stronger than what the mechanism delivers. Either state the limitation in the README self-update section, or add minisign/cosign signature verification over SHA256SUMS in a follow-up.

Minor parser note in the same area: map.insert lets a later line silently override an earlier entry for the same basename after the path component is stripped. That grants no extra capability to an attacker who already controls the release, but rejecting duplicate keys would make the parser stricter for free.


201-203: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Sensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: External

Reachability path
● Entry
  src/cmd/mod.rs
│
▼
● Hop
  src/cmd/update.rs:70
  execute
│
▼
● Sink
  src/cmd/self_update.rs

Verify that download_file pins HTTPS across the redirect chain.

Both the archive and SHA256SUMS are fetched through download_file using URLs taken from the release JSON. src/install/download.rs is not in this diff, so its scheme enforcement, redirect policy, and timeout are unproven here.

This matters more than a normal download. An on-path attacker who can serve one plaintext hop can serve both the archive and SHA256SUMS, and the digest comparison then compares attacker bytes against an attacker hash. Verifying the archive only helps if the transport for both fetches is authenticated and encrypted. GitHub asset URLs redirect to objects.githubusercontent.com, so redirects must be followed. The scheme must stay https on every hop.

If download_file does not already enforce this, reject any non-https asset_url and sums_url in execute_with_client before the fetch.

#!/bin/bash
# Description: Inspect download_file transport properties: scheme checks, redirect policy, timeouts.
set -euo pipefail

fd -t f 'download.rs' src | while IFS= read -r f; do
  echo "===== $f ====="
  cat -n "$f"
done

echo "===== redirect / scheme / timeout usage across the crate ====="
rg -nP --type=rust -C3 'redirect\s*\(|Policy::|\.timeout\s*\(|starts_with\("https|scheme\(\)'

Comment thread README.md Outdated
Comment thread src/cmd/self_update.rs
Comment thread src/cmd/self_update.rs
Comment thread src/cmd/self_update.rs
Comment thread src/cmd/self_update.rs
Comment thread src/cmd/self_update.rs Outdated
Comment thread src/cmd/self_update.rs
Comment thread src/cmd/self_update.rs
Comment thread src/cmd/update.rs
@tonythethompson
tonythethompson marked this pull request as ready for review August 6, 2026 02:55

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @tonythethompson, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add numan update --self for CLI self-upgrades

✨ Enhancement 📝 Documentation 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add numan update --self [--check] to upgrade the numan CLI binary.
• For standalone installs, download GitHub Release assets, verify SHA256SUMS, and replace safely.
• For brew/winget/cargo installs, detect channel and print the exact upgrade command.
Diagram

graph TD
  A["numan update"] --> B{"--self?"}
  B --> C["Package update flow"]
  B --> D["self_update.rs"] --> E{{"GitHub Releases API"}} --> F[("Temp dir")] --> G["SHA256SUMS verify"] --> H["Extract + replace"]

  subgraph Legend
    direction LR
    _cmd["Command/module"] ~~~ _dec{"Decision"} ~~~ _ext{{"External"}} ~~~ _fs[("Filesystem")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Adopt a dedicated Rust self-update crate
  • ➕ Less bespoke code for release lookup, extraction, and binary swapping
  • ➕ Potentially better coverage for platform edge cases
  • ➖ Adds dependency surface area and constrains behavior to crate abstractions
  • ➖ May not support the exact SHA256SUMS/asset naming conventions used here without customization
2. Always delegate upgrades to package managers
  • ➕ Avoids in-place replacement complexity (especially Windows)
  • ➕ Aligns with OS/package-manager expectations
  • ➖ Does not help standalone installs (a primary target of this feature)
  • ➖ Would require users to manually figure out the right upgrade path

Recommendation: The current approach is appropriate: it keeps package-manager installs safe by printing explicit commands, and for standalone installs it includes checksum verification plus OS-specific replacement strategies. If maintenance burden grows (more targets, signing, more platforms), revisiting a well-supported self-update crate would be reasonable.

Files changed (6) +660 / -4

Enhancement (3) +648 / -0
mod.rsRegister new 'self_update' command module +1/-0

Register new 'self_update' command module

• Exports 'self_update' from the cmd module tree so 'update' can dispatch into it.

src/cmd/mod.rs

self_update.rsImplement GitHub Release-based self-update with verification and safe replacement +633/-0

Implement GitHub Release-based self-update with verification and safe replacement

• Adds standalone self-update: fetch latest GitHub Release metadata, select a platform-specific asset, download archive + 'SHA256SUMS', verify integrity, extract the 'numan' binary, and replace the running executable (atomic on Unix; rename-aside on Windows). Includes install-method detection to emit brew/winget/cargo upgrade hints and unit tests for key helpers.

src/cmd/self_update.rs

update.rsAdd '--self' flag and early dispatch to self-update flow +14/-0

Add '--self' flag and early dispatch to self-update flow

• Extends 'UpdateArgs' with '--self' and routes to 'cmd::self_update' before acquiring the mutation lock, rejecting combinations with explicit package names.

src/cmd/update.rs

Documentation (3) +12 / -4
AGENTS.mdDocument new 'update --self' command and module location +2/-1

Document new 'update --self' command and module location

• Updates the command/module inventory to describe 'numan update --self [--check]' and introduces the new 'self_update.rs' module responsibilities.

AGENTS.md

CHANGELOG.mdAdd changelog entry for CLI self-update +4/-0

Add changelog entry for CLI self-update

• Adds an Unreleased note describing 'numan update --self', including standalone behavior, checksum verification, and package-manager hints.

CHANGELOG.md

README.mdUpdate usage docs to include 'update --self' and clarify package updates +6/-3

Update usage docs to include 'update --self' and clarify package updates

• Clarifies that 'numan update' applies to package upgrades and documents the new self-update commands in examples and the command table.

README.md

@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds self-updates for standalone installations while directing package-manager installations to their native upgrade commands.

  • Fetches and compares GitHub release versions for self-update checks.
  • Authenticates release checksums with Ed25519 before verifying and extracting an archive.
  • Stages executable replacements before atomically installing them on Unix and adds rollback handling on Windows.
  • Extends the release workflow and documentation for signed checksum publication.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the staged Unix replacement resolves the destructive permission-failure path, and signed checksums establish independent authenticity before installation.

Important Files Changed

Filename Overview
src/cmd/self_update.rs Implements install-method detection, release discovery, signed-checksum verification, archive extraction, and platform-specific staged binary replacement; both previously reported defects are addressed.
.github/workflows/release.yml Generates and signs SHA256SUMS before uploading all distribution artifacts to the GitHub release.
scripts/sign-sha256sums.py Produces the detached base64 Ed25519 signature format consumed by the Rust verifier.
src/cmd/update.rs Integrates the self-update mode into the existing update command without altering package-update behavior.
src/install/download.rs Strengthens download transport handling by rejecting HTTPS-to-HTTP redirects.

Reviews (6): Last reviewed commit: "Add path context to streaming SHA-256 fi..." | Re-trigger Greptile

Comment thread src/cmd/self_update.rs Outdated
Comment thread src/cmd/self_update.rs Outdated
Inject exe into the self-update test seam, preserve Unix modes,
stage Windows replacements before rename, defer asset naming until
apply, require https URLs, tighten the cargo upgrade hint, clarify
README managed-install behavior, and expand failure-path tests.

Co-authored-by: Anthony Thompson <github@trackdub.com>
@qodo-code-review

qodo-code-review Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. HTTPS not enforced ✓ Resolved 🐞 Bug ⛨ Security
Description
execute_with_client downloads browser_download_url values without validating they are HTTPS,
while download_file supports arbitrary HTTP/HTTPS and local file:///path sources. This weakens
the trust boundary for a path that replaces the running executable (an attacker able to influence
the download URL/path could deliver a malicious binary with a matching SHA256SUMS).
Code

src/cmd/self_update.rs[R279-282]

+    client
+        .download(asset_url, &archive_path)
+        .with_context(|| format!("Failed to download {asset_name}"))?;
+    client
Relevance

●●● Strong

Security hardening; team has accepted integrity/trust-boundary fixes in core paths before.

PR-#23

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The self-update flow downloads asset_url and sums_url obtained from GitHub release metadata
without any scheme validation, and the shared downloader explicitly supports local file sources and
generic HTTP/HTTPS requests. This combination means the self-update path is not enforcing an
HTTPS-only transport requirement.

src/cmd/self_update.rs[256-284]
src/install/download.rs[7-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`numan update --self` accepts release-provided download URLs and passes them directly to the downloader. The downloader supports plaintext HTTP and local file paths; for a self-replacing updater, this is an unsafe input surface.

## Issue Context
Self-update is a high-trust path (it replaces the CLI binary). Even with SHA256SUMS verification, an attacker who can control both the archive and checksum delivery path can provide a matching checksum for a malicious archive.

## Fix Focus Areas
- src/cmd/self_update.rs[256-284]
- src/install/download.rs[7-41]

## What to change
1. In `execute_with_client`, validate `asset_url` and `sums_url` before downloading:
  - Reject any URL that is not `https://...`.
  - (Optional but recommended) parse URLs and explicitly reject `file://` and scheme-less/local paths.
2. Ensure redirects cannot downgrade to non-HTTPS:
  - Either configure the reqwest client in `download_file` with a redirect policy that rejects scheme changes, or after `.send()` verify `response.url().scheme() == "https"` and bail otherwise.
3. Add a clear error message explaining that only HTTPS URLs are allowed for self-update downloads.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Whole-file hash OOM ✓ Resolved 🐞 Bug ☼ Reliability
Description
The new self-update flow verifies the downloaded archive via integrity::verify_and_report, which
loads the entire archive into memory with std::fs::read. A large (or unexpectedly large) release
asset can cause high memory usage or an OOM/crash during self-update before extraction limits apply.
Code

src/cmd/self_update.rs[R288-291]

+    let expected = sums.get(&asset_name).with_context(|| {
+        format!("SHA256SUMS does not list '{asset_name}'. Refusing to install.")
+    })?;
+    integrity::verify_and_report(&archive_path, expected, &asset_name)?;
Relevance

●● Moderate

Streaming hash avoids OOM but requires nontrivial refactor; no close precedent on memory/streaming
validation.

PR-#23

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Self-update newly calls integrity::verify_and_report on the downloaded archive. That integrity
function reads the full file into memory (std::fs::read), so the memory footprint scales with
archive size and can fail before any extraction limits are enforced.

src/cmd/self_update.rs[286-295]
src/core/integrity.rs[5-33]
src/cmd/self_update.rs[301-319]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Checksum verification uses `std::fs::read` to load the entire file into RAM, which is avoidable and can crash the CLI for large artifacts.

## Issue Context
This PR introduces a new caller (`self_update`) that verifies a downloaded GitHub release archive with `integrity::verify_and_report`. The extraction step caps *uncompressed* size, but checksum verification happens earlier and reads the *compressed* archive fully into memory.

## Fix Focus Areas
- src/core/integrity.rs[5-33]
- src/cmd/self_update.rs[286-296]
- src/cmd/self_update.rs[301-319]

## What to change
1. Update integrity verification to hash files incrementally:
  - Replace `std::fs::read(path)?` usage with a streaming `File`/`BufReader` loop that feeds `Sha256::update()` in chunks.
  - Update `verify_and_report` to compute the "actual" hash via the same streaming method (avoid a second full-file read).
2. (Optional but recommended) add a size guard for self-update artifacts:
  - After download, check `std::fs::metadata(&archive_path)?.len()` against a reasonable upper bound and bail with context.
3. Consider also guarding the `std::fs::read(&bin)` that loads the extracted executable into a `Vec<u8>` (even if expected to be small).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 23 rules
✅ REVIEW.md

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread src/cmd/self_update.rs
Comment thread src/cmd/self_update.rs
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo Fixer

✅ Committed (2) · ☑ Fixed (2)

Grey Divider

Commits pushed directly to this PR — no separate fix PR opened.

Process — 2 fixed
  • ☑ Fixed: HTTPS not enforced
  • ☑ Fixed: Whole-file hash OOM

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 6, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6bf16dacf3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cmd/self_update.rs
Stage permissions on the temp binary before persist so a mode failure
cannot leave dest non-executable. Restore CodeRabbit self-update
hardening that was clobbered by an autofix pass, keep streaming
checksums and HTTPS redirect refusal from download/integrity.

Co-authored-by: Anthony Thompson <github@trackdub.com>
Co-authored-by: Anthony Thompson <github@trackdub.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cmd/self_update.rs (1)

207-228: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Check managed installs before printing the upgrade hint.

When check is true, this return path executes before fetch_latest. The command then prints an upgrade command even when the installed version is current. Fetch and compare release metadata in check mode before reporting availability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cmd/self_update.rs` around lines 207 - 228, Update execute_with_client so
check mode fetches and compares release metadata before the
method.upgrade_hint() early return. Only print the managed-install upgrade hint
when not checking, or after confirming an update is available; preserve the
existing hint behavior for normal execution.

Source: MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cmd/self_update.rs`:
- Around line 207-219: Update execute_with_client and its caller execute to
resolve the installation root and lock state, then call
acquire_mutation_lock(root) and snapshot the lockfile before any self-update
write or replace_binary operation. Thread the required root and lock-state
values through the self-update flow while preserving the existing release-client
and version injection behavior.

In `@src/core/integrity.rs`:
- Around line 6-18: Update hash_file to attach anyhow context to both File::open
and reader.read failures, including the operation and affected path in each
message. Preserve the existing hashing behavior and Result-based error
propagation.

---

Outside diff comments:
In `@src/cmd/self_update.rs`:
- Around line 207-228: Update execute_with_client so check mode fetches and
compares release metadata before the method.upgrade_hint() early return. Only
print the managed-install upgrade hint when not checking, or after confirming an
update is available; preserve the existing hint behavior for normal execution.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2985220b-ead0-487e-ad64-cbe7d59e874e

📥 Commits

Reviewing files that changed from the base of the PR and between e7b5378 and 198d874.

📒 Files selected for processing (3)
  • src/cmd/self_update.rs
  • src/core/integrity.rs
  • src/install/download.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Greptile Review
  • GitHub Check: Test (windows-latest)
  • GitHub Check: Real-Nu acceptance (windows-latest)
  • GitHub Check: Analyze (rust)
🧰 Additional context used
📓 Path-based instructions (11)
**/*

📄 CodeRabbit inference engine (CLAUDE.md)

**/*: Use Serena's semantic, symbol-aware tools as the primary tools for reading and editing code; use built-in Read, Glob, Grep, and Edit only under the stated exceptions.
Before editing a code file, inspect its symbol overview, read the specific symbols being changed, and edit them with Serena's symbol-aware tools.
Understand the existing code before changing it and make the smallest change that satisfies the request; avoid unrelated cleanup, premature abstractions, unnecessary error handling, feature flags, and compatibility shims.
Prefer editing existing files over creating new ones, and never create Markdown or README files unless explicitly requested.
For exploratory questions, provide a 2–3 sentence recommendation with the main tradeoff and do not implement until the user agrees.
For UI or frontend changes that cannot be tested in a browser, explicitly state that browser testing was not performed rather than claiming success.
Address security issues when discovered, including injection, XSS, SQL injection, path traversal, and secret leaks.
Pause and obtain confirmation before destructive, hard-to-reverse, externally visible, or third-party-upload actions, including deleting files or branches, dropping tables, force-pushing, modifying CI/CD, posting externally, or uploading content.
When blocked, investigate the root cause instead of bypassing it with --no-verify, --force, or deletion; investigate unfamiliar files, branches, and configuration before deleting them.
Only commit when explicitly asked; do not proactively update git configuration or push changes.
Do not skip Git hooks unless explicitly asked; if a pre-commit hook fails, fix the issue, re-stage, and create a new commit rather than amending.
Stage files by name rather than using git add -A or git add ., to avoid accidentally including secrets or large binaries.
Use a HEREDOC for commit messages, and add co-author attribution only when the user explicitly requests the exact trailer.
Do not force-pus...

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php,c,cpp,h,hpp,cs,swift,kt,kts}

📄 CodeRabbit inference engine (CLAUDE.md)

Add comments only when the WHY is non-obvious; do not narrate what the code does, reference the current task, or reference the PR in comments.

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
!**/.env,!**/credentials.json,!**/*.pem

📄 CodeRabbit inference engine (CLAUDE.md)

Do not commit files that appear to contain secrets, including .env, credentials.json, and PEM files; warn before doing so even if explicitly requested.

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the Rust 2021 edition.
Use anyhow::Result with .context(...) in application code; use thiserror for library error types that callers match on.
Use clap derive macros for CLI definitions.
Use serde with serde_json or toml for serialization.
Function parameters must use &Path, not &PathBuf.
Library code must not panic; error paths should return anyhow::Result with context where appropriate.
Add or update tests for behavior changes, including relevant failure paths.
New mutating code paths must acquire the mutation lock via acquire_mutation_lock(root) and snapshot the lockfile before writes.
Lockfile, journal, and state-file JSON writes must use write_json_atomic.
numan install must write only to $NUMAN_ROOT; it must not invoke Nu or register plugins/autoloads.
Only activate and deactivate may modify Nu integration state.
Treat the lockfile as the authoritative source of truth; derived projections such as autoload state must not be authoritative.
Install payloads under versioned, content-addressed paths and never overwrite them in place.
Never overwrite foreign autoload files; respect OWNERSHIP_MARKER.
Pass plugin paths through environment variables only; do not use runtime interpolation in Nu program strings.

**/*.rs: All CI gates must pass: cargo test, cargo clippy -- -D warnings, and cargo fmt --check.
Every mutating command—including install, remove, update, gc, and future nupm import—must call acquire_mutation_lock(root).
Lockfiles, journals, and state files must use write_json_atomic; partial writes are not allowed.
Pending activation, autoload, and lifecycle journals must be stored under $NUMAN_ROOT/state/.
Module autoload identity must match all four fields: Nu executable hash, Nu version, vendor autoload directory, and managed file path; the lockfile module_activation value is authoritative.
Never overwrite foreign autoload files; respect OWNERSHIP_MARKER.
Pass paths to Nu only throu...

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run and keep cargo fmt/rustfmt clean, and ensure cargo clippy -- -D warnings passes.

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
**/*.{rs,nu}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{rs,nu}: Real-Nu acceptance tests must be marked #[ignore] and should be run when changes affect activation or nupm import; unit tests must not spawn real nu and should use injectable seams such as FakeCandidateRunner or registrars.
The nupm integration must be read-only toward NUPM_HOME, must not execute build.nu, and must not perform bidirectional synchronization.

Unit tests must use FakeCandidateRunner or injectable registrars and must not spawn a real nu process.

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Match existing naming, module layout, and documentation level in the file being edited; update AGENTS.md, docs/, or command help when structure, conventions, or user-visible behavior changes.

Tests must cover failure modes, not only successful execution.

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
**/*.{rs,md,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use the repository's established serialization and module conventions rather than introducing unrelated refactors.

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Use compile-time #[cfg(target_env)] platform detection rather than std::env::consts; LIBC is a compile-time constant.
Use clap derive macros for the CLI implementation.

Files:

  • src/install/download.rs
  • src/core/integrity.rs
  • src/cmd/self_update.rs
src/install/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Install operations must remain inert: they may write only to $NUMAN_ROOT and must not perform Nu integration.

Files:

  • src/install/download.rs
src/core/integrity.rs

📄 CodeRabbit inference engine (AGENTS.md)

Compute and verify artifact SHA256 hashes.

Files:

  • src/core/integrity.rs
🔍 Remote MCP GitHub Copilot

Additional review context

  • PR #94 has one unresolved P1 concern: managed-install --self --check returns the package-manager hint before fetching release metadata, so it does not determine whether an update is available.
  • One unresolved security concern remains: SHA-256 verification is not independently authenticating because both the archive and SHA256SUMS originate from the same GitHub Release.
  • The release workflow publishes the three supported platform archives and generates SHA256SUMS from those artifacts.
  • Earlier review findings about HTTPS enforcement, streaming checksum hashing, Windows staging, Unix permissions, and test coverage were marked resolved in PR comments.
  • PR #93’s channel detection is related but remains unmerged, so self-update’s independent detection behavior should not assume those safeguards are available.
🔀 Multi-repo context tonythethompson/QuickShell, tonythethompson/dependency-chain-substrate

Linked repositories findings

tonythethompson/QuickShell

  • GitHub release artifacts are Windows installer executables (QuickShell-Setup-*-x64.exe / *-arm64.exe), while WinGet uses separate package identifiers. This supports treating package-managed installs differently from standalone binary updates. [::tonythethompson/QuickShell::] .github/workflows/release-extension.yml:203-221, docs/release-packages.md:35-59
  • WinGet updates are submitted externally with wingetcreate; no application-level self-replacement contract is present. [::tonythethompson/QuickShell::] .github/workflows/release-extension.yml:244-368

tonythethompson/dependency-chain-substrate

  • Releases publish .nupkg packages and use dotnet tool install/update, not standalone binaries or SHA256SUMS. [::tonythethompson/dependency-chain-substrate::] .github/workflows/release.yml:80-143, scripts/install-dcs-from-release.ps1:1-42
  • Release tags follow v<semver> and are validated against the project version, but this repository provides no reusable standalone-update artifact contract. [::tonythethompson/dependency-chain-substrate::] .github/workflows/release.yml:25-48

No linked repository contains consumers of numan or its new self_update APIs.

🔇 Additional comments (9)
src/cmd/self_update.rs (7)

31-31: Restore the forced Cargo upgrade command.

cargo install numan-cli can leave an existing installation unchanged. Use cargo install --locked --force numan-cli.


243-243: Resolve the platform asset only after status checks.

release_asset_name can reject an unsupported platform before the up-to-date and check-mode returns. Move it to the apply path so --self --check can still report the version status.


373-378: Preserve the existing Unix file mode.

make_executable changes a restrictive mode such as 0o700 to 0o755. Preserve the destination mode and add only the required owner-execute bit.


386-414: Stage the Windows replacement before moving the running binary.

The code renames dest before it writes the replacement temp file. A process interruption during the write leaves no numan.exe. Write and flush the staged file first, then rename the running binary and persist the staged file. Include manual recovery guidance if rollback fails.


433-572: LGTM!


574-598: LGTM!


274-280: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Other (CWE-494): Download of Code Without Integrity Check

Reachability: External

Reachability path
● Entry
  src/cmd/self_update.rs:10
  download_file
│
▼
● Sink
  src/install/download.rs

Use an independently authenticated release manifest.

The release metadata selects both the archive and SHA256SUMS. HTTPS protects transport, but the checksum does not authenticate the archive when one release publisher can replace both files. An attacker with release-publishing access can supply a matching malicious binary that reaches replace_binary. Verify a signed manifest or provenance attestation against a trust root that is independent of the fetched release assets.

src/core/integrity.rs (1)

21-23: LGTM!

Also applies to: 38-39

src/install/download.rs (1)

39-42: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Security Misconfiguration (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: External

Reachability path
● Entry
  src/cmd/self_update.rs:10
  download_file
│
▼
● Sink
  src/install/download.rs

Parse the source URL before enforcing the no-downgrade rule.

starts_with("https://") is case-sensitive. A case-variant HTTPS URL can pass the self-update preflight but skip this final redirect check. Parse url and compare its scheme instead of matching the raw string. Add a regression test that redirects a case-variant HTTPS source to HTTP.

Comment thread src/cmd/self_update.rs Outdated
Comment thread src/core/integrity.rs
cursoragent and others added 3 commits August 6, 2026 03:15
Verify SHA256SUMS.sig with a baked-in release public key before
trusting checksums. Sign releases via scripts/sign-sha256sums.py when
NUMAN_RELEASE_SIGNING_KEY is set. Keep Unix staged permission apply.

Co-authored-by: Anthony Thompson <github@trackdub.com>
Homebrew, winget, and cargo installs still cannot self-replace, but
--check now fetches the latest release and reports whether an update
is available before printing the package-manager upgrade hint.

Co-authored-by: Anthony Thompson <github@trackdub.com>
Surface open/read failures with the affected path so integrity check
errors are actionable during self-update and package verify.

Co-authored-by: Anthony Thompson <github@trackdub.com>
@tonythethompson
tonythethompson merged commit c8d0764 into master Aug 6, 2026
21 checks passed
@tonythethompson
tonythethompson deleted the cursor/update-self-flag-689b branch August 6, 2026 23:23
@linear-code

linear-code Bot commented Aug 6, 2026

Copy link
Copy Markdown

NUM-69

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