Skip to content

Releases: CodeTruss/codetruss-cli

CodeTruss CLI v0.2.46

Choose a tag to compare

@github-actions github-actions released this 07 Aug 22:03
Immutable release. Only release title and notes can be modified.
  • A repository's own scope globs could crash the review that reads them.
    --allow and --deny are matched with minimatch, which expands brace
    groups through brace-expansion, and the bundled copy was 5.0.7. That version
    bounds the number of expansions at 100,000 but not their length, so a
    pattern like {a,b} repeated a few hundred times keeps the count under the
    cap while making every result as long as the pattern has groups. The arrays
    built while combining them exhaust the heap and abort the process
    (CVE-2026-14257, and the incomplete fix for it). This is reachable here
    because allow and deny are read from the scanned repository's
    .codetruss.yml and validated only as non-empty strings: on 0.2.45's shipped
    bundle, a 7.5 KB glob in that file ends codetruss review with an uncatchable
    out-of-memory abort rather than a verdict. brace-expansion moves to 5.0.9,
    which bounds the intermediate arrays as well, and the same input now returns a
    verdict. Scope matching is otherwise unchanged.

    What this is not: nothing is disclosed, nothing is altered, and no verdict
    changes. A pattern cannot reach the matcher from a diff, a filename, or the
    network — only from flags you typed or a config file in the tree you pointed
    the CLI at. The worst outcome was a local tool dying instead of reporting.

  • Dependency floors that were pinned to vulnerable versions have been
    raised.
    minimatch moves to 10.2.6, whose own ^5.0.8 requirement means
    brace-expansion can no longer resolve below the patched line. postcss was
    held at 8.5.16 by a workspace override added as a security pin months ago and
    never refreshed; it moves to 8.5.26, which carries nanoid 3.3.18 with it.
    Neither postcss nor nanoid is in the shipped bundle — they reach the
    repository only through the test runner — so this changes nothing you install.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Windows (PowerShell):

irm https://codetruss.com/install.ps1 | iex

Verify what you installed

sha256  047dc9915d8c0594c913d1c29813edeecb7c71b0fd4e594ab0221139955ad643
gh attestation verify codetruss-cli-0.2.46.tgz --repo CodeTruss/codetruss-cli

The archive ships with a CycloneDX SBOM and SLSA build provenance. You
should not have to take our word for what is in it.

CodeTruss CLI v0.2.45

Choose a tag to compare

@github-actions github-actions released this 07 Aug 21:17
Immutable release. Only release title and notes can be modified.
f233b42
  • A PASS was reachable by typing. codetruss-ignore: <reason> exists so a
    developer can dismiss a finding beside the code it is about, and the one
    promise it makes is that "nothing was found" can never be reached by editing
    text. It could be. A dismissed finding stops gating the verdict — that is what
    dismissing is for — and the marker was honored wherever those characters
    appeared on the finding's own line, including inside a string literal. A
    minified bundle is one physical line, so a single planted string dismissed
    every credential finding in the file, and the verdict followed. The marker is
    now read only where a person could have written it. It must sit in a COMMENT,
    decided by the same classifier the comment analyzers ship, which separates
    comments from code and from strings; in a language that classifier does not
    cover, the marker is honored only in the placement that needs no classifier —
    a line whose every preceding character is whitespace or comment punctuation.
    Markers are no longer read out of generated, vendored or minified content at
    all: that text had no author who could have meant it. And the reason itself is
    now redacted against the credential patterns before it is quoted. A reason runs
    to the end of its line, so a marker written just before a connection string
    harvested the password verbatim onto a signed receipt and synced it to the
    hosted database — the secret scanner's promise that values never leave it now
    holds for the text other passes copy out of the repository too.

  • Oversized-file findings counted comments as code. The size analyzer
    measured non-blank lines and then printed the number as a fact: "parser.ts has
    2202 lines of code", in a document a customer can disprove with wc. Two of
    this repository's own HIGH findings existed only because of it — the same two
    files measure 1995 and 1996 lines of code, both below the threshold that made
    them HIGH — and the overcount inflated every oversized finding, because the
    800-line gate read the same number. Both the gate and the printed number now
    come from the classifier. Nothing stops being reported that a refactor would
    have helped: a file with 800 lines of code has 800 lines of code however they
    are counted. What stops is documentation manufacturing severity.

  • Redirects that are not redirect calls are now findings. The open-redirect
    rule matched two method names, redirect and sendRedirect. Most navigation
    in a React or Next.js codebase is neither: it is <Link href={returnTo}>,
    <form action={next}>, location.href = next, location.assign(...) or
    router.push(...), and none of those is a call to anything the rule was
    looking for. It missed a live open redirect in our own repository on that
    basis. Those shapes are sinks now. The call forms are gated on their receiver,
    because push and replace unqualified are Array.prototype.push and
    String.prototype.replace; the binding forms fire only where the untrusted
    value IS the navigation target — a value, or a field of the request itself —
    because reading taint off a record that a route segment merely looked up turns
    every call-to-action on a [slug] page into an open redirect. Measured against
    this repository, the narrow rule adds the real defect plus two links a reviewer
    should confirm; the wide one added five more that no reviewer should have to.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Windows (PowerShell):

