Skip to content

Commit 03889e7

Browse files
committed
fix: out-of-band checksum integrity for install.sh (closes #36)
install.sh now fetches CHECKSUMS.sha256 from the GitHub release API instead of the repo tree, preventing a single compromised commit from swapping both code and checksums. Hard fail on verification failure with TOKEN_OPTIMIZER_SKIP_VERIFY=1 escape hatch for air-gapped installs. Also: pin CLA action to SHA, add Node 24 compat, expand checksum scope to cover install.sh and hooks/hooks.json, bump to v5.7.2.
1 parent f94c84c commit 03889e7

9 files changed

Lines changed: 153 additions & 33 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"name": "token-optimizer",
1414
"source": "./",
1515
"description": "Audit, fix, and monitor Claude Code context window usage. Find the ghost tokens.",
16-
"version": "5.7.1",
16+
"version": "5.7.2",
1717
"author": {
1818
"name": "Alex Greenshpun",
1919
"url": "https://linkedin.com/in/alexgreensh"

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
},
88
"homepage": "https://github.com/alexgreensh/token-optimizer",
99
"repository": "https://github.com/alexgreensh/token-optimizer",
10-
"version": "5.7.1",
10+
"version": "5.7.2",
1111
"license": "PolyForm-Noncommercial-1.0.0",
1212
"keywords": ["token", "optimization", "context", "audit", "cost", "coach"]
1313
}

