fix(security): config 0600 + install pin/checksum (RUSH-2285) - #32
Conversation
save_config / legacy migration write ~/.linear-cli as 0700 and config.json as 0600 (API key on disk). load_config re-hardens pre-existing loose modes. install.sh pins v0.16.1 and verifies SHA-256 before install; fails closed on mismatch. Bump to 0.16.1 + CHANGELOG + README pin.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Code ReviewerVerdict: Ready to merge Build: No build needed — standalone Python script. Tests: 76/76 passed (0 failures, 0 errors, 0 skips) — full suite ran clean. 4 new
CI: 2/2 green (unittest, both pass) Changes that work well
Issues that need attentionNo issues found. Each changed hunk was reviewed against its test, and the tests exercise both the happy path (modes set) and the hardening path (pre-existing loose modes get tightened). The error paths ( One minor observation (not blocking)
Things to verify manuallyThe install script's checksum verification was tested by the author (fail-closed confirmed with a bad SHA). No additional manual verification needed — the four new tests, the 76-passing suite, and green CI cover the reported surface. Reviewed by Code Reviewer — actually ran the build and tests on this branch. |
Non-author review (agent process — prix-cloud paused per #1767)Verdict: non-author review clear for merge. Reviewer is this overnight agent process (not the PR author workflow). CI: both ScopeSecurity fix-only: config 0700/0600, install pin + SHA-256, version 0.16.1. Supersedes security half of open PR #27. Config modes — verifiedConstants and write path force private modes: Harden-on-load closes the loose-umask window:
Tests for 0600 / harden — verified (ran) def test_save_config_writes_private_directory_and_file(self):
"""API key lives in config.json — dir 0700, file 0600 (RUSH-2285)."""
...
self.assertEqual(
linear_cli.CONFIG_PATH.parent.stat().st_mode & 0o777, 0o700
)
self.assertEqual(linear_cli.CONFIG_PATH.stat().st_mode & 0o777, 0o600)
def test_load_config_hardens_preexisting_loose_modes(self):
"""Existing loose umask configs get tightened on load."""
...
self.assertEqual(cfg_dir.stat().st_mode & 0o777, 0o700)
self.assertEqual(cfg_path.stat().st_mode & 0o777, 0o600)
self.assertFalse(mode & stat.S_IRGRP)
self.assertFalse(mode & stat.S_IROTH)Local: Install pin + checksum fail-closed — verified (ran)VERSION="${LINEAR_CLI_VERSION:-v0.16.1}"
EXPECTED_SHA256="${LINEAR_CLI_SHA256:-53017551174816eabaf02a3978d138c04c5f45771f85dcf8b16876de346f9efd}"
URL="https://raw.githubusercontent.com/${REPO}/${VERSION}/linear"curl -fsSL "$URL" -o "$TMP"
if ! verify_sha256 "$TMP"; then
echo "Checksum verification failed for ${URL}" >&2
...
exit 1
fi
# Install only after checksum passes (fail closed).
mkdir -p "$(dirname "$TARGET")"
mv "$TMP" "$TARGET"Local evidence:
Version / docsCHANGELOG Security section for 0.16.1 (CHANGELOG.md:8-18). README pin to Residual notes (non-blocking)
Clear for merge under CI green + this non-author review comment. |
fix-only security — RUSH-2285 leftovers from canceled RUSH-594 / stale PR #27.
Summary
~/.linear-cli0700andconfig.json0600(API key on disk).load_configre-hardens pre-existing loose umask modes.install.shpinsv0.16.1(not floatingmain) and verifies SHA-256 before install; fails closed on mismatch.0.16.0→0.16.1+ CHANGELOG + README pin.Key Evidence
linear(CONFIG_DIR_MODE 0700, CONFIG_FILE_MODE 0600)._harden_config_permsfromload_config.install.shVERSION=v0.16.1, EXPECTED_SHA256=53017551174816eabaf02a3978d138c04c5f45771f85dcf8b16876de346f9efd.Verification (ran)
After merge
Tag
v0.16.1on the merge commit so the pinned install URL resolves.Linear: RUSH-2285
no-surface (CLI security + installer; run output above is the proof)