Skip to content

feat: add SHA-256 checksum verification to install.js#592

Open
MaxHuang22 wants to merge 6 commits intolarksuite:mainfrom
MaxHuang22:feat/install-checksum-verification
Open

feat: add SHA-256 checksum verification to install.js#592
MaxHuang22 wants to merge 6 commits intolarksuite:mainfrom
MaxHuang22:feat/install-checksum-verification

Conversation

@MaxHuang22
Copy link
Copy Markdown
Collaborator

@MaxHuang22 MaxHuang22 commented Apr 21, 2026

Summary

Downloaded binary archives in scripts/install.js were not verified against any checksum, leaving the install path vulnerable to CDN tampering, MITM, or corruption. This PR bundles GoReleaser's checksums.txt in the npm package and verifies the SHA-256 of every downloaded archive before extraction.

Changes

  • Restructure scripts/install.js to be side-effect-free on require() via require.main === module guard
  • Add getExpectedChecksum(archiveName, checksumsDir?) to parse GoReleaser checksums.txt format
  • Add verifyChecksum(archivePath, expectedHash) with [SECURITY]-prefixed error on mismatch
  • Add assertAllowedHost(url) host allowlist (github.com, objects.githubusercontent.com, registry.npmmirror.com) and --max-redirs 3 to curl args in download()
  • Integrate checksum verification into install() flow (after download, before extraction)
  • Add checksums.txt to package.json files array
  • Add Download checksums from release step to .github/workflows/release.yml publish-npm job
  • Create scripts/install.test.js with 7 unit tests for exported functions

Test Plan

  • make unit-test passed
  • make validate passed (build, vet, unit test, integration test)
  • skipped: local-eval E2E (pure JavaScript change, no Go CLI commands affected)
  • skipped: local-eval skillave (no shortcut/skill changes)
  • acceptance-reviewer passed (5/5 cases)
  • manual verification: node --test scripts/install.test.js (7/7 pass), node -e "require('./scripts/install.js')" (side-effect-free)

Related Issues

N/A

Summary by CodeRabbit

  • Security
    • Restricted allowed download hosts and enforced checksum verification for installers.
  • Chores
    • CI now fetches checksum manifest for releases and fails publishing if it's missing; checksum file is included in published package.
  • Tests
    • Added tests covering checksum lookup and verification behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 73cf43de-be3d-4a87-8359-278a642fc7b7

📥 Commits

Reviewing files that changed from the base of the PR and between 537967b and 2a19f0c.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

📝 Walkthrough

Walkthrough

Adds SHA-256 checksum handling for published CLI binaries: release workflow downloads checksums.txt, package.json includes it for publish, scripts/install.js verifies archive checksums and restricts hosts, and tests for checksum helpers were added.

Changes

Cohort / File(s) Summary
Release Workflow
.github/workflows/release.yml
Adds an authenticated step in publish-npm to download checksums.txt from the GitHub release for the current tag and fail if missing/empty.
Package Configuration
package.json
Adds checksums.txt to the files array so it is included in the published npm package.
Installation Script
scripts/install.js
Introduces host allowlist and assertAllowedHost, adds SHA-256 checksum lookup (getExpectedChecksum) and verification (verifyChecksum) before extraction, limits curl redirects, moves side-effectful operations behind require.main === module, exposes checksum helpers, and defers bin dir creation into install().
Installation Tests
scripts/install.test.js
New tests for getExpectedChecksum and verifyChecksum covering extraction, selection, missing checksums, matching (case-insensitive), and mismatch error behavior.

Sequence Diagram