.codex-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "token-optimizer",
3-
"version": "5.7.1",
3+
"version": "5.7.2",
44
"description": "Audit, monitor, and reduce Codex context waste with continuity helpers and explicit outline tools.",
55
"skills": "./skills/",
66
"interface": {

.github/workflows/cla.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ permissions:
1414
jobs:
1515
CLAAssistant:
1616
runs-on: ubuntu-latest
17+
env:
18+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
1719
steps:
1820
- name: "CLA Assistant"
1921
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
20-
uses: contributor-assistant/github-action@v2.6.1
22+
# Pinned to v2.6.1 SHA for supply-chain safety
23+
uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08
2124
env:
2225
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2326
with:

CHECKSUMS.sha256

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
20255099ff8c12467d4e88e038431f9ff59071fef90c59c51c7bee777066a488 hooks/hooks.json
2+
2f53504a8f493112c8b318f50cf192e94f358a96d6e3b0fb76cbd258d13d02fc install.sh
13
b1ccc6e69e2632966bc474db62027579966bba36e9fd33aed75233b71c921bcf skills/token-optimizer/scripts/activity_tracker.py
24
552d55795c86875618509db9cb1b075fcbaff7fcb6b174ab99550d858371b5d5 skills/token-optimizer/scripts/archive_result.py
35
92fd8040bef69da2846b3622976afab408c7dc4b5f40d35b09f0d294d2d35b72 skills/token-optimizer/scripts/bash_compress.py
46
de4efe4d7fb810fa7ad5caad9daf65fe8241a27df241df138cde0600185c5a61 skills/token-optimizer/scripts/bash_hook.py
57
e70da0fe35a36cdc657889419eac2f0aed48dd167f5085ce1b29b7c774090488 skills/token-optimizer/scripts/benchmark.py
68
507e97f069736d49a20e77052916d19ca512a747cdf63827ca5bfdeffe45deb1 skills/token-optimizer/scripts/codex_compact_prompt.py
7-
959bf75853003d7c5615a287dad5aec5b1e67f7cc9ac943057cd83f1465b19d1 skills/token-optimizer/scripts/codex_doctor.py
9+
a1c9c14604e6c8a0e86eb64de61de67862283f1d8dc7f7e43c9b2a3b2d907efd skills/token-optimizer/scripts/codex_doctor.py
810
b113cc3a01527d6aeb1c259e5a293481c53cda1f4ffe612df7f2933b6c777d8c skills/token-optimizer/scripts/codex_hook_bridge.py
9-
2dd9eab1b7333409edb0ffaca7e70762f32412be7c0e2614d2e4f2992572069b skills/token-optimizer/scripts/codex_install.py
11+
33dfb5ca621d0a874f9869c4d51cca7bc2d3a44934ab4159be08203ef6244c53 skills/token-optimizer/scripts/codex_install.py
1012
a137b8bf14ee275ae3988df9e00ae7feee39313046a75ac5d743eb8f06e9fabf skills/token-optimizer/scripts/codex_io.py
1113
7e7163df683e6cf96d4db9a7359c4f737206f0f9f5b2a79687a018090b46bbc5 skills/token-optimizer/scripts/codex_session.py
1214
d67d6a53f65485127c458d60804550763e305769a0a3e842f9a5370864692252 skills/token-optimizer/scripts/codex_statusline.py
@@ -27,7 +29,7 @@ b8345d767a48f40d275480973681baf56dc76329d8f3eaa6260966d6d6b787cd skills/token-o
2729
fef4a2385caf3c9495987007d16edf73a022cddbd0aecf2293025e3d9599996d skills/token-optimizer/scripts/detectors/websearch_routing.py
2830
6f023687c905db9be267aafcf2a85f08739bee8c0aae42b58eeae4b8889690b1 skills/token-optimizer/scripts/hook_io.py
2931
f647eabc7067499b74f72c4ba5c55738fdc18c6539a7a5b99c65d03126016d8d skills/token-optimizer/scripts/injection.py
30-
e72fad8482a67442a0a874be8950a4c47cfd317f56e1978226c3350451c6100d skills/token-optimizer/scripts/measure.py
32+
b6e8ccaf090a6c1205c4a6552f2ebc01caa20c58053cb41a1d8cae070326c0b7 skills/token-optimizer/scripts/measure.py
3133
bd27835f85c5fd45c9cf67cb8b969038489249c56182ec2638a4f1223799b98f skills/token-optimizer/scripts/outline.py
3234
7e996f6c5d3c36ea0f0d70b8eec18080d0582c0155790637ac34abbd0b3e5057 skills/token-optimizer/scripts/plugin_env.py
3335
8f4b3675f890f0fbd2412faa76f61c048fc59cd152de521236e982a21fdceade skills/token-optimizer/scripts/read_cache.py

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
</p>
44

55
<p align="center">
6-
<a href="https://github.com/alexgreensh/token-optimizer/releases"><img src="https://img.shields.io/badge/version-5.7.0-green" alt="Version 5.6.13"></a>
6+
<a href="https://github.com/alexgreensh/token-optimizer/releases"><img src="https://img.shields.io/badge/version-5.7.2-green" alt="Version 5.7.2"></a>
77
<a href="https://github.com/alexgreensh/token-optimizer/releases"><img src="https://img.shields.io/github/release-date/alexgreensh/token-optimizer?label=last%20release&color=blue" alt="Last Release"></a>
88
<a href="https://github.com/alexgreensh/token-optimizer"><img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet" alt="Claude Code Plugin"></a>
99
<a href="https://github.com/alexgreensh/token-optimizer/tree/main/openclaw"><img src="https://img.shields.io/badge/OpenClaw-v2.4.1-brightgreen" alt="OpenClaw v2.4.1"></a>
@@ -94,6 +94,8 @@ git clone https://github.com/alexgreensh/token-optimizer.git ~/.claude/token-opt
9494
bash ~/.claude/token-optimizer/install.sh
9595
```
9696

97+
The installer verifies file integrity against checksums from the latest GitHub release. If you're offline or behind a restrictive proxy, set `TOKEN_OPTIMIZER_SKIP_VERIFY=1` before running.
98+
9799
Works on Claude Code, [OpenCode](#opencode), and [OpenClaw](#openclaw). Each platform has its own native plugin (Python for Claude Code, TypeScript for OpenCode and OpenClaw). No bridging, no shared runtime, zero cross-platform dependencies.
98100

99101
</details>
@@ -293,6 +295,20 @@ Claude Code and OpenClaw today, with native plugins for each. Codex support is i
293295
Windsurf and Cursor are next on the roadmap. Full Codex parity is waiting on upstream hook/cache surfaces for invisible read substitution, structure-map substitution, and compact lifecycle hooks.
294296
</details>
295297

298+
<details>
299+
<summary>🔐 <strong>How does install.sh verify file integrity?</strong></summary>
300+
301+
The installer fetches `CHECKSUMS.sha256` from the latest GitHub Release (not from the repo tree), then verifies every script file against those checksums. This out-of-band verification means a compromised commit cannot swap both the code and the checksums simultaneously. If the checksum fetch fails or any file fails verification, the installer exits with a non-zero status and does not continue. You can also verify manually:
302+
303+
```bash
304+
# Download checksums from the latest release
305+
curl -sL $(gh release view --json assets -q '.assets[] | select(.name=="CHECKSUMS.sha256") | .url') -o /tmp/checksums.sha256
306+
307+
# Verify your install
308+
cd ~/.claude/token-optimizer && shasum -a 256 -c /tmp/checksums.sha256
309+
```
310+
</details>
311+
296312
---
297313

298314
## Why install this first

install.sh

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818

1919
set -euo pipefail
2020

21-
REPO_HTTPS="https://github.com/alexgreensh/token-optimizer.git"
22-
REPO_SSH="git@github.com:alexgreensh/token-optimizer.git"
21+
GITHUB_REPO="alexgreensh/token-optimizer"
22+
REPO_HTTPS="https://github.com/${GITHUB_REPO}.git"
23+
REPO_SSH="git@github.com:${GITHUB_REPO}.git"
2324
INSTALL_DIR="${HOME}/.claude/token-optimizer"
2425
SKILL_DIR="${HOME}/.claude/skills"
2526

@@ -63,6 +64,11 @@ if ! command -v git &>/dev/null; then
6364
fi
6465
info "git OK"
6566

67+
# curl (needed for out-of-band checksum verification)
68+
if ! command -v curl &>/dev/null; then
69+
fail "curl not found. Install curl first."
70+
fi
71+
6672
# Claude Code directory
6773
if [ ! -d "${HOME}/.claude" ]; then
6874
fail "~/.claude/ not found. Install Claude Code first: https://claude.ai/download"
@@ -152,29 +158,56 @@ else
152158
fi
153159

154160
# ── Integrity Verification ────────────────────────────────────
155-
# Checksums verify file integrity post-clone/pull.
156-
# For full supply-chain security, pin to a specific commit SHA in your
157-
# deployment pipeline instead of relying on HEAD.
158-
# This check is advisory: it warns but does not block, because the
159-
# CHECKSUMS.sha256 file itself is updated with each code change.
160-
161-
CHECKSUM_FILE="${INSTALL_DIR}/CHECKSUMS.sha256"
162-
if [ -f "$CHECKSUM_FILE" ]; then
163-
info "Verifying file integrity..."
164-
(
165-
cd "$INSTALL_DIR" || exit 1
166-
if sha256sum -c "$CHECKSUM_FILE" --quiet 2>/dev/null || \
167-
shasum -a 256 -c "$CHECKSUM_FILE" --quiet 2>/dev/null; then
168-
printf "${GREEN}>${NC} Integrity check passed\n"
169-
else
170-
printf "${YELLOW}!${NC} WARNING: Integrity check failed or checksums are outdated.\n"
171-
printf "${YELLOW}!${NC} This is expected immediately after a code update (checksums\n"
172-
printf "${YELLOW}!${NC} are regenerated with each release). If you did not just\n"
173-
printf "${YELLOW}!${NC} update, verify your clone manually: cd ${INSTALL_DIR} && git log --oneline -5\n"
174-
fi
175-
)
161+
# Checksums are fetched from the GitHub release (out-of-band), NOT from
162+
# the repo tree. This prevents a single compromised commit from swapping
163+
# both code and checksums simultaneously.
164+
# Set TOKEN_OPTIMIZER_SKIP_VERIFY=1 to bypass (air-gapped installs).
165+
166+
CHECKSUM_FILE="/tmp/token-optimizer-checksums-$$.sha256"
167+
trap 'rm -f "$CHECKSUM_FILE"' EXIT
168+
169+
fetch_release_checksums() {
170+
local asset_url
171+
asset_url=$(curl -fsSL \
172+
"https://api.github.com/repos/${GITHUB_REPO}/releases/latest" \
173+
2>/dev/null \
174+
| python3 -c '
175+
import json, sys
176+
try:
177+
data = json.load(sys.stdin)
178+
for a in data.get("assets", []):
179+
if a["name"] == "CHECKSUMS.sha256":
180+
print(a["browser_download_url"])
181+
break
182+
except Exception:
183+
pass
184+
' 2>/dev/null)
185+
186+
if [ -z "$asset_url" ]; then
187+
return 1
188+
fi
189+
190+
curl -fsSL -o "$CHECKSUM_FILE" "$asset_url" 2>/dev/null && [ -s "$CHECKSUM_FILE" ]
191+
}
192+
193+
if [ "${TOKEN_OPTIMIZER_SKIP_VERIFY:-}" = "1" ]; then
194+
warn "Skipping integrity verification (TOKEN_OPTIMIZER_SKIP_VERIFY=1)"
176195
else
177-
warn "CHECKSUMS.sha256 not found, skipping integrity check."
196+
info "Fetching checksums from GitHub release..."
197+
if fetch_release_checksums; then
198+
info "Verifying file integrity (out-of-band checksums)..."
199+
(
200+
cd "$INSTALL_DIR" || exit 1
201+
if sha256sum -c "$CHECKSUM_FILE" --quiet 2>/dev/null || \
202+
shasum -a 256 -c "$CHECKSUM_FILE" --quiet 2>/dev/null; then
203+
printf "${GREEN}>${NC} Integrity check passed\n"
204+
else
205+
fail "Integrity check FAILED. Files do not match release checksums. Your install may be compromised. Re-clone from: https://github.com/${GITHUB_REPO}"
206+
fi
207+
)
208+
else
209+
fail "Could not fetch checksums from GitHub release. Cannot verify file integrity. Check network connectivity or install manually from: https://github.com/${GITHUB_REPO}/releases"
210+
fi
178211
fi
179212

180213
# Log the current commit SHA so users can audit which version is installed.

scripts/sign-release.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/bash
2+
# Generates CHECKSUMS.sha256 and attaches it to a GitHub release.
3+
#
4+
# Usage:
5+
# scripts/sign-release.sh v5.7.2
6+
#
7+
# Prerequisites: gh CLI authenticated, tag already pushed.
8+
#
9+
# Copyright (C) 2026 Alex Greenshpun
10+
# SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
11+
12+
set -euo pipefail
13+
14+
TAG="${1:-}"
15+
16+
if [ -z "$TAG" ]; then
17+
echo "Usage: scripts/sign-release.sh <tag>"
18+
echo "Example: scripts/sign-release.sh v5.7.2"
19+
exit 1
20+
fi
21+
22+
if ! command -v gh &>/dev/null; then
23+
echo "Error: gh CLI not found. Install: https://cli.github.com"
24+
exit 1
25+
fi
26+
27+
if ! gh release view "$TAG" &>/dev/null; then
28+
echo "Error: release $TAG not found. Create it first:"
29+
echo " git tag $TAG && git push origin $TAG"
30+
echo " gh release create $TAG --title \"$TAG\" --generate-notes"
31+
exit 1
32+
fi
33+
34+
REPO_ROOT="$(git rev-parse --show-toplevel)"
35+
CHECKSUM_FILE="${REPO_ROOT}/CHECKSUMS.sha256"
36+
37+
echo "Generating checksums for tracked scripts..."
38+
39+
HASH_CMD="sha256sum"
40+
if [ "$(uname)" = "Darwin" ]; then
41+
HASH_CMD="shasum -a 256"
42+
fi
43+
44+
{
45+
echo "${REPO_ROOT}/install.sh"
46+
echo "${REPO_ROOT}/hooks/hooks.json"
47+
find "${REPO_ROOT}/skills/token-optimizer/scripts" -type f -name "*.py"
48+
} | sort | while read -r f; do
49+
[ -f "$f" ] || continue
50+
$HASH_CMD "$f" | sed "s|${REPO_ROOT}/||"
51+
done > "$CHECKSUM_FILE"
52+
53+
CHECKSUM_COUNT=$(wc -l < "$CHECKSUM_FILE" | tr -d ' ')
54+
echo "Generated ${CHECKSUM_COUNT} checksums"
55+
56+
if [ "$CHECKSUM_COUNT" -eq 0 ]; then
57+
echo "Error: no files found to checksum. Aborting upload."
58+
exit 1
59+
fi
60+
61+
echo "Uploading CHECKSUMS.sha256 to release $TAG..."
62+
gh release upload "$TAG" "$CHECKSUM_FILE" --clobber
63+
64+
echo ""
65+
echo "Done. Verify with:"
66+
echo " gh release view $TAG"

skills/token-optimizer/scripts/measure.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9366,7 +9366,7 @@ def setup_hook(dry_run=False):
93669366

93679367
# ========== Persistent Dashboard Daemon ==========
93689368

9369-
TOKEN_OPTIMIZER_VERSION = "5.7.1" # Keep in sync with plugin.json + marketplace.json
9369+
TOKEN_OPTIMIZER_VERSION = "5.7.2" # Keep in sync with plugin.json + marketplace.json
93709370
_DAEMON_RUNTIME = detect_runtime()
93719371
_DAEMON_RUNTIME_SUFFIX = "codex" if _DAEMON_RUNTIME == "codex" else "claude"
93729372
DAEMON_LABEL = "com.token-optimizer.codex-dashboard" if _DAEMON_RUNTIME == "codex" else "com.token-optimizer.dashboard"

0 commit comments

Comments
 (0)