33CodeTruss CLI follows semantic versioning. Release artifacts and their SHA-256
44checksums are published at < https://codetruss.com/downloads/codetruss-cli-latest.json > .
55
6- The current public release is [ v0.2.39 on GitHub] ( https://github.com/DeliriumPulse /codetruss-cli/releases/tag/v0.2.39 ) ,
6+ The current public release is [ v0.2.40 on GitHub] ( https://github.com/CodeTruss /codetruss-cli/releases/tag/v0.2.40 ) ,
77distributed from < https://codetruss.com/downloads/codetruss-cli-latest.json > .
88The npm ` latest ` tag is still
99[ ` @codetruss/cli@0.2.24 ` ] ( https://www.npmjs.com/package/@codetruss/cli/v/0.2.24 ) :
@@ -16,6 +16,90 @@ were superseded before distribution.
1616
1717No unreleased changes.
1818
19+ ## 0.2.40 — 2026-08-07
20+
21+ - ** Python can now be analyzed locally, if you ask for it.** `codetruss
22+ grammars install python` downloads the ` web-tree-sitter` runtime and the
23+ compiled Python grammar (722 KB) into your data directory — XDG on macOS and
24+ Linux, ` LOCALAPPDATA ` on Windows. Nothing is bundled in the tarball, nothing
25+ is fetched during an analysis, and no other command installs it for you. The
26+ CLI ships a hand-written JavaScript parser precisely because these grammars
27+ are several times its entire release budget, and that trade is unchanged for
28+ anyone who does not run this command. `codetruss grammars list|status|
29+ uninstall` round out the group; ` status` exits non-zero when a pack is
30+ missing or fails verification, so it can gate a setup script.
31+ - ** The pack is pinned, verified as it arrives, and verified again every time
32+ it is loaded.** Each artifact's SHA-256 is compiled into the CLI at build
33+ time. The download is hashed as it streams, with the pinned length enforced
34+ mid-stream so a wrong or hostile origin cannot write an unbounded file to
35+ disk; artifacts land in a scratch directory and are moved into place only
36+ after every one of them verifies, so a pack directory is never half-installed.
37+ The only download origin is ` codetruss.com ` — no third-party CDN, and
38+ redirects are refused. Hashing is streamed in-process, never shelled out to
39+ ` shasum ` or ` Get-FileHash ` . ** Every** failure — absent, truncated, over-long,
40+ wrong digest, unreadable, or an unexpected extra file in the pack directory —
41+ resolves to "pack unavailable", and the run reports Python as skipped. There
42+ is no path on which unverified bytes are executed.
43+ - ** Python runs the complete rule pack, not the reduced JavaScript subset.**
44+ That subset exists because a hand-written parser might disagree with
45+ tree-sitter, and only rules proven to agree were admitted. A grammar pack * is*
46+ the hosted parser and the hosted grammar, so there is no divergence to guard
47+ against — and narrowing it would report less than the same code receives in a
48+ hosted scan, for no gain in precision. Command injection, path traversal,
49+ SSRF and insecure deserialization are checked in Python locally; they remain
50+ unchecked in JavaScript, TypeScript and TSX, and the receipt keeps saying so.
51+ - ** Verified against the hosted path over 233 real Python files** — the
52+ full-stack FastAPI template, three further repositories, and a synthetic
53+ fixture covering each rule class. Both parsers produced the same 11 findings,
54+ with ** zero divergence in either direction** .
55+ - ** Receipts move to the ` local-registry-v4 ` profile, which states what the run
56+ actually did about Python.** The pass set is unchanged from v3; the wording
57+ had to change, because v3 says flatly that the local pass covers "JavaScript,
58+ TypeScript and TSX only" and that Python received no security analysis, and
59+ that is false whenever a pack is installed. There are now three
60+ distinguishable statements instead of one frozen sentence: ** absent** names
61+ the Python file count and the command that would cover them, ** verified**
62+ names the rule pack and the file count while keeping the JavaScript subset's
63+ limits scoped to JavaScript, and a ** failed** pack now says * which* kind of
64+ failure it was — a digest mismatch (the pack does not match what this CLI
65+ published, so reinstall), a runtime that would not start on this machine even
66+ though the digests matched, or a scan that threw partway and had its partial
67+ results discarded. Only a real digest mismatch renders the tampering sentence;
68+ an out-of-memory error no longer accuses your install of not matching the
69+ published digests. Every failure branch closes with the provable "No findings
70+ from this pack were reported" in place of the wider absolute claim.
71+ ` local-registry-v3 ` keeps a frozen renderer, so receipts signed by 0.2.39
72+ still verify byte-for-byte.
73+ - ** The bytes that are verified are now the exact bytes that execute.** The
74+ loader used to hash each artifact by path and then re-open the same path to
75+ ` require() ` it, so the file that was hashed and the file that ran were two
76+ separate reads with a window between them — three digests and a directory
77+ listing wide enough for another process with write access to the pack
78+ directory to swap a hostile ` tree-sitter.js ` in after the check and have it
79+ executed. ` inspectGrammarPack ` now reads each artifact once and returns the
80+ buffer it hashed; the runtime is compiled from that buffer and the two WASM
81+ artifacts are handed to ` web-tree-sitter ` as in-memory ` Uint8Array ` s
82+ (` wasmBinary ` and ` Language.load ` ), so nothing is ever resolved from a path a
83+ second time. Artifacts are opened ` O_NOFOLLOW ` and rejected unless they are
84+ regular files; a symlinked pack root, a pack root not owned by the current
85+ user, or one writable by group or other is refused, and a loose root created
86+ by an earlier CLI is tightened to ` 0700 ` on install. A local same-user race
87+ that reliably executed attacker code against the previous loader now fails
88+ every attempt.
89+ - ** Fixed: Python was silently dropped from the second half of every review.**
90+ The tree-sitter runtime reassigns its own entry in Node's module cache while
91+ initializing, so loading it a second time in one process returned the wrong
92+ object. A review analyzes twice — once for the baseline tree, once for the
93+ final tree — which meant the final analysis quietly failed to load the grammar
94+ and reported Python as unanalyzable even with a healthy pack installed. The
95+ runtime is now loaded once per process. Digests are still re-checked on every
96+ load; only the runtime construction is reused.
97+ - ** Fixed: the Windows data directory was resolved with POSIX path rules.**
98+ ` LOCALAPPDATA ` was checked with a path test that treats ` C:\Users\… ` as
99+ relative anywhere other than Windows, which made the branch correct on Windows
100+ and unverifiable everywhere else. It now names the Windows path flavour
101+ explicitly, and is covered by a test that runs on every platform.
102+
19103## 0.2.39 — 2026-08-07
20104
21105- ** Two analyzers join the registry, which now holds 15.** Both come from a
0 commit comments