irm https://codetruss.com/install.ps1 | iex

Verify what you installed

sha256  0ced9ca92b28a96faf997a1c45911fd2dd77bb4107fdd3153d88692139c714ce
gh attestation verify codetruss-cli-0.2.45.tgz --repo CodeTruss/codetruss-cli

The archive ships with a CycloneDX SBOM and SLSA build provenance. You
should not have to take our word for what is in it.

CodeTruss CLI v0.2.44

Choose a tag to compare

@github-actions github-actions released this 07 Aug 20:38
Immutable release. Only release title and notes can be modified.
67a9013
  • The person you hand a receipt to can now check it. Until this release a
    receipt could only be verified by the repository that produced it: codetruss verify measures a receipt against the signing keys the local .codetruss.yml
    pins, so the client, auditor, or acquirer the evidence was written for got
    receipt signer <fp> does not match trusted key <fp> and stopped there. That
    is most of the point of handing someone a receipt, and it did not work. The
    gap was concrete rather than theoretical: publishing one of our own receipts
    publicly required shipping a bespoke standalone verifier alongside it, because
    the CLI would not check another install's receipt. codetruss verify-receipt <receipt.json|dir> is the supported path. It needs nothing but the files —
    no checkout, no account, no configuration — and it reports two claims
    separately, because they are two different facts and merging them would be a
    lie. Integrity is that these bytes have not changed since they were
    signed; it is established from the receipt alone, by checking the signature
    under the key the receipt carries, reproducing the Markdown byte-for-byte from
    the signed JSON, and matching the recorded digests. Provenance is that a
    party you trust signed them, and it is established only against a
    --public-key you obtained from that party some other way. A receipt vouching
    for its own key proves nothing about who wrote it — forging one takes a
    keypair and a minute — so a run without a supplied key can never print a
    verified result or exit 0. That ceiling is the feature, not a missing half of
    one. The exit codes carry the distinction into scripts: 0 for both claims, 1
    for bytes that are intact but unattributed, 2 for bytes that are not what was
    signed. When integrity fails, provenance is not evaluated at all and says so,
    rather than printing a key match over altered bytes. Evidence a publisher
    withheld — usually the patch, the only part of a receipt that quotes source —
    is reported as unchecked next to the digest the signature does cover, and the
    integrity line names the hole instead of reading clean. codetruss verify is
    unchanged and still requires a trusted key; its refusal now names the command
    that can check a foreign receipt instead of dead-ending. Both paths run one
    shared check list against one shared set of accepted Markdown renderings, so
    neither can drift into checking less than it claims, and every superseded
    profile wording stays reproducible, so receipts signed by older releases keep
    verifying byte-for-byte.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Windows (PowerShell):

irm https://codetruss.com/install.ps1 | iex

Verify what you installed

sha256  8a405b77b2042c8daca6f2def782fa8e38a2ffcec4ebe9643631d3491af50884
gh attestation verify codetruss-cli-0.2.44.tgz --repo CodeTruss/codetruss-cli

The archive ships with a CycloneDX SBOM and SLSA build provenance. You
should not have to take our word for what is in it.

CodeTruss CLI v0.2.43

Choose a tag to compare