sequenceDiagram
    participant CI as CI (Release)
    participant GH as GitHub Releases
    participant NPM as npm Registry
    participant User as User Environment
    participant Install as install.js
    participant Curl as curl
    participant FS as File System

    CI->>GH: Authenticate + download `checksums.txt` for tag
    CI->>NPM: Publish package (includes `checksums.txt`)

    User->>NPM: npm install `@larksuite/cli`
    NPM-->>User: Package (with `checksums.txt`)
    User->>Install: Run postinstall (install.js)

    Install->>Install: assertAllowedHost(downloadUrl)
    Install->>Curl: Download archive (--max-redirs 3)
    Curl-->>Install: Archive file
    Install->>FS: Read `checksums.txt`
    Install->>Install: compute SHA-256(archive)
    Install->>Install: compare with expected hash

    alt Match
        Install->>FS: extract archive to bin
        Install-->>User: installation success
    else Mismatch or missing entry
        Install-->>User: throw `[SECURITY] Checksum mismatch` / error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • liangshuo-1

Poem

🐰 I hop through CI fields so bright,
I fetch the checksums, guard the night,
I sniff the hosts and tally the hash,
No sneaky archive can make me crash,
Hooray—safe installs, hop with delight! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding SHA-256 checksum verification to install.js. It is concise, clear, and directly reflects the primary objective of this PR.
Description check ✅ Passed The PR description comprehensively covers all required template sections: a clear summary of motivation, detailed change list, thorough test plan with checkmarks, and related issues.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actions github-actions bot added the size/M Single-domain feat or fix with limited business impact label Apr 21, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.69%. Comparing base (fbed6be) to head (2a19f0c).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #592      +/-   ##
==========================================
+ Coverage   60.92%   61.69%   +0.76%     
==========================================
  Files         399      402       +3     
  Lines       34089    35073     +984     
==========================================
+ Hits        20770    21639     +869     
- Misses      11395    11429      +34     
- Partials     1924     2005      +81     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 21, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@2a19f0c23120d34ac19f681a66ee62a20788a1be

🧩 Skill update

npx skills add MaxHuang22/cli#feat/install-checksum-verification -y -g

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
scripts/install.js (1)

42-61: ⚠️ Potential issue | 🟠 Major

Validate redirect targets against the host allowlist, or disable automatic redirects.

The assertAllowedHost() check at line 50 only validates the initial URL. However, curl --location will follow Location headers to other hosts without validation. While --max-redirs 3 limits the redirect count, curl has no built-in option to enforce host allowlisting on redirects (--proto-redir controls protocol types, not hosts). Either validate each redirect target or use --location-trusted with explicit protocol constraints, or remove --location to reject redirects.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/install.js` around lines 42 - 61, The current download function uses
curl with "--location" so redirects are followed without re-checking hosts;
update the download(url, destPath) implementation to remove the "--location"
flag from args and implement manual redirect handling: perform the request, on
3xx responses read the Location header, call assertAllowedHost(newUrl) before
following, enforce the existing "--max-redirs" limit (or the args' max redirs
value) and other timeout options, and continue following only when the
redirected host is allowed; preserve the isWindows check that adds
"--ssl-revoke-best-effort" and keep using execFileSync("curl", ...) for each
request/redirect step.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/plans/2026-04-21-install-checksum.md`:
- Around line 150-152: The fenced expected-output block containing [
'getExpectedChecksum', 'verifyChecksum' ] should be labeled as a text fence for
markdown linting; update the code fence that wraps getExpectedChecksum and
verifyChecksum so it begins with ```text and ends with ``` to mark it as plain
text output.

In `@docs/superpowers/specs/2026-04-21-install-checksum-design.md`:
- Around line 7-21: The fenced code blocks in the spec (the trust-chain diagram
starting with "GoReleaser (CI) generates dist/checksums.txt", the install flow
block containing "download archive (github, fallback mirror) -> expectedHash =
getExpectedChecksum(archiveName)", and the test-case outline starting with
"getExpectedChecksum - returns correct hash...") are unlabeled and trigger
markdownlint; add the language label "text" to each triple-backtick fence for
those blocks (and the other unlabeled fences noted around the similar sections)
so they read ```text ... ``` to silence the linter while preserving content.

