@blamejs/exceptd-skills 0.19.33 → 0.19.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/bin/exceptd.js +895 -2828
- package/data/_indexes/_meta.json +2 -2
- package/lib/auto-discovery.js +56 -286
- package/lib/canonical-eq.js +7 -40
- package/lib/citation-resolve.js +22 -70
- package/lib/collectors/ai-api.js +170 -76
- package/lib/collectors/cicd-pipeline-compromise.js +113 -136
- package/lib/collectors/citation-hygiene.js +72 -210
- package/lib/collectors/containers.js +41 -130
- package/lib/collectors/cred-stores.js +31 -115
- package/lib/collectors/crypto-codebase.js +55 -138
- package/lib/collectors/crypto.js +24 -54
- package/lib/collectors/hardening.js +20 -78
- package/lib/collectors/kernel.js +16 -46
- package/lib/collectors/library-author.js +198 -211
- package/lib/collectors/mcp.js +24 -70
- package/lib/collectors/runtime.js +24 -86
- package/lib/collectors/sbom.js +130 -118
- package/lib/collectors/scan-excludes.js +33 -139
- package/lib/collectors/secrets.js +62 -178
- package/lib/cross-ref-api.js +39 -123
- package/lib/currency-severity.js +8 -27
- package/lib/cve-batch.js +13 -21
- package/lib/cve-cli.js +13 -20
- package/lib/cve-curation.js +72 -239
- package/lib/cve-regression-watcher.js +29 -155
- package/lib/cvss.js +13 -54
- package/lib/doctor-bucketing.js +3 -19
- package/lib/exit-codes.js +10 -42
- package/lib/flag-suggest.js +7 -25
- package/lib/framework-gap.js +39 -113
- package/lib/gap-detectors.js +37 -159
- package/lib/id-validation.js +9 -30
- package/lib/job-queue.js +13 -36
- package/lib/lint-skills.js +88 -236
- package/lib/playbook-runner.js +759 -2107
- package/lib/prefetch.js +101 -376
- package/lib/refresh-external.js +199 -633
- package/lib/refresh-network.js +78 -311
- package/lib/rfc-cli.js +23 -68
- package/lib/scoring.js +85 -146
- package/lib/sign.js +43 -229
- package/lib/source-advisories.js +43 -194
- package/lib/source-ghsa.js +37 -120
- package/lib/source-osv.js +94 -266
- package/lib/ttp-mapper.js +28 -27
- package/lib/upstream-check-cli.js +36 -29
- package/lib/upstream-check.js +19 -44
- package/lib/validate-catalog-meta.js +17 -61
- package/lib/validate-cve-catalog.js +52 -121
- package/lib/validate-indexes.js +25 -76
- package/lib/validate-package.js +16 -62
- package/lib/validate-playbooks.js +78 -286
- package/lib/validate-vendor.js +16 -49
- package/lib/verify.js +56 -286
- package/lib/version-pins.js +5 -34
- package/lib/worker-pool.js +11 -30
- package/lib/xml-tokenizer.js +47 -152
- package/manifest.json +53 -53
- package/orchestrator/dispatcher.js +17 -68
- package/orchestrator/event-bus.js +11 -74
- package/orchestrator/index.js +138 -413
- package/orchestrator/pipeline.js +28 -85
- package/orchestrator/scanner.js +34 -138
- package/orchestrator/scheduler.js +20 -84
- package/package.json +1 -1
- package/sbom.cdx.json +242 -242
- package/scripts/audit-catalog-gaps.js +9 -62
- package/scripts/audit-cross-skill.js +5 -31
- package/scripts/audit-perf.js +29 -28
- package/scripts/backfill-theater-test.js +7 -64
- package/scripts/bootstrap.js +12 -44
- package/scripts/build-indexes.js +40 -154
- package/scripts/builders/activity-feed.js +4 -14
- package/scripts/builders/catalog-summaries.js +3 -10
- package/scripts/builders/currency.js +7 -20
- package/scripts/builders/cwe-chains.js +7 -30
- package/scripts/builders/did-ladders.js +6 -13
- package/scripts/builders/frequency.js +5 -19
- package/scripts/builders/jurisdiction-clocks.js +6 -25
- package/scripts/builders/recipes.js +6 -14
- package/scripts/builders/section-offsets.js +13 -51
- package/scripts/builders/stale-content.js +7 -28
- package/scripts/builders/summary-cards.js +8 -29
- package/scripts/builders/theater-fingerprints.js +21 -31
- package/scripts/builders/token-budget.js +4 -31
- package/scripts/check-agents-md-collectors.js +26 -57
- package/scripts/check-catalog-gap-budget.js +15 -32
- package/scripts/check-changelog-extract.js +18 -48
- package/scripts/check-codebase-patterns-currency.js +6 -22
- package/scripts/check-codebase-patterns.js +63 -143
- package/scripts/check-epss-consistency.js +9 -64
- package/scripts/check-framework-gap-coverage.js +13 -31
- package/scripts/check-manifest-snapshot.js +62 -81
- package/scripts/check-sbom-currency.js +44 -142
- package/scripts/check-test-count.js +15 -52
- package/scripts/check-test-coverage.js +83 -198
- package/scripts/check-test-subjects.js +21 -62
- package/scripts/check-ttp-references.js +14 -38
- package/scripts/check-ttp-upstream.js +8 -40
- package/scripts/check-version-bump.js +9 -61
- package/scripts/check-version-tags.js +20 -121
- package/scripts/predeploy.js +38 -184
- package/scripts/refresh-manifest-snapshot.js +16 -38
- package/scripts/refresh-mitre-atlas.js +7 -8
- package/scripts/refresh-mitre-attack.js +1 -8
- package/scripts/refresh-mitre-d3fend.js +3 -9
- package/scripts/refresh-mitre-ics-attack.js +7 -8
- package/scripts/refresh-reverse-refs.js +27 -94
- package/scripts/refresh-rfc-index.js +7 -10
- package/scripts/refresh-sbom.js +31 -161
- package/scripts/refresh-upstream-catalogs.js +63 -148
- package/scripts/release.js +69 -234
- package/scripts/run-e2e-scenarios.js +26 -73
- package/scripts/sync-manifest-metadata.js +10 -34
- package/scripts/sync-package-description.js +8 -17
- package/scripts/validate-vendor-online.js +13 -44
- package/scripts/verify-shipped-tarball.js +35 -141
|
@@ -1,72 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* skipped agent/editor worktree copies (`.claude/worktrees/`), so a tree
|
|
10
|
-
* holding N detached worktrees scanned each one as a full repo — inflating
|
|
11
|
-
* hit counts to N+1 duplicates of the same source and forcing manual dedup.
|
|
12
|
-
*
|
|
13
|
-
* Two layers:
|
|
14
|
-
* 1. NAME exclusions — directory basenames never worth descending into
|
|
15
|
-
* (dependency caches, build output, VCS metadata, agent scratch).
|
|
16
|
-
* 2. A LINKED-WORKTREE predicate — a directory that is itself a git
|
|
17
|
-
* worktree distinct from the scan root. A `git worktree add` target
|
|
18
|
-
* carries a `.git` *file* (a gitdir pointer) rather than a `.git`
|
|
19
|
-
* directory; agent tools frequently stamp full repo copies under
|
|
20
|
-
* `.claude/worktrees/`. Skipping these keeps a scan to the one tree
|
|
21
|
-
* the operator actually pointed at.
|
|
22
|
-
*
|
|
23
|
-
* Collectors apply both: spread DEFAULT_CODE_EXCLUDES into their exclude
|
|
24
|
-
* Set, and call isLinkedWorktreeDir(fullPath) before descending into a
|
|
25
|
-
* subdirectory.
|
|
4
|
+
* Shared directory-walk exclusion policy for every code-scope collector: a set of
|
|
5
|
+
* directory basenames never worth descending into, and a predicate for linked git
|
|
6
|
+
* worktrees, which rescan the host tree's source and multiply every hit.
|
|
7
|
+
* Collectors spread DEFAULT_CODE_EXCLUDES into their exclude Set and call
|
|
8
|
+
* isLinkedWorktreeDir(fullPath) before descending.
|
|
26
9
|
*/
|
|
27
10
|
|
|
28
11
|
const fs = require("node:fs");
|
|
29
12
|
const path = require("node:path");
|
|
30
13
|
|
|
31
|
-
// Directory basenames excluded from every code-scope walk. Superset of the
|
|
32
|
-
// per-collector lists that predated this module, plus agent/editor scratch
|
|
33
|
-
// (`.claude`) and additional dependency/build caches that only ever hold
|
|
34
|
-
// generated or third-party content.
|
|
35
14
|
const DEFAULT_CODE_EXCLUDES = Object.freeze([
|
|
36
|
-
// VCS + agent/editor scratch
|
|
37
15
|
".git", ".hg", ".svn", ".claude", ".idea", ".vscode",
|
|
38
|
-
// dependency trees / package caches
|
|
39
16
|
"node_modules", ".pnpm-store", "bower_components",
|
|
40
17
|
".venv", "venv", "__pycache__", ".pytest_cache", ".mypy_cache",
|
|
41
18
|
".tox", ".gradle", ".m2",
|
|
42
|
-
// build output
|
|
43
19
|
"dist", "build", "out", "target", "coverage",
|
|
44
20
|
".next", ".nuxt", ".svelte-kit", ".turbo", ".cache",
|
|
45
21
|
]);
|
|
46
22
|
|
|
47
|
-
/**
|
|
48
|
-
* Build an exclude Set for a collector. Pass any collector-specific extra
|
|
49
|
-
* basenames; they are merged with the shared defaults.
|
|
50
|
-
*
|
|
51
|
-
* @param {Iterable<string>} [extra] additional basenames to exclude
|
|
52
|
-
* @returns {Set<string>}
|
|
53
|
-
*/
|
|
54
23
|
function codeExcludeSet(extra = []) {
|
|
55
24
|
return new Set([...DEFAULT_CODE_EXCLUDES, ...extra]);
|
|
56
25
|
}
|
|
57
26
|
|
|
58
27
|
/**
|
|
59
|
-
* True when `dir` is a git worktree linked
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* `.claude/worktrees/<id>/`); descending into them rescans unrelated repo
|
|
63
|
-
* state. A normal repo root has a `.git` *directory* and is NOT skipped.
|
|
64
|
-
*
|
|
65
|
-
* Cheap and synchronous: one lstat. Returns false on any error so a walk
|
|
66
|
-
* never aborts on a permission/race issue.
|
|
67
|
-
*
|
|
68
|
-
* @param {string} dir absolute path to a candidate directory
|
|
69
|
-
* @returns {boolean}
|
|
28
|
+
* True when `dir` is a git worktree linked elsewhere — its `.git` entry is a file
|
|
29
|
+
* (a `gitdir: …` pointer), where a normal repo root has a `.git` *directory* and
|
|
30
|
+
* is NOT skipped. Returns false on any error, so a walk never aborts on a race.
|
|
70
31
|
*/
|
|
71
32
|
function isLinkedWorktreeDir(dir) {
|
|
72
33
|
try {
|
|
@@ -79,46 +40,14 @@ function isLinkedWorktreeDir(dir) {
|
|
|
79
40
|
}
|
|
80
41
|
|
|
81
42
|
/**
|
|
82
|
-
* Shared cwd-tree walker for every code-scope collector. Returns one entry
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* Behavior preserved from those walkers:
|
|
88
|
-
* - Directory basenames in `excludes` are never descended into.
|
|
89
|
-
* - A linked git worktree (its `.git` is a gitdir-pointer FILE) is never
|
|
90
|
-
* descended into (isLinkedWorktreeDir).
|
|
91
|
-
* - Recursion follows ONLY real directories (`entry.isDirectory()`).
|
|
92
|
-
* Symlinks are never traversed and symlink targets are never emitted
|
|
93
|
-
* as files — matching the old behavior where a Dirent for a symlink is
|
|
94
|
-
* neither isDirectory() nor isFile(), so it fell through both branches.
|
|
95
|
-
* - Depth is capped at `maxDepth`.
|
|
96
|
-
*
|
|
97
|
-
* Performance: `fs.realpathSync` is called ONLY on directories and symlinks
|
|
98
|
-
* (the only entries that can introduce a traversal cycle), never on regular
|
|
99
|
-
* files. The old walkers realpath'd every file entry, which on a large repo
|
|
100
|
-
* dominated walk time. A regular file cannot create a directory cycle, so
|
|
101
|
-
* dropping its realpath preserves cycle protection exactly while removing
|
|
102
|
-
* the per-file syscall. The symlink-cycle `seen` set still guards every
|
|
103
|
-
* directory and symlink by canonical path.
|
|
104
|
-
*
|
|
105
|
-
* Depth-cap visibility: a subtree pruned because it would exceed `maxDepth`
|
|
106
|
-
* is otherwise an invisible coverage gap — the files inside are never emitted
|
|
107
|
-
* and the caller cannot tell "scanned, nothing there" from "never scanned".
|
|
108
|
-
* Pass `opts.truncations` (an array) to have the walker push one
|
|
109
|
-
* `{ rel, depth }` entry per directory whose contents were skipped for being
|
|
110
|
-
* beyond the cap, so a content collector can surface a `depth_capped` notice
|
|
111
|
-
* symmetric with its per-file size-cap reporting. The return value is
|
|
112
|
-
* unchanged (the file array); callers that don't pass `truncations` are
|
|
113
|
-
* unaffected.
|
|
43
|
+
* Shared cwd-tree walker for every code-scope collector. Returns one entry per
|
|
44
|
+
* regular file as `{ full, rel, name }`, `rel` forward-slashed on every platform.
|
|
45
|
+
* Directory basenames in `excludes` and linked git worktrees are never descended
|
|
46
|
+
* into, recursion follows only real directories — a symlink is neither traversed
|
|
47
|
+
* nor emitted — and depth is capped at `maxDepth`.
|
|
114
48
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* @param {number} [opts.maxDepth] max recursion depth (collector-specific)
|
|
118
|
-
* @param {Set<string>} [opts.excludes] directory basenames to skip
|
|
119
|
-
* @param {Array<{rel:string, depth:number}>} [opts.truncations] out-param the
|
|
120
|
-
* walker appends to when a directory is pruned for exceeding maxDepth
|
|
121
|
-
* @returns {Array<{full:string, rel:string, name:string}>}
|
|
49
|
+
* `opts.truncations`, when an array, is an out-param: one `{ rel, depth }` per
|
|
50
|
+
* directory pruned for exceeding the cap. The return value is the file array.
|
|
122
51
|
*/
|
|
123
52
|
function walkTree(root, opts = {}) {
|
|
124
53
|
const maxDepth = opts.maxDepth ?? 6;
|
|
@@ -137,21 +66,14 @@ function walkTree(root, opts = {}) {
|
|
|
137
66
|
const full = path.join(dir, entry.name);
|
|
138
67
|
|
|
139
68
|
if (entry.isDirectory()) {
|
|
140
|
-
// Cycle guard: canonicalize
|
|
141
|
-
//
|
|
142
|
-
// here is bounded by directory count, not file count.
|
|
69
|
+
// Cycle guard: canonicalize so a symlinked or bind-mounted loop is
|
|
70
|
+
// visited at most once.
|
|
143
71
|
let real;
|
|
144
72
|
try { real = fs.realpathSync(full); } catch { continue; }
|
|
145
73
|
if (seen.has(real)) continue;
|
|
146
74
|
seen.add(real);
|
|
147
|
-
// Never descend into a linked git worktree (its `.git` is a gitdir
|
|
148
|
-
// pointer file) — agent tooling stamps full repo copies under
|
|
149
|
-
// `.claude/worktrees/<id>/`; walking them rescans the same source
|
|
150
|
-
// as the host tree and multiplies every hit.
|
|
151
75
|
if (isLinkedWorktreeDir(full)) continue;
|
|
152
|
-
//
|
|
153
|
-
// contents are about to be dropped. Record the prune so the caller
|
|
154
|
-
// can report the unscanned subtree instead of silently missing it.
|
|
76
|
+
// Checked before descending, so the pruned directory can be recorded.
|
|
155
77
|
if (depth + 1 > maxDepth) {
|
|
156
78
|
if (truncations) {
|
|
157
79
|
truncations.push({
|
|
@@ -163,19 +85,13 @@ function walkTree(root, opts = {}) {
|
|
|
163
85
|
}
|
|
164
86
|
walk(full, depth + 1);
|
|
165
87
|
} else if (entry.isSymbolicLink()) {
|
|
166
|
-
// A symlink Dirent is neither isDirectory() nor isFile()
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
// canonicalize for the cycle guard, never emit, never traverse.
|
|
170
|
-
try { fs.realpathSync(full); } catch { /* dangling link — ignore */ }
|
|
88
|
+
// A symlink Dirent is neither isDirectory() nor isFile(), so it is
|
|
89
|
+
// never traversed and never emitted — including a dangling one.
|
|
90
|
+
continue;
|
|
171
91
|
} else if (entry.isFile()) {
|
|
172
|
-
//
|
|
173
|
-
// `seen` check
|
|
174
|
-
//
|
|
175
|
-
// readdir.
|
|
176
|
-
// Emit forward-slash rel paths on every platform so artifact
|
|
177
|
-
// summaries match the SARIF evidence_locations (which normalize the
|
|
178
|
-
// same way) — on Windows path.relative returns backslash separators.
|
|
92
|
+
// A regular file cannot introduce a directory cycle, so no realpath /
|
|
93
|
+
// `seen` check. Windows path.relative returns backslashes, so rel is
|
|
94
|
+
// normalized to match SARIF locations.
|
|
179
95
|
out.push({ full, rel: path.relative(root, full).split(path.sep).join("/"), name: entry.name });
|
|
180
96
|
}
|
|
181
97
|
}
|
|
@@ -184,27 +100,15 @@ function walkTree(root, opts = {}) {
|
|
|
184
100
|
return out;
|
|
185
101
|
}
|
|
186
102
|
|
|
187
|
-
// Per-indicator cap
|
|
188
|
-
// upload. 50 locations per result is well within GitHub code-scanning's
|
|
189
|
-
// rendering budget while still showing the operator where every distinct
|
|
190
|
-
// finding lives.
|
|
103
|
+
// Per-indicator cap: 50 sits well inside GitHub code-scanning's rendering budget.
|
|
191
104
|
const MAX_EVIDENCE_LOCATIONS_PER_INDICATOR = 50;
|
|
192
105
|
|
|
193
106
|
/**
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* `
|
|
197
|
-
* `
|
|
198
|
-
*
|
|
199
|
-
* present — many collectors store `line: 0` to mean "file-level, no line",
|
|
200
|
-
* which omits the region so SARIF points at the file rather than line 0.
|
|
201
|
-
*
|
|
202
|
-
* Identical `{ uri, startLine }` entries are de-duplicated and the list is
|
|
203
|
-
* capped at MAX_EVIDENCE_LOCATIONS_PER_INDICATOR.
|
|
204
|
-
*
|
|
205
|
-
* @param {Array<object>} hits entries with a `rel` or `file` repo-relative
|
|
206
|
-
* path and an optional `line` number
|
|
207
|
-
* @returns {Array<{uri:string,startLine?:number}>}
|
|
107
|
+
* Turns a collector's per-indicator file hits into `evidence_locations` for SARIF
|
|
108
|
+
* `results[].locations`. Each hit yields `{ uri, startLine? }` from its `rel` (or
|
|
109
|
+
* `file`) repo-relative path; `startLine` is emitted only above zero, because
|
|
110
|
+
* collectors store `line: 0` to mean "file-level, no line". Identical entries
|
|
111
|
+
* de-duplicate; the list caps at MAX_EVIDENCE_LOCATIONS_PER_INDICATOR.
|
|
208
112
|
*/
|
|
209
113
|
function buildEvidenceLocations(hits) {
|
|
210
114
|
if (!Array.isArray(hits) || hits.length === 0) return [];
|
|
@@ -227,19 +131,9 @@ function buildEvidenceLocations(hits) {
|
|
|
227
131
|
}
|
|
228
132
|
|
|
229
133
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* with no region. Pairing the offset with this helper lets SARIF point at the
|
|
234
|
-
* exact line.
|
|
235
|
-
*
|
|
236
|
-
* Returns 1 for offset 0 / a non-finite offset (file-level fallback at the
|
|
237
|
-
* first line). Counts `\n` occurrences before the offset; `\r\n` is handled
|
|
238
|
-
* because the `\n` is what increments the line.
|
|
239
|
-
*
|
|
240
|
-
* @param {string} content the file text the offset indexes into
|
|
241
|
-
* @param {number} offset 0-based offset of the match within `content`
|
|
242
|
-
* @returns {number} 1-based line number
|
|
134
|
+
* Converts a 0-based offset into `content` to a 1-based line number, so a
|
|
135
|
+
* content-regex collector can pair a match's `m.index` with a SARIF region.
|
|
136
|
+
* Returns 1 for offset 0 or a non-finite offset. Counts `\n`, covering `\r\n`.
|
|
243
137
|
*/
|
|
244
138
|
function lineFromOffset(content, offset) {
|
|
245
139
|
if (typeof content !== "string") return 1;
|