@github-actions github-actions released this 07 Aug 18:37
Immutable release. Only release title and notes can be modified.
80a6cbb
  • A release can no longer reach you carrying code that does not compile.
    Three type errors shipped in 0.2.42 and stopped the following release at its
    first build step. The errors were trivial in themselves: a finding category
    that does not exist, and a test helper that was not updated when one of the
    fields it passes became required. Why they shipped is the part worth fixing.
    The CLI's test sources were compiled by no gate upstream of the release job —
    linting does not typecheck, the website build typechecks the website, and the
    test run cannot stand in for either, because esbuild strips types without ever
    checking them. A suite reports 12 of 12 passing with a type error sitting in
    the file it just ran. The release job was the first step in the chain to
    compile those files, which meant a failed build was the earliest available
    symptom, and it arrived only once a version was already a release candidate. A
    typecheck gate now runs that same compiler over those same files ahead of the
    tests, in both the local gate and continuous integration, so this class of
    defect surfaces at the edit that causes it instead of at the release it blocks.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Windows (PowerShell):

irm https://codetruss.com/install.ps1 | iex

Verify what you installed

sha256  e759a7ffce29ae0a0ba61aeb9bd8819cd75adb114abd1410a7e309d6f8a923c3
gh attestation verify codetruss-cli-0.2.43.tgz --repo CodeTruss/codetruss-cli

The archive ships with a CycloneDX SBOM and SLSA build provenance. You
should not have to take our word for what is in it.

CodeTruss CLI v0.2.41

Choose a tag to compare

@github-actions github-actions released this 07 Aug 16:20
Immutable release. Only release title and notes can be modified.
  • You can dismiss a finding you have judged wrong, in the place the judgement
    belongs.
    A codetruss-ignore: <reason> comment on a finding's own line, or
    on a comment-only line directly above it, marks that finding as dismissed. A
    marker trailing a line of code governs only that line, so it can never reach a
    neighbouring finding its author never looked at. A dismissal never deletes
    anything.
    The finding, its location, and the exact reason its author gave
    all survive into the signed receipt under a "Suppressed findings" heading, and
    the reader decides whether the reason is good — a receipt whose evidence could
    be erased by editing a comment would not be evidence, and "nothing was found"
    must never be reachable that way. The reason is mandatory for the same reason:
    the reason is the output, and "someone decided this was fine" is not
    evidence. A bare codetruss-ignore therefore dismisses nothing and is
    reported by location, so a developer who wrote one finds out why it did
    nothing. Receipts that dismissed nothing are unchanged, byte for byte.
  • Python SQL injection is now caught through a cursor held in a local.
    cur = conn.cursor() then cur.execute(f"... {user_input}") — the canonical
    psycopg/sqlite3/MySQLdb two-step — was invisible, because the sink test was
    lexical and cur does not read as a database receiver. The receiver is now
    resolved to its binding, so a name bound to a .cursor() call counts however
    it is spelled, including with conn.cursor() as cur:. executemany joins
    execute; exec stays name-gated, since a bare .exec() is far more often
    RegExp.prototype.exec than SQL. Generic receiver names were not loosened, so
    nothing else lost precision.
  • A stalled grammar-pack download now fails with a sentence instead of
    hanging.
    codetruss grammars install is a foreground command someone is
    watching, and fetch will wait out a server that writes one byte and holds
    the socket open forever. Two clocks bound it — a whole-transfer budget and an
    idle budget — and the reason it was abandoned survives into the error, rather
    than the bare "This operation was aborted" an abort produces on its own. Both
    are generous enough that a slow connection is never mistaken for a hostile
    origin.
  • A grammar pack's artifacts are bound by role, not by name prefix. The
    loader picked the first file whose name started with tree-sitter-, so a pack
    carrying an extra artifact ordered ahead of the real grammar would have had
    the extra one loaded — and the pin verifier, which only proved each digest
    appeared somewhere, would not have caught it. Each role must now be filled by
    exactly one pinned artifact; an ambiguous pack does not resolve at all. A pack
    that fails this reports a runtime failure rather than a digest failure, so a
    defect in the CLI's own pin never publishes a receipt accusing the user's
    install of tampering.
  • SECURITY.md now states what the grammar-pack digest pin does not cover.
    The pin protects against a compromised download origin, which is what it was
    built for. It cannot protect against a compromised build: the pin, the
    published artifact, and the offline check that compares them all derive from
    the same node_modules on the release machine. pnpm grammars:attest narrows
    that window — it checks the lockfile digest against the npm registry, verifies
    the registry's signature, and compares the downloaded tarball against the
    files the pack is cut from — and the document says plainly that it does not
    close it.
  • hooks doctor names which fields drifted and what to run. It reported
    only that an installed handler "differs", which reads identically for a config
    installed several versions ago and a deliberate hand-edit, and named no
    remedy. It now lists the drifted field names — enough to diagnose, without
    putting handler command text in the message — and names the reinstall command.
    This repository's own committed .codex/hooks.json was the config that
    exposed it: several versions stale, missing core.longpaths=true and pinned
    to the old Stop timeout. It has been refreshed, and a test now compares the
    committed hook configuration against what the installer actually writes, so it
    cannot drift again unnoticed.
  • Build attestation is verified against the CodeTruss organisation. The CLI
    repository moved from the DeliriumPulse account, and every release still in
    circulation has been re-attested under the organisation, so one command
    verifies all of them: gh attestation verify <artifact> --repo CodeTruss/codetruss-cli. The transferred --repo DeliriumPulse/… slug
    returns HTTP 404 and is no longer advertised anywhere. The published manifest,
    the verifier, and the verifier's own tests now derive that command from a
    single module rather than each restating it; the Homebrew tap, plugin
    marketplace and support links follow the organisation too.
  • Internal: hooks.ts is now seven modules behind an unchanged public
    surface.
    Installation, uninstallation, the doctor, the pre-commit block, the
    agent handler shapes, the agent runner and executable resolution each have
    their own file. No behaviour changed, and the hook tests are unmodified by
    design — an unchanged test suite passing over a moved implementation is the
    evidence that the move was only a move.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Windows (PowerShell):