In `@scripts/install.js`:
- Around line 106-110: The installer currently returns null when checksums.txt
is missing (the fs.existsSync(checksumsPath) branch), allowing installs to
proceed unverified; change this to fail-closed by throwing an Error or exiting
non-zero with a clear message when checksumsPath is missing, and add an explicit
opt-out (e.g., read an env var like SKIP_CHECKSUMS=true or a CLI flag) so
callers of the verification logic (the checksum verification flow that expects
the checksums content) can opt out for local dev; update the log text to reflect
the required opt-out variable and ensure callers no longer treat null as "skip"
but instead propagate the error.

---

Outside diff comments:
In `@scripts/install.js`:
- Around line 42-61: The current download function uses curl with "--location"
so redirects are followed without re-checking hosts; update the download(url,
destPath) implementation to remove the "--location" flag from args and implement
manual redirect handling: perform the request, on 3xx responses read the
Location header, call assertAllowedHost(newUrl) before following, enforce the
existing "--max-redirs" limit (or the args' max redirs value) and other timeout
options, and continue following only when the redirected host is allowed;
preserve the isWindows check that adds "--ssl-revoke-best-effort" and keep using
execFileSync("curl", ...) for each request/redirect step.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ccca54a-0a7d-40a9-b9dd-c580b973148e

📥 Commits

Reviewing files that changed from the base of the PR and between 04e3a28 and 33f4b8d.

📒 Files selected for processing (6)
  • .github/workflows/release.yml
  • docs/superpowers/plans/2026-04-21-install-checksum.md
  • docs/superpowers/specs/2026-04-21-install-checksum-design.md
  • package.json
  • scripts/install.js
  • scripts/install.test.js

Comment thread docs/superpowers/plans/2026-04-21-install-checksum.md Outdated
Comment thread docs/superpowers/specs/2026-04-21-install-checksum-design.md Outdated
Comment thread scripts/install.js
Change-Id: I5444e3f34642d7c0740b6422a70ca6921a85e363
Change-Id: I87548be25d30c384e743da17b1d161b9d9f0ea87
Change-Id: Ifc2067bf1b824b02257dba7b53716fbe18d0f6b6
Change-Id: I2580782866049f1f62a2597e86b7bf59d0e50925
Change-Id: I2d7c44d9d5b9075158f63c0f8cf66c1e0abe3d8d
@MaxHuang22 MaxHuang22 force-pushed the feat/install-checksum-verification branch from 33f4b8d to 537967b Compare April 21, 2026 13:05
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 48-53: The release workflow step that downloads checksums is
fragile: it derives the tag from package.json and doesn't fail if checksums.txt
is missing, which can silently disable verification in getExpectedChecksum
(scripts/install.js). Update the GitHub Action to use the triggering tag
(github.ref_name) instead of reading package.json, and add an explicit check
after gh release download to fail the job if checksums.txt is not present (exit
nonzero and log a clear error). Optionally add a follow-up step that runs the
install verification (e.g., invoke the verification path in scripts/install.js
or a dry-run installation) to ensure checksums.txt actually validates artifacts
before publishing.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3a8a3355-5498-49d6-8fee-b9c11af93389

📥 Commits

Reviewing files that changed from the base of the PR and between 33f4b8d and 537967b.

📒 Files selected for processing (4)
  • .github/workflows/release.yml
  • package.json
  • scripts/install.js
  • scripts/install.test.js
✅ Files skipped from review due to trivial changes (2)
  • package.json
  • scripts/install.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/install.js

Comment thread .github/workflows/release.yml Outdated
…orkflow

Address CodeRabbit review: use GITHUB_REF_NAME instead of parsing
package.json to avoid version drift, and add explicit file check to
fail loudly if checksums.txt is missing or empty.

Change-Id: I8a5658412b6afc338ad2a642baba146cceafd0fc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant