Skip to content

fix(security): verify release checksum and harden install download#373

Open
deane0310 wants to merge 2 commits intomainfrom
fix/install-hardening
Open

fix(security): verify release checksum and harden install download#373
deane0310 wants to merge 2 commits intomainfrom
fix/install-hardening

Conversation

@deane0310
Copy link
Copy Markdown
Collaborator

@deane0310 deane0310 commented Apr 9, 2026

Summary

  • Verify the SHA-256 of the downloaded release archive against checksums.txt shipped in the npm tarball, aborting install with a [SECURITY]-prefixed error on mismatch.
  • Harden scripts/install.js download path: allowlist initial hosts (github.com, registry.npmmirror.com), bound curl connect/total time and redirect count, and fall back across mirrors on transient failures.
  • Make scripts/install.js side-effect-free on require so scripts/install.test.js can unit-test verifyChecksum / getExpectedChecksum without triggering a real install.
  • Publish checksums.txt as a release asset from .github/workflows/release.yml and include it in the published npm package via package.json files.

Test plan

  • npm install -g @larksuite/cli succeeds end-to-end on darwin/linux/windows against a real release
  • Tampering with the downloaded archive triggers a [SECURITY] abort with the expected guidance
  • Primary source failure falls back to the npmmirror source and still verifies
  • node --test scripts/install.test.js passes locally (4 tests covering hash match / mismatch / lookup / missing entry)
  • Release workflow produces and uploads checksums.txt alongside the archives

Summary by CodeRabbit

  • Bug Fixes

    • Enforced HTTPS/TLS for installer downloads and stricter download timeouts/retries.
    • Added checksum verification to detect tampering or corruption and improved error differentiation.
  • New Features

    • CI now blocks publishing when release checksums are missing or do not match expected artifacts.
    • Package now includes the release checksums file so integrity can be verified during install.
  • Tests

    • Added tests covering checksum lookup and verification behavior.

@github-actions github-actions bot added the size/L Large or sensitive change across domains or core paths label Apr 9, 2026
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 9, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Adds checksum manifest handling and verification across release and install flows: the release workflow downloads and validates checksums.txt before npm publish; package.json includes checksums.txt; the install script now enforces HTTPS, verifies SHA‑256 checksums, improves error types and extraction, and exports verification helpers; tests added for checksum logic.

Changes

Cohort / File(s) Summary
Release & Package Configuration
.github/workflows/release.yml, package.json
Workflow: download checksums.txt from the GitHub release and abort publish-npm if missing/invalid for each platform artifact. Package: add checksums.txt to published files.
Installation Script & Exports
scripts/install.js
Replaced curl exec with execFileSync args, enforce HTTPS and TLS, add timeouts/redirect limits, load and parse checksums.txt, compute/verify SHA‑256 via streaming, introduce ChecksumError/NetworkError/PackageIntegrityError, stop retries on integrity failures, platform-specific extraction (tar / PowerShell script), always cleanup temp dir, export verification helpers and error classes.
Tests
scripts/install.test.js
New Node test suite validating verifyChecksum success/failure and getExpectedChecksum behavior for present/missing/absent manifest entries; uses temp fixtures and cleanup.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI Entry Point
    participant Install as Install Script
    participant Release as GitHub Release (checksums.txt)
    participant Source as Download Source (mirror)
    participant Verify as Checksum Verifier
    participant Extract as Archive Extractor
    participant FS as File System

    CLI->>Install: start install()
    Install->>Release: read checksums.txt (from package or release)
    Release-->>Install: checksums manifest (or error)

    loop For each source
        Install->>Source: download artifact (HTTPS, curl)
        Source-->>Install: binary data or NetworkError
        alt NetworkError
            Install->>Install: try next source
        else success
            Install->>FS: write temp file
            Install->>Verify: verifyChecksum(file, expectedHash)
            Verify->>FS: stream & compute SHA-256
            FS-->>Verify: digest
            alt Checksum matches
                Verify-->>Install: ok
                Install->>Extract: extract archive (tar / PowerShell)
                Extract->>FS: place binaries
                FS-->>Extract: done
                Extract-->>Install: success
            else Checksum mismatch
                Verify-->>Install: throw ChecksumError
                Install->>Install: abort (no further retries)
            end
        end
    end

    Install->>FS: cleanup temp
    FS-->>Install: cleaned
    Install-->>CLI: exit status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