irm https://codetruss.com/install.ps1 | iex

Verify what you installed

sha256  cb0b9d69f15e7113a6a523a6b139ec3150b7d07459e5242b2c047fc9aac57d04
gh attestation verify codetruss-cli-0.2.41.tgz --repo CodeTruss/codetruss-cli

The archive ships with a CycloneDX SBOM and SLSA build provenance. You
should not have to take our word for what is in it.

CodeTruss CLI v0.2.40

Choose a tag to compare

@github-actions github-actions released this 07 Aug 16:19
Immutable release. Only release title and notes can be modified.
  • Python can now be analyzed locally, if you ask for it. codetruss grammars install python downloads the web-tree-sitter runtime and the
    compiled Python grammar (722 KB) into your data directory — XDG on macOS and
    Linux, LOCALAPPDATA on Windows. Nothing is bundled in the tarball, nothing
    is fetched during an analysis, and no other command installs it for you. The
    CLI ships a hand-written JavaScript parser precisely because these grammars
    are several times its entire release budget, and that trade is unchanged for
    anyone who does not run this command. codetruss grammars list|status| uninstall round out the group; status exits non-zero when a pack is
    missing or fails verification, so it can gate a setup script.
  • The pack is pinned, verified as it arrives, and verified again every time
    it is loaded.
    Each artifact's SHA-256 is compiled into the CLI at build
    time. The download is hashed as it streams, with the pinned length enforced
    mid-stream so a wrong or hostile origin cannot write an unbounded file to
    disk; artifacts land in a scratch directory and are moved into place only
    after every one of them verifies, so a pack directory is never half-installed.
    The only download origin is codetruss.com — no third-party CDN, and
    redirects are refused. Hashing is streamed in-process, never shelled out to
    shasum or Get-FileHash. Every failure — absent, truncated, over-long,
    wrong digest, unreadable, or an unexpected extra file in the pack directory —
    resolves to "pack unavailable", and the run reports Python as skipped. There
    is no path on which unverified bytes are executed.
  • Python runs the complete rule pack, not the reduced JavaScript subset.
    That subset exists because a hand-written parser might disagree with
    tree-sitter, and only rules proven to agree were admitted. A grammar pack is
    the hosted parser and the hosted grammar, so there is no divergence to guard
    against — and narrowing it would report less than the same code receives in a
    hosted scan, for no gain in precision. Command injection, path traversal,
    SSRF and insecure deserialization are checked in Python locally; they remain
    unchecked in JavaScript, TypeScript and TSX, and the receipt keeps saying so.
  • Verified against the hosted path over 233 real Python files — the
    full-stack FastAPI template, three further repositories, and a synthetic
    fixture covering each rule class. Both parsers produced the same 11 findings,
    with zero divergence in either direction.
  • Receipts move to the local-registry-v4 profile, which states what the run
    actually did about Python.
    The pass set is unchanged from v3; the wording
    had to change, because v3 says flatly that the local pass covers "JavaScript,
    TypeScript and TSX only" and that Python received no security analysis, and
    that is false whenever a pack is installed. There are now three
    distinguishable statements instead of one frozen sentence: absent names
    the Python file count and the command that would cover them, verified
    names the rule pack and the file count while keeping the JavaScript subset's
    limits scoped to JavaScript, and a failed pack now says which kind of
    failure it was — a digest mismatch (the pack does not match what this CLI
    published, so reinstall), a runtime that would not start on this machine even
    though the digests matched, or a scan that threw partway and had its partial
    results discarded. Only a real digest mismatch renders the tampering sentence;
    an out-of-memory error no longer accuses your install of not matching the
    published digests. Every failure branch closes with the provable "No findings
    from this pack were reported" in place of the wider absolute claim.
    local-registry-v3 keeps a frozen renderer, so receipts signed by 0.2.39
    still verify byte-for-byte.
  • The bytes that are verified are now the exact bytes that execute. The
    loader used to hash each artifact by path and then re-open the same path to
    require() it, so the file that was hashed and the file that ran were two
    separate reads with a window between them — three digests and a directory
    listing wide enough for another process with write access to the pack
    directory to swap a hostile tree-sitter.js in after the check and have it
    executed. inspectGrammarPack now reads each artifact once and returns the
    buffer it hashed; the runtime is compiled from that buffer and the two WASM
    artifacts are handed to web-tree-sitter as in-memory Uint8Arrays
    (wasmBinary and Language.load), so nothing is ever resolved from a path a
    second time. Artifacts are opened O_NOFOLLOW and rejected unless they are
    regular files; a symlinked pack root, a pack root not owned by the current
    user, or one writable by group or other is refused, and a loose root created
    by an earlier CLI is tightened to 0700 on install. A local same-user race
    that reliably executed attacker code against the previous loader now fails
    every attempt.
  • Fixed: Python was silently dropped from the second half of every review.
    The tree-sitter runtime reassigns its own entry in Node's module cache while
    initializing, so loading it a second time in one process returned the wrong
    object. A review analyzes twice — once for the baseline tree, once for the
    final tree — which meant the final analysis quietly failed to load the grammar
    and reported Python as unanalyzable even with a healthy pack installed. The
    runtime is now loaded once per process. Digests are still re-checked on every
    load; only the runtime construction is reused.
  • Fixed: the Windows data directory was resolved with POSIX path rules.
    LOCALAPPDATA was checked with a path test that treats C:\Users\… as
    relative anywhere other than Windows, which made the branch correct on Windows
    and unverifiable everywhere else. It now names the Windows path flavour
    explicitly, and is covered by a test that runs on every platform.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Windows (PowerShell):

