Skip to content

Commit 12fbfbc

Browse files
committed
release: v0.3.0 — rescan correctness (not_rechecked), scan_id hardening, CI, honesty wording
1 parent 5c6dec2 commit 12fbfbc

26 files changed

Lines changed: 1086 additions & 77 deletions

.github/workflows/ci.yml

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# CodeInspectus CI — automatic checks on every pull request and push to master (CG-77).
2+
#
3+
# Rationale (CG-71 Claim 5): the repo had NO automatic PR/push CI — only the
4+
# manual pin-engines.yml. This adds it, in two tiers that match the tool's own
5+
# engine-gated design (evals E16/E17 self-skip when the security engines are absent):
6+
#
7+
# always-run (no engines) — MUST stay green on every PR regardless of engines.
8+
# Pure-TS surface only: typecheck, build, unit tests, the MCP stdio transport
9+
# smoke, the eval harness (E01-E15 run; E16 Opengrep + E17 Trivy self-skip),
10+
# and an npm-pack contents gate (runtime-only payload; no src/fixtures leak).
11+
# Redaction is covered here by eval E03; the gitleaks-backed redaction-e2e is
12+
# engine-dependent and runs in the engine job below.
13+
#
14+
# engine (linux/amd64) — fetches + cosign/checksum-verifies + SHA-pins the three
15+
# engine binaries and pulls the Trivy vuln DB, then runs the FULL eval (E16 +
16+
# E17 now execute -> 17/17), the gitleaks-backed redaction-e2e, and a real
17+
# fixture scan. Proven feasible in CG-77 recon: every engine has a non-null
18+
# linux-x64 pin and a fresh verified install reproduces the committed SHAs.
19+
#
20+
# Network egress used by the engine job (GitHub-hosted runners have UNRESTRICTED
21+
# egress, so no allow-list is enforced here; listed for the record / future
22+
# hardening — a too-tight allow-list would fail-close the job it protects):
23+
# github.com, objects.githubusercontent.com engine release assets + .sig/.cert/
24+
# .sigstore.json/checksums; action code
25+
# *.sigstore.dev (fulcio/rekor/tuf-repo-cdn) cosign keyless verify-blob (tlog + TUF root)
26+
# mirror.gcr.io, ghcr.io Trivy vuln-DB OCI pull (install-time only)
27+
# registry.npmjs.org npm ci
28+
#
29+
# Third-party actions are pinned to a commit SHA (mirrors the project's engine
30+
# SHA-pin ethos). No run: step consumes untrusted github.event.* input.
31+
name: ci
32+
33+
on:
34+
push:
35+
branches: [master]
36+
pull_request:
37+
38+
permissions:
39+
contents: read
40+
41+
concurrency:
42+
group: ci-${{ github.ref }}
43+
cancel-in-progress: true
44+
45+
jobs:
46+
always:
47+
name: always-run (no engines)
48+
runs-on: ubuntu-latest
49+
steps:
50+
# audit mode — logs egress for a future block-mode allowlist (CG-78); does not restrict.
51+
- uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
52+
with:
53+
egress-policy: audit
54+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
55+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
56+
with:
57+
node-version: 20
58+
cache: npm
59+
- run: npm ci
60+
- name: Typecheck (tsc --noEmit)
61+
run: npm run typecheck
62+
- name: Build (tsc --noEmit && tsup)
63+
run: npm run build
64+
- name: Unit tests (vitest, full)
65+
run: npm test
66+
- name: MCP stdio transport smoke (boots, lists 6 tools, stdout pure JSON-RPC)
67+
run: node scripts/smoke-stdio.mjs
68+
- name: Eval — E01-E15 (engine evals E16/E17 self-skip with no engines)
69+
run: npm run eval
70+
- name: npm pack — runtime-only payload (no src/fixtures/evals/scripts/docs)
71+
run: |
72+
npm pack --dry-run --json | node -e '
73+
const files = JSON.parse(require("fs").readFileSync(0, "utf8"))[0].files.map((f) => f.path);
74+
const leak = files.filter((f) => /^(src|fixtures|evals|scripts|docs)\//.test(f));
75+
console.log("packed top-level:", [...new Set(files.map((f) => f.split("/")[0]))].sort().join(", "));
76+
if (leak.length) { console.error("LEAK — non-runtime paths packed:", leak.join(", ")); process.exit(1); }
77+
console.log("OK — runtime-only,", files.length, "files");
78+
'
79+
80+
engine:
81+
name: engine (linux/amd64)
82+
runs-on: ubuntu-latest
83+
steps:
84+
# audit mode — logs egress for a future block-mode allowlist (CG-78); does not restrict.
85+
- uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
86+
with:
87+
egress-policy: audit
88+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
89+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
90+
with:
91+
node-version: 20
92+
cache: npm
93+
- name: Install cosign (mandatory, fail-closed, for opengrep + trivy verify)
94+
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
95+
- run: npm ci
96+
- name: Build
97+
run: npm run build
98+
- name: Assert runner is linux/amd64 (lockfile platform linux-x64)
99+
run: |
100+
actual="$(node -e "process.stdout.write(process.platform + '-' + process.arch)")"
101+
echo "runner platform: $actual"
102+
test "$actual" = "linux-x64"
103+
- name: Install engines — fetch + cosign/checksum-verify + SHA-pin + Trivy DB
104+
run: node dist/index.js install-engines
105+
- name: Assert freshly-verified SHAs == committed engines.lock.json pins
106+
shell: bash
107+
run: |
108+
# install-engines rewrites only generated_at + provenance.at timestamps; every
109+
# sha256 pin must be byte-identical to what is committed. A changed pin means an
110+
# upstream asset moved under a fixed version tag -> fail closed (supply-chain).
111+
if git diff -- engines.lock.json | grep -qE '^[+-][[:space:]]*"sha256"'; then
112+
echo "FAIL: an engine SHA256 pin changed after a fresh verified install:"
113+
git diff -- engines.lock.json | grep -E '^[+-][[:space:]]*"sha256"'
114+
exit 1
115+
fi
116+
echo "OK: all engine SHA256 pins unchanged (only install timestamps differ)."
117+
- name: Verify installed binaries against the lockfile (native OS)
118+
run: node dist/index.js verify-engines
119+
- name: Eval — full suite; REQUIRE E16 Opengrep + E17 Trivy to RUN (0 skipped)
120+
shell: bash
121+
run: |
122+
# The engine tier EXISTS to prove Opengrep + Trivy actually execute. A skip
123+
# here is a FAILURE, not acceptable degradation: if the Trivy DB pull silently
124+
# failed (populateTrivyDb logs + returns undefined, so install-engines still
125+
# exits 0) or an engine did not run, E16/E17 self-skip and the eval harness
126+
# exits 0 — a false green. Asserting 0 skipped turns that into a RED instead.
127+
out="${RUNNER_TEMP:-/tmp}/eval.out"
128+
set -o pipefail
129+
npm run eval | tee "$out"
130+
grep -qE '0 failed, 0 skipped' "$out" || {
131+
echo "FAIL: engine tier requires 0 skipped — E16 Opengrep + E17 Trivy must RUN, not skip (check the Trivy DB pull from mirror.gcr.io / ghcr.io)."
132+
exit 1
133+
}
134+
- name: Redaction e2e — gitleaks-detected non-pattern secrets stay redacted
135+
run: npm run test:redaction
136+
- name: Real scan smoke on the fixture app (direct runScan path)
137+
run: npx tsx scripts/dev-scan.ts "$PWD/fixtures/vulnerable-app"

.github/workflows/pin-engines.yml

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
# artifact (engines.lock.merged.json). A maintainer reviews the diff and commits.
1111
#
1212
# Why a matrix (vs. pinning all platforms from one machine): pinning is
13-
# platform-agnostic, but running each job natively lets us ALSO smoke-run the
14-
# binary on its real OS (`verify-engines`) before trusting the pin.
13+
# platform-agnostic, but running each job on its NATIVE OS downloads + verifies
14+
# (cosign / checksums, fail-closed) the real per-platform asset before pinning it.
1515
name: pin-engines
1616

1717
on:
@@ -39,15 +39,15 @@ jobs:
3939
- os: windows-latest
4040
platform: win32-x64
4141
steps:
42-
- uses: actions/checkout@v4
42+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
4343

44-
- uses: actions/setup-node@v4
44+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
4545
with:
4646
node-version: 20
4747
cache: npm
4848

4949
- name: Install cosign
50-
uses: sigstore/cosign-installer@v3
50+
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
5151

5252
- run: npm ci
5353
- run: npm run build
@@ -61,14 +61,17 @@ jobs:
6161
echo "runner platform: $actual (expected $EXPECTED_PLATFORM)"
6262
test "$actual" = "$EXPECTED_PLATFORM"
6363
64-
- name: Pin + verify this platform (fail-closed)
64+
- name: Pin this platform (fail-closed)
6565
shell: bash
6666
run: |
67+
# --pin-only downloads each asset, cosign/checksum-verifies it (fail-closed),
68+
# extracts the binary and records its SHA256 + provenance — all on the native OS.
69+
# It installs NO runnable binary, so `verify-engines` (which resolves an installed
70+
# binary) must not follow it here — that mismatch was the CG-71 latent bug.
6771
node dist/index.js install-engines --pin-only
68-
node dist/index.js verify-engines # smoke: SHA + provenance on the native OS
6972
7073
- name: Upload per-platform lockfile
71-
uses: actions/upload-artifact@v4
74+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
7275
with:
7376
name: lockfile-${{ matrix.platform }}
7477
path: engines.lock.json
@@ -79,13 +82,13 @@ jobs:
7982
needs: pin
8083
runs-on: ubuntu-24.04
8184
steps:
82-
- uses: actions/checkout@v4
83-
- uses: actions/setup-node@v4
85+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
86+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
8487
with:
8588
node-version: 20
8689

8790
- name: Download all per-platform lockfiles
88-
uses: actions/download-artifact@v4
91+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
8992
with:
9093
path: pins
9194

@@ -119,7 +122,7 @@ jobs:
119122
run: diff -u engines.lock.json engines.lock.merged.json || true
120123

121124
- name: Upload merged lockfile for human review
122-
uses: actions/upload-artifact@v4
125+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
123126
with:
124127
name: engines.lock.merged
125128
path: engines.lock.merged.json

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@ All notable changes to CodeInspectus are documented here. Versioning follows
44
[Semantic Versioning](https://semver.org). AI-code detections and compliance mappings are
55
AI-drafted and practitioner-reviewed — see the honesty notes in the [README](README.md).
66

7+
## [0.3.0] — 2026-07-12
8+
9+
Rescan now reports "resolved" only when it can prove it, plus honesty fixes to install docs and stored-scan handling.
10+
11+
### Changed
12+
- **Rescan no longer over-claims "resolved."** A prior finding is reported resolved only when CodeInspectus can prove it was re-checked and is gone — the producing engine actually ran, results weren't truncated, and the original scan's scope was reproduced. When it can't confirm (an engine didn't run, results hit a limit, or the prior scan predates captured scope), the finding is reported as **`not_rechecked`** — an honest "couldn't confirm," never a false all-clear. A genuine fix still shows as resolved.
13+
- **Severity threshold on rescan is now display-only.** It affects what's shown, not what's compared — so filtering to "medium and up" can no longer make a still-present lower-severity finding look resolved.
14+
15+
### Fixed
16+
- Rescans could report a still-present finding as "resolved" when the re-scan used a narrower filter, an engine quietly didn't run, or a co-located secret's identity shifted between runs. All three paths are closed; rescan now matches findings on stable identity, not just a run-specific fingerprint.
17+
18+
### Docs
19+
- Install prerequisites now stated up front: **Node.js 18+** and **cosign** on your PATH (cosign verifies the Opengrep and Trivy downloads; the install fails closed without it — Gitleaks needs none).
20+
- Refined the write-scope wording from 0.2.0: CodeInspectus never edits or deletes your source code or repository; scan history and engines live under `~/.codeinspectus`, and an SBOM is written to a managed directory by default, or a path you choose.
21+
22+
### Internal
23+
- Hardened stored-scan handling against path traversal and added validation of loaded scan files. Added continuous integration (build, tests, engine-verified evals) with dependency-pinned workflows. No change to what gets detected — the 35-rule detection set is unchanged.
24+
25+
### Known limitations (stated plainly)
26+
- A rescan run with a smaller `max_findings` than the original may report some findings as `not_rechecked` rather than resolved — by design, so a truncated re-scan never produces a false all-clear.
27+
728
## [0.2.1] — 2026-07-06
829

930
### Fixed

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ before shipping.
4343

4444
## Install
4545

46+
**Prerequisites:** **Node.js ≥18**, and [**cosign**](https://github.com/sigstore/cosign)
47+
on your `PATH` for `install-engines`. Signature verification is **fail-closed**: **Opengrep**
48+
and **Trivy** will **not** pin without cosign — `install-engines` exits non-zero for them —
49+
so install it first (`brew install cosign`). **Gitleaks** verifies by checksum and needs no
50+
cosign.
51+
4652
```bash
4753
# Register once per machine with your agent (see "Client registration"), then:
4854
npx codeinspectus install-engines
@@ -93,12 +99,13 @@ auto-runs the scan → fix → rescan loop.
9399
| `codeinspectus_rescan` | Re-scan after fixes; diffs vs a prior scan → resolved / remaining / introduced. |
94100
| `codeinspectus_compliance_report` | Per-framework **code-level control coverage** (not certification). |
95101
| `codeinspectus_explain_finding` | Deep explanation + full remediation for one finding. |
96-
| `codeinspectus_generate_sbom` | CycloneDX/SPDX SBOM (written to the managed dir by default). |
102+
| `codeinspectus_generate_sbom` | CycloneDX/SPDX SBOM (written to the managed dir by default, or a path you choose). |
97103
| `codeinspectus_list_rules` | Active detectors, engine versions, detection-DB + Trivy-DB freshness. |
98104

99-
CodeInspectus **never writes to or deletes your code or repo** — it reads and reports;
100-
your agent applies the fixes. (The optional SBOM is written to a managed dir outside
101-
your project by default — see `codeinspectus_generate_sbom`.)
105+
CodeInspectus **never edits or deletes your source code or repository** — it reads and
106+
reports; your agent applies the fixes. It stores engine data and scan history under
107+
`~/.codeinspectus`; the only file it writes is an optional SBOM — to a managed directory
108+
by default, or a path you choose (see `codeinspectus_generate_sbom`).
102109

103110
Each scan also reports a read-only **git-safety** state: if there's no git repo or
104111
uncommitted changes, it recommends creating a checkpoint before fixes — your agent
@@ -255,7 +262,7 @@ practitioner)*
255262

256263
## Changelog
257264

258-
Per-version release notes live in [`CHANGELOG.md`](CHANGELOG.md). Current: **v0.2.0**.
265+
Per-version release notes live in [`CHANGELOG.md`](CHANGELOG.md). Current: **v0.3.0**.
259266

260267
## Licenses
261268

agent-rules/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Drop-in rules that make your AI coding agent run CodeInspectus's **scan → surface →
44
consent → fix → rescan** loop (PRD §12): the tool reports the findings, your agent
55
**surfaces them to you and asks before changing code**, and fixes only what you approve.
6-
CodeInspectus never writes to your code or repo — it never edits or deletes your files; your agent applies any
6+
CodeInspectus never edits or deletes your source code or repositorythe only file it writes is an optional SBOM (to a managed directory by default, or a path you choose), and engine data + scan history stay under `~/.codeinspectus`; your agent applies any
77
fix, with your consent. Each scan also returns a read-only **git-safety advisory**: if you have
88
uncommitted work — or no git repo — your agent will offer, with your approval, to checkpoint first
99
so any fix can be rolled back cleanly (CodeInspectus itself never runs git). All clients use the

agent-rules/claude-code.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
After you generate or substantially modify code in this repo, run the CodeInspectus
44
loop. **Principle: CodeInspectus REPORTS; the user DECIDES; you fix only with consent.**
5-
CodeInspectus never writes to your code or repo — it never edits or deletes your files.
5+
CodeInspectus never edits or deletes your source code or repositorythe only file it writes is an optional SBOM (managed directory by default, or a path you choose), and engine data + scan history stay under `~/.codeinspectus`.
66

77
1. **Scan.** Call `codeinspectus_scan` with the absolute repo path. It runs locally with
88
zero network egress and returns CWE-keyed findings, each with a `remediation`.

agent-rules/cline.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# CodeInspectus security loop (Cline custom instructions)
22

33
**Principle: CodeInspectus REPORTS; the user DECIDES; you fix only with consent.** The tool never
4-
writes to your code or repo — it never edits your files. After you generate or modify code:
4+
edits or deletes your source code or repositorythe only file it writes is an optional SBOM (managed dir by default, or a path you choose), with data under `~/.codeinspectus`. After you generate or modify code:
55

66
1. **Scan**`codeinspectus_scan` on the project root (absolute path). Local, zero egress;
77
returns CWE-keyed findings, each with a `remediation`.

agent-rules/codex-AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
## Security: CodeInspectus loop
22

33
**Principle: CodeInspectus REPORTS; the user DECIDES; you fix only with consent.** The tool never
4-
writes to your code or repo (it never edits your files). When you write or change code:
4+
edits or deletes your source code or repository — the only file it writes is an optional SBOM (managed dir by default, or a path you choose), with data under `~/.codeinspectus`. When you write or change code:
55

66
1. **Scan**`codeinspectus_scan` (absolute repo path); local, zero-egress; returns CWE-keyed
77
findings, each with a `remediation`.

agent-rules/cursor.mdc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ description: CodeInspectus — scan, surface findings, fix only with user consen
33
alwaysApply: true
44
---
55

6-
CodeInspectus REPORTS; the user DECIDES; you fix only with consent. The tool never writes to your
7-
code or repo — it never edits your files. After generating or modifying code:
6+
CodeInspectus REPORTS; the user DECIDES; you fix only with consent. The tool never edits or deletes
7+
your source code or repository — the only file it writes is an optional SBOM (managed dir by
8+
default, or a path you choose), with data under `~/.codeinspectus`. After generating or modifying code:
89

910
1. **Scan** — `codeinspectus_scan` on the workspace root (absolute path); local, zero egress;
1011
returns CWE-keyed findings + a `remediation` each.

agent-rules/windsurf.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# CodeInspectus security loop (Windsurf rule)
22

3-
CodeInspectus REPORTS; the user DECIDES; you fix only with consent. The tool never writes to
4-
your code or repo (it never edits your files). After generating or editing code:
3+
CodeInspectus REPORTS; the user DECIDES; you fix only with consent. The tool never edits or deletes
4+
your source code or repository — the only file it writes is an optional SBOM (managed dir by
5+
default, or a path you choose), with data under `~/.codeinspectus`. After generating or editing code:
56

67
1. **Scan**`codeinspectus_scan` on the workspace root (absolute path); local, zero-egress;
78
returns CWE-keyed findings + a `remediation` each.

0 commit comments

Comments
 (0)