size/M

Suggested reviewers

  • liangshuo-1

Poem

🐰 I hopped through bytes and hashed each crumb,
checksums snug where the binaries come,
I guard the hops, I guard the trail,
no broken bits shall ever prevail,
a rabbit seal on every download drum. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main security-focused changes: verifying release checksums and hardening the install download process.
Description check ✅ Passed The PR description covers all required template sections with substantial detail: Summary (4 bullets explaining key changes), Changes implicitly listed in the summary, Test Plan (5 items with checkboxes), and Related Issues marked as None.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/install-hardening

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@896e72d50dadefcb2f2ab664d6a116b947d697b4

🧩 Skill update

npx skills add larksuite/cli#fix/install-hardening -y -g

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 9, 2026

Greptile Summary

This PR hardens the npm install script for @larksuite/cli by adding SHA-256 checksum verification against a checksums.txt bundled in the npm package, switching from shell interpolation to execFileSync with an args array, adding URL allowlisting and curl transport hardening, and making the module side-effect-free for testability. The .goreleaser.yml already configures name_template: checksums.txt, so the fetch step in the release workflow will resolve correctly.

Confidence Score: 5/5

Safe to merge — all prior P0/P1 concerns are addressed and no new blocking issues found.

Both previously flagged issues (wrong error type for missing checksums.txt, and weak filename-only check in the release workflow) are fully resolved. The implementation is clean: no shell injection, defense-in-depth URL and TLS enforcement, stream-based SHA-256 verification before extraction, proper error class routing, side-effect-free exports, and four targeted tests. No P0 or P1 findings remain.

No files require special attention.

Vulnerabilities

  • Shell injection eliminated: execSync with template-string URLs replaced by execFileSync with a plain args array — no shell expansion.
  • URL allowlisting (ALLOWED_INITIAL_HOSTS) and --proto =https / --proto-redir =https together enforce HTTPS for both initial and redirect hops.
  • Stream-based SHA-256 verification before extraction ensures a tampered archive (from either source) is rejected with a [SECURITY]-prefixed error.
  • The trust chain (npm maintainer → npm package → checksums.txt → binary) is sound: an attacker who can replace the npm package can replace everything anyway, so no additional attack surface is introduced.
  • $ErrorActionPreference = 'Stop' and -LiteralPath in the PowerShell extraction script prevent silent failures and path-injection respectively.

Important Files Changed

Filename Overview
scripts/install.js Major refactor: adds error class hierarchy, URL allowlisting, curl transport hardening via execFileSync args array, stream-based SHA-256 checksum verification, and side-effect-free module export. Logic is sound and well-commented.
scripts/install.test.js New test file covering hash match, hash mismatch, entry lookup, missing file (PackageIntegrityError), and missing entry (ChecksumError) — all cases exercised correctly with proper teardown.
.github/workflows/release.yml Adds fetch + regex-verified validation of checksums.txt from the GitHub Release before npm publish. The goreleaser config already sets name_template: checksums.txt, so the download pattern resolves correctly.
package.json Adds checksums.txt to the files array so it is bundled in the published npm tarball, enabling offline checksum verification at install time.

Sequence Diagram