irm https://codetruss.com/install.ps1 | iex

Verify what you installed

sha256  5d64313b8b60acbd1f93e2246557967885a98fdc8c486ea7b2a6417fd8acdac2
gh attestation verify codetruss-cli-0.2.40.tgz --repo CodeTruss/codetruss-cli

The archive ships with a CycloneDX SBOM and SLSA build provenance. You
should not have to take our word for what is in it.

CodeTruss CLI v0.2.39

Choose a tag to compare

@github-actions github-actions released this 07 Aug 09:56
Immutable release. Only release title and notes can be modified.
bd83905

Two analyzers join the registry, which now holds 15. Both target things AI-written code does that compile fine and pass tests.

Comment Signal reports comments that repeat the line beneath them, and comments that narrate an edit (// Updated to use the new auth middleware), address the reader, or describe the work as provisional (// In a real app you would verify this) — the last of which no TODO scan can see, because it carries no marker.

Comment density is reported as a metric and is never a finding. Across the design study the most densely commented codebase produced zero restating comments and the sparsest produced sixty-one, so a density rule would penalise exactly the code worth rewarding.

Speculative Structure reports exported values that appear in no other file, and catch blocks whose entire body logs an error and rethrows it unchanged. Export findings are worded as candidates, because a symbol reached through a dynamic import looks identical to this pass.

Both analyzers emit only INFO and LOW, and the verdict escalates at MEDIUM — so neither can turn a PASS into a REVIEW_REQUIRED or a FAILED. A comment that repeats the line below it is not a reason to stop an agent mid-turn.

Both came from a study that ran candidate rules against eight real repositories and kept only what survived. Rules that measured badly were dropped, including several that seemed obviously right beforehand.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Verify what you installed

sha256  feb9a7454abaf2c25bdeade2a6e638137290df8c0725a447e3aca04aef2bd8f0
gh attestation verify codetruss-cli-0.2.39.tgz --repo CodeTruss/codetruss-cli

CodeTruss CLI v0.2.38

Choose a tag to compare

@github-actions github-actions released this 07 Aug 08:31
Immutable release. Only release title and notes can be modified.
c29284b

Scope drift detection was going quiet on exactly the run where it matters most.

When a repository has no configured scope, a single changed file was allowed to establish its own directory as approved scope — which is circular, since the scope was inferred from the very change being judged. On a first run with no configuration, an agent that wandered into an unrelated directory produced no signal at all.

The single-file allowance is now the whole turn's fallback rather than something each directory can claim for itself: it applies only when admitting it would be the turn's only inferred root. A one-file turn still infers, and a cohesive single-directory turn still infers at any file count. A turn that touches two unrelated directories now reports drift, as it did before inference existed.

Measured on the seeded fixture: with 0.2.36 the drift was reported only when scope was passed explicitly; with 0.2.38 it is reported on the bare default run.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Verify what you installed

sha256  8b6093e502e10402fa6f84cd2cac86c7db9c3e54502c7566f1635ce993b32ff7
gh attestation verify codetruss-cli-0.2.38.tgz --repo CodeTruss/codetruss-cli

CodeTruss CLI v0.2.37

Choose a tag to compare

@github-actions github-actions released this 07 Aug 07:40
Immutable release. Only release title and notes can be modified.
f420be1

Two fixes that came out of chasing a Windows CI failure to its root, plus a guard so one class of release mistake cannot happen again.

Never signal a recorded process id. On Windows, a freed pid is recycled within milliseconds. Both process-tree killers ran taskkill on a leader pid recorded earlier — including from the child's own exit handler, where the leader is dead by definition — which could force-kill whatever unrelated process had inherited that pid. In CI it killed a freshly forked test worker. Termination is now gated on a liveness check through our own process handle, which cannot be misdirected.

The 0.2.35 changelog entry is restored. The 0.2.36 release overwrote its heading, so the release that moved security analysis onto your machine shipped with no entry of its own. It is back, byte-identical to what it said.

The release build now refuses to cut a version whose changelog is wrong — a missing heading, a broken version chain, a duplicate. The bug above is why.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Verify what you installed

sha256  082a03fe5dd2d9c516acc76504e0bb45bc7d3b5a1fa7308fb2d627ce07a3c627
gh attestation verify codetruss-cli-0.2.37.tgz --repo CodeTruss/codetruss-cli

CodeTruss CLI v0.2.36

Choose a tag to compare

@github-actions github-actions released this 07 Aug 05:57
Immutable release. Only release title and notes can be modified.
ed7debb

A Windows correctness release. The repository walk emitted whatever path separator the host used, so a Windows run produced src\users.ts while every other surface in the CLI — receipts, git snapshots, policy globs, scope inference — used src/users.ts.

That had three consequences, all fixed by normalizing at the source:

  • A signed receipt's findings table and its changed-files table named the same file two different ways. A receipt disagreed with itself.
  • Receipt bytes differed by platform for an identical tree, so a cross-platform reproduction could not match.
  • Vendored-directory exclusion (.claude/, vendor/, …) silently stopped matching on Windows, pulling tooling payloads back into analysis.

changedFindings() now compares paths separator-agnostically as well, as defense in depth.

Install

curl -fsSL https://codetruss.com/install.sh | sh

Verify what you installed

sha256  953e3f48725a7471043b48a55aedd33ef78854615a341dd95881d90eeae3e814
gh attestation verify codetruss-cli-0.2.36.tgz --repo CodeTruss/codetruss-cli

The archive ships with a CycloneDX SBOM and SLSA build provenance. You should not have to take our word for what is in it.