sequenceDiagram
    participant npm as npm install
    participant install as install.js
    participant cs as checksums.txt (bundled)
    participant gh as github.com (primary)
    participant mirror as registry.npmmirror.com (fallback)

    npm->>install: node scripts/install.js
    install->>cs: getExpectedChecksum(archiveName)
    alt checksums.txt missing
        cs-->>install: PackageIntegrityError
        install-->>npm: "package looks broken"
    else entry missing
        cs-->>install: ChecksumError
        install-->>npm: [SECURITY] abort
    else found
        cs-->>install: expectedHash (SHA-256)
    end

    install->>install: validate URL (HTTPS + allowlist)
    install->>gh: curl --proto =https --proto-redir =https ... (download archive)
    alt primary succeeds
        gh-->>install: archive bytes
    else NetworkError
        install->>mirror: curl (same flags, fallback)
        alt fallback succeeds
            mirror-->>install: archive bytes
        else NetworkError
            mirror-->>install: NetworkError
            install-->>npm: "all download sources failed"
        end
    end

    install->>install: verifyChecksum(archivePath, expectedHash)
    alt SHA-256 mismatch
        install-->>npm: [SECURITY] integrity check failure
    else match
        install->>install: extract + chmod 0o755
        install-->>npm: "installed successfully"
    end
Loading

Reviews (2): Last reviewed commit: "fix(install): harden checksum verificati..." | Re-trigger Greptile

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.

🧹 Nitpick comments (1)
scripts/install.test.js (1)

4-14: Minor style inconsistency in imports.

Lines 4-5 use the node: protocol prefix while lines 6-9 use bare module names. This is purely cosmetic but consider aligning for consistency.

♻️ Suggested alignment
 const { test } = require("node:test");
 const assert = require("node:assert");
-const fs = require("fs");
-const os = require("os");
-const path = require("path");
-const crypto = require("crypto");
+const fs = require("node:fs");
+const os = require("node:os");
+const path = require("node:path");
+const crypto = require("node:crypto");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/install.test.js` around lines 4 - 14, The imports in
scripts/install.test.js mix the "node:" protocol and bare module names;
standardize them for consistency by either prefixing all built-ins with "node:"
(e.g., node:fs, node:os, node:path, node:crypto) or removing the "node:" prefix
from test/assert to match the others—apply the change where require is used and
ensure references to verifyChecksum, getExpectedChecksum, and ChecksumError
remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/install.test.js`:
- Around line 4-14: The imports in scripts/install.test.js mix the "node:"
protocol and bare module names; standardize them for consistency by either
prefixing all built-ins with "node:" (e.g., node:fs, node:os, node:path,
node:crypto) or removing the "node:" prefix from test/assert to match the
others—apply the change where require is used and ensure references to
verifyChecksum, getExpectedChecksum, and ChecksumError remain unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6fdaed1d-dd6e-4223-b32d-4c35369fc89e

📥 Commits

Reviewing files that changed from the base of the PR and between 619ec8c and 066e701.

📒 Files selected for processing (4)
  • .github/workflows/release.yml
  • package.json
  • scripts/install.js
  • scripts/install.test.js

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 61-71: The grep pattern interpolates ${VERSION} directly (in the
loop that iterates over plat and checks checksums.txt) so dots and other regex
metacharacters in VERSION can alter matching; update the check to perform an
exact filename match by either escaping regex metacharacters in VERSION before
use or switching to a literal match approach (e.g., use grep -F / fixed-string
or use awk to compare the second field exactly to the constructed filename
"lark-cli-${VERSION}-${plat}") so malformed versions cannot bypass checksum
validation.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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: c62efff4-b182-474f-bee3-f8fe8e1382a0

📥 Commits

Reviewing files that changed from the base of the PR and between 066e701 and 896e72d.

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 9, 2026

Tip:

Greploop — Automatically fix all review issues by running /greploops in Claude Code. It iterates: fix, push, re-review, repeat until 5/5 confidence.

Use the Greptile plugin for Claude Code to query reviews, search comments, and manage custom context directly from your terminal.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

An unexpected error occurred while generating fixes: Resource not accessible by integration - https://docs.github.com/rest/git/trees#create-a-tree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants