@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,32 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Captures the public skill surface from manifest.json into
|
|
4
|
+
* manifest-snapshot.json. Run it AFTER an intentional surface change and commit
|
|
5
|
+
* the new snapshot alongside that change — never to "fix" a failing
|
|
6
|
+
* check-manifest-snapshot.js gate, whose breaking-change list is the thing to
|
|
7
|
+
* read first. A breaking change is a surface narrowing every downstream
|
|
8
|
+
* consumer needs to know about.
|
|
4
9
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Do NOT run this to "fix" a failing check-manifest-snapshot.js gate
|
|
11
|
-
* blindly — read the breaking-change list first. A breaking change is
|
|
12
|
-
* a surface narrowing every downstream consumer needs to know about.
|
|
13
|
-
*
|
|
14
|
-
* commitOnly mode. Pass `--commit-only` (or set the env
|
|
15
|
-
* EXCEPTD_SNAPSHOT_AUDIT_ACK=1) to acknowledge that the operator
|
|
16
|
-
* deliberately wants to overwrite the committed snapshot. When neither
|
|
17
|
-
* flag nor env is set AND the snapshot would actually change, the
|
|
18
|
-
* script refuses and emits a structured diff hint. This stops an
|
|
19
|
-
* accidental `npm run refresh-snapshot` (run as muscle-memory while
|
|
20
|
-
* triaging a failing gate) from masking a real breaking change.
|
|
21
|
-
*
|
|
22
|
-
* Usage:
|
|
23
|
-
* node scripts/refresh-manifest-snapshot.js # dry-shows the diff
|
|
24
|
-
* EXCEPTD_SNAPSHOT_AUDIT_ACK=1 \
|
|
25
|
-
* node scripts/refresh-manifest-snapshot.js # writes the new snapshot
|
|
26
|
-
* node scripts/refresh-manifest-snapshot.js --commit-only # same thing, on argv
|
|
27
|
-
*
|
|
28
|
-
* The flag is documented in scripts/predeploy.js so contributors see it
|
|
29
|
-
* the moment the snapshot gate fails.
|
|
10
|
+
* Overwriting an existing snapshot takes `--commit-only` or
|
|
11
|
+
* EXCEPTD_SNAPSHOT_AUDIT_ACK=1. Without either, a run whose capture differs
|
|
12
|
+
* refuses and prints a diff hint, so muscle memory cannot mask a real breaking
|
|
13
|
+
* change. scripts/predeploy.js names the flag where the gate fails.
|
|
30
14
|
*/
|
|
31
15
|
|
|
32
16
|
const fs = require("fs");
|
|
@@ -64,18 +48,14 @@ const manifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, "utf8"));
|
|
|
64
48
|
const snapshot = captureSurface(manifest);
|
|
65
49
|
const newJson = JSON.stringify(snapshot, null, 2) + "\n";
|
|
66
50
|
|
|
67
|
-
// F5 — refuse to overwrite an existing snapshot unless the operator
|
|
68
|
-
// has explicitly acknowledged the rewrite (env or --commit-only flag).
|
|
69
51
|
const argv = process.argv.slice(2);
|
|
70
52
|
const commitOnly =
|
|
71
53
|
argv.includes("--commit-only") ||
|
|
72
54
|
process.env.EXCEPTD_SNAPSHOT_AUDIT_ACK === "1";
|
|
73
55
|
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
// guard decides on may not be the file it then reads. ENOENT IS the "no prior
|
|
78
|
-
// snapshot, write a fresh one" signal.
|
|
56
|
+
// Branch on the read RESULT, never on an existsSync-then-read probe: that is a
|
|
57
|
+
// check-then-use window (CodeQL js/file-system-race) where the file the guard
|
|
58
|
+
// decided on need not be the one read. ENOENT IS the "no prior snapshot" signal.
|
|
79
59
|
let current = null;
|
|
80
60
|
try {
|
|
81
61
|
current = fs.readFileSync(SNAPSHOT_PATH, "utf8");
|
|
@@ -83,8 +63,7 @@ try {
|
|
|
83
63
|
if (e.code !== "ENOENT") throw e;
|
|
84
64
|
}
|
|
85
65
|
if (current !== null && !commitOnly) {
|
|
86
|
-
//
|
|
87
|
-
// changes every run and shouldn't trigger the guard.
|
|
66
|
+
// _generated_at changes every run, so it must not trigger the guard.
|
|
88
67
|
const stripGenerated = (s) => s.replace(
|
|
89
68
|
/"_generated_at":\s*"[^"]+",?\s*\n?/, ""
|
|
90
69
|
);
|
|
@@ -108,9 +87,8 @@ fs.writeFileSync(SNAPSHOT_PATH, newJson, "utf8");
|
|
|
108
87
|
console.log(`[refresh-manifest-snapshot] wrote ${snapshot.skill_count} skills to manifest-snapshot.json`);
|
|
109
88
|
console.log("[refresh-manifest-snapshot] commit this file alongside the surface change.");
|
|
110
89
|
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
// after refresh).
|
|
90
|
+
// A tracked SHA-256 lets check-manifest-snapshot.js catch a hand edit made
|
|
91
|
+
// after the refresh.
|
|
114
92
|
const crypto = require("crypto");
|
|
115
93
|
const snapshotSha = crypto.createHash("sha256").update(newJson).digest("hex");
|
|
116
94
|
const snapshotShaPath = path.join(ROOT, "manifest-snapshot.sha256");
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Thin per-type wrapper for the MITRE ATLAS refresher; the logic lives in
|
|
5
|
+
* scripts/refresh-upstream-catalogs.js#refreshAtlas. Wired as
|
|
6
|
+
* `npm run refresh-mitre-atlas`, and takes --dry-run.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* node scripts/refresh-mitre-atlas.js [--dry-run]
|
|
10
|
-
*
|
|
11
|
-
* Wired as `npm run refresh-mitre-atlas`.
|
|
8
|
+
* `CAP=<n>` bounds how many NEW techniques one run may add, matching every
|
|
9
|
+
* other per-type wrapper. Context backfill onto existing rows is never capped.
|
|
12
10
|
*/
|
|
13
11
|
const { refreshAtlas } = require("./refresh-upstream-catalogs.js");
|
|
14
12
|
const dry = process.argv.includes("--dry-run");
|
|
15
|
-
|
|
13
|
+
const cap = Number(process.env.CAP || Infinity);
|
|
14
|
+
refreshAtlas({ dry, cap }).catch((e) => { console.error("[err]", e); process.exit(1); });
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Thin per-type wrapper for the MITRE ATT&CK refresher. Logic lives in
|
|
4
|
+
* Refreshes only the MITRE ATT&CK catalog. Logic lives in
|
|
7
5
|
* scripts/refresh-upstream-catalogs.js#refreshAttack.
|
|
8
|
-
*
|
|
9
|
-
* node scripts/refresh-mitre-attack.js [--dry-run]
|
|
10
|
-
* CAP=200 node scripts/refresh-mitre-attack.js
|
|
11
|
-
*
|
|
12
|
-
* Wired as `npm run refresh-mitre-attack`.
|
|
13
6
|
*/
|
|
14
7
|
const { refreshAttack } = require("./refresh-upstream-catalogs.js");
|
|
15
8
|
const dry = process.argv.includes("--dry-run");
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* scripts/refresh-upstream-catalogs.js#refreshD3fend.
|
|
8
|
-
*
|
|
9
|
-
* node scripts/refresh-mitre-d3fend.js [--dry-run]
|
|
10
|
-
* CAP=120 node scripts/refresh-mitre-d3fend.js
|
|
11
|
-
*
|
|
12
|
-
* Wired as `npm run refresh-mitre-d3fend`.
|
|
4
|
+
* Thin wrapper for the MITRE D3FEND refresher; the logic lives in
|
|
5
|
+
* scripts/refresh-upstream-catalogs.js#refreshD3fend. Wired as
|
|
6
|
+
* `npm run refresh-mitre-d3fend`.
|
|
13
7
|
*/
|
|
14
8
|
const { refreshD3fend } = require("./refresh-upstream-catalogs.js");
|
|
15
9
|
const dry = process.argv.includes("--dry-run");
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* `npm run refresh-mitre-ics-attack [-- --dry-run]` — per-type wrapper for the
|
|
5
|
+
* MITRE ICS-attack STIX refresher; the logic lives in
|
|
6
|
+
* scripts/refresh-upstream-catalogs.js#refreshIcsAttack.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* node scripts/refresh-mitre-ics-attack.js [--dry-run]
|
|
10
|
-
*
|
|
11
|
-
* Wired as `npm run refresh-mitre-ics-attack`.
|
|
8
|
+
* `CAP=<n>` bounds how many NEW techniques one run may add, matching every
|
|
9
|
+
* other per-type wrapper. Context backfill onto existing rows is never capped.
|
|
12
10
|
*/
|
|
13
11
|
const { refreshIcsAttack } = require("./refresh-upstream-catalogs.js");
|
|
14
12
|
const dry = process.argv.includes("--dry-run");
|
|
15
|
-
|
|
13
|
+
const cap = Number(process.env.CAP || Infinity);
|
|
14
|
+
refreshIcsAttack({ dry, cap }).catch((e) => { console.error("[err]", e); process.exit(1); });
|
|
@@ -1,35 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* atlas-ttps, `skills_referencing` for the other three) listing every
|
|
11
|
-
* skill that points at that entry. The reverse field drifts whenever a
|
|
12
|
-
* skill adds or removes a forward ref without the catalog being updated
|
|
13
|
-
* in lockstep — this script rebuilds the reverse direction from the
|
|
14
|
-
* forward source of truth so the two never disagree.
|
|
15
|
-
*
|
|
16
|
-
* Behaviour. For each catalog file:
|
|
17
|
-
* 1. Walk every skill's relevant forward-ref array in manifest.json.
|
|
18
|
-
* 2. For every catalog entry, list every skill that references it.
|
|
19
|
-
* 3. Sort the resulting skill list and write it back into the per-entry
|
|
20
|
-
* reverse field. All other fields are preserved untouched.
|
|
21
|
-
*
|
|
22
|
-
* The script is idempotent: a second run produces no further changes.
|
|
23
|
-
*
|
|
24
|
-
* The script does NOT touch playbooks_referencing — that field carries
|
|
25
|
-
* playbook ids (data/playbooks/*.json), not skill names; it has its own
|
|
26
|
-
* source of truth and is out of scope for this refresh.
|
|
27
|
-
*
|
|
28
|
-
* Run: node scripts/refresh-reverse-refs.js
|
|
29
|
-
* npm run refresh-reverse-refs
|
|
30
|
-
*
|
|
31
|
-
* Exit code: 0 always (script is unconditionally write-mode). Use
|
|
32
|
-
* tests/reverse-ref-drift.test.js as the read-only drift detector.
|
|
3
|
+
* Rebuilds the denormalised reverse-reference fields in the data catalogs from the
|
|
4
|
+
* forward direction, which is the source of truth: a skill's atlas_refs / cwe_refs
|
|
5
|
+
* / d3fend_refs / rfc_refs, and a CVE's forward refs. Every other field is
|
|
6
|
+
* preserved and a second run changes nothing. `playbooks_referencing` is out of
|
|
7
|
+
* scope — it carries playbook ids, not skill names.
|
|
8
|
+
* Write-mode only, exit 0 always; the read-only drift detector is
|
|
9
|
+
* tests/reverse-ref-drift.test.js.
|
|
33
10
|
*/
|
|
34
11
|
|
|
35
12
|
'use strict';
|
|
@@ -43,17 +20,12 @@ const CVE_CATALOG_PATH = path.join(REPO_ROOT, 'data', 'cve-catalog.json');
|
|
|
43
20
|
const DATA_DIR = path.join(REPO_ROOT, 'data');
|
|
44
21
|
|
|
45
22
|
/* Per-catalog config:
|
|
46
|
-
* file
|
|
47
|
-
* forwardField
|
|
48
|
-
* reverseField
|
|
49
|
-
* source
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* entries so the reverse direction tracks operator-queryable
|
|
53
|
-
* truth, not in-progress curation state)
|
|
54
|
-
* entryKey field on the source object used as the reverse-list value
|
|
55
|
-
* ('name' for skills; '<self-id>' for CVE entries via the
|
|
56
|
-
* map key, so the helper substitutes the iterating key)
|
|
23
|
+
* file relative path under data/
|
|
24
|
+
* forwardField array (or dict, with forwardFieldShape) on the source object
|
|
25
|
+
* reverseField per-entry field this script overwrites in the catalog
|
|
26
|
+
* source 'manifest.skills' walks every skill; 'cve.entries' every
|
|
27
|
+
* non-draft CVE
|
|
28
|
+
* entryKey field used as the reverse-list value; null means the key itself
|
|
57
29
|
*/
|
|
58
30
|
const CATALOGS = [
|
|
59
31
|
{
|
|
@@ -84,12 +56,7 @@ const CATALOGS = [
|
|
|
84
56
|
source: 'manifest.skills',
|
|
85
57
|
entryKey: 'name',
|
|
86
58
|
},
|
|
87
|
-
//
|
|
88
|
-
// `evidence_cves` as the operator-facing "which CVEs land here" index;
|
|
89
|
-
// previously hand-maintained and drifted whenever a new CVE landed
|
|
90
|
-
// without the matching CWE's evidence_cves being updated. Now mirrors
|
|
91
|
-
// `cve.cwe_refs` → `cwe.evidence_cves` automatically. Drafts excluded
|
|
92
|
-
// (they're invisible to default consumers anyway).
|
|
59
|
+
// `evidence_cves` mirrors cve.cwe_refs: which CVEs land on this CWE.
|
|
93
60
|
{
|
|
94
61
|
file: 'cwe-catalog.json',
|
|
95
62
|
forwardField: 'cwe_refs',
|
|
@@ -97,14 +64,8 @@ const CATALOGS = [
|
|
|
97
64
|
source: 'cve.entries',
|
|
98
65
|
entryKey: null, // value is the iterating CVE id
|
|
99
66
|
},
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
// by gap-id) and gap.evidence_cves (array of CVE ids). The forward
|
|
103
|
-
// shape on the CVE side is an OBJECT not an array — keys are the gap
|
|
104
|
-
// ids, values are per-CVE narrative. The reverse direction (which CVEs
|
|
105
|
-
// cite this gap) is a simple set of CVE ids on the gap entry. The
|
|
106
|
-
// helper handles the dict-keyed forward field via the
|
|
107
|
-
// `forwardFieldShape: 'object-keys'` flag.
|
|
67
|
+
// The forward side here is an OBJECT, not an array: keys are gap ids, values
|
|
68
|
+
// the per-CVE narrative, hence forwardFieldShape. The reverse is a set of ids.
|
|
108
69
|
{
|
|
109
70
|
file: 'framework-control-gaps.json',
|
|
110
71
|
forwardField: 'framework_control_gaps',
|
|
@@ -113,12 +74,7 @@ const CATALOGS = [
|
|
|
113
74
|
source: 'cve.entries',
|
|
114
75
|
entryKey: null, // value is the iterating CVE id
|
|
115
76
|
},
|
|
116
|
-
//
|
|
117
|
-
// technique gets a `cve_refs` array carrying the CVE ids that cite it.
|
|
118
|
-
// Pre-v0.13 these catalogs had only forward refs (CVE → TTP); operators
|
|
119
|
-
// reading an ATLAS / ATT&CK entry could not see which CVEs cite it
|
|
120
|
-
// without grepping the whole catalog. These back-edges make the
|
|
121
|
-
// relationship symmetric: a citation is readable from either end.
|
|
77
|
+
// `cve_refs` back-edges: an ATLAS or ATT&CK entry shows which CVEs cite it.
|
|
122
78
|
{
|
|
123
79
|
file: 'atlas-ttps.json',
|
|
124
80
|
forwardField: 'atlas_refs',
|
|
@@ -152,16 +108,9 @@ function buildReverseIndex(skills, forwardField) {
|
|
|
152
108
|
return index;
|
|
153
109
|
}
|
|
154
110
|
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
// Draft entries are skipped — drafts are invisible to default consumers
|
|
159
|
-
// via cross-ref-api, so the reverse direction should track operator-
|
|
160
|
-
// queryable truth, not in-progress curation state.
|
|
161
|
-
//
|
|
162
|
-
// v0.12.40: forwardFieldShape parameter handles the
|
|
163
|
-
// CVE.framework_control_gaps case where the forward field is a dict
|
|
164
|
-
// (gap-id → narrative) rather than an array.
|
|
111
|
+
// catalogEntryId -> Set<cveId>. Drafts are skipped: they are invisible to default
|
|
112
|
+
// consumers through cross-ref-api. Pass forwardFieldShape 'object-keys' when the
|
|
113
|
+
// forward field is a dict.
|
|
165
114
|
function buildCveReverseIndex(cveCatalog, forwardField, forwardFieldShape) {
|
|
166
115
|
const index = new Map();
|
|
167
116
|
for (const [cveId, entry] of Object.entries(cveCatalog)) {
|
|
@@ -221,14 +170,9 @@ function rebuildCatalog(cfg, manifest, cveCatalog) {
|
|
|
221
170
|
}
|
|
222
171
|
}
|
|
223
172
|
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
// failing gates for the two signals this script can surface live
|
|
228
|
-
// elsewhere: orphan forward refs are hard-errored by lib/lint-skills.js
|
|
229
|
-
// ref-resolution ("<id> not present in data/<catalog>"), and reverse-field
|
|
230
|
-
// drift is hard-failed by tests/reverse-ref-drift.test.js. Do not wire
|
|
231
|
-
// this script itself as a "reverse refs clean?" check — it always passes.
|
|
173
|
+
// Orphans — forward refs naming a catalog entry that does not exist — are
|
|
174
|
+
// reported, never fatal. Never wire this script up as a "reverse refs clean?"
|
|
175
|
+
// check: the failing gates are lib/lint-skills.js and reverse-ref-drift.test.js.
|
|
232
176
|
for (const id of index.keys()) {
|
|
233
177
|
if (!seenIds.has(id)) orphans.push(id);
|
|
234
178
|
}
|
|
@@ -249,15 +193,8 @@ function rebuildCatalog(cfg, manifest, cveCatalog) {
|
|
|
249
193
|
};
|
|
250
194
|
}
|
|
251
195
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
* `feeds_into[]` listing playbook ids it chains TO; `fed_by[]` is the
|
|
255
|
-
* symmetric "which playbooks chain into me." Pre-v0.13 operators reading
|
|
256
|
-
* a playbook couldn't see what fed it without grepping every other
|
|
257
|
-
* playbook's feeds_into. The reverse field lives on the playbook's
|
|
258
|
-
* top-level under `_meta.fed_by` (paired with the existing _meta.feeds_into
|
|
259
|
-
* for shape symmetry).
|
|
260
|
-
*/
|
|
196
|
+
// `_meta.fed_by` is the symmetric counterpart of `_meta.feeds_into`: which
|
|
197
|
+
// playbooks chain INTO this one. Writes each playbook file in place.
|
|
261
198
|
function rebuildPlaybookReverse() {
|
|
262
199
|
const playbooksDir = path.join(DATA_DIR, 'playbooks');
|
|
263
200
|
if (!fs.existsSync(playbooksDir)) return { file: 'playbooks/*.json', source: 'playbook.feeds_into', reverseField: 'fed_by', changed: 0, added: 0, removed: 0, unchanged: 0, orphans: [] };
|
|
@@ -271,12 +208,8 @@ function rebuildPlaybookReverse() {
|
|
|
271
208
|
if (!data || !data._meta || !data._meta.id) continue;
|
|
272
209
|
playbookEntries.push({ file: f, filePath, data });
|
|
273
210
|
}
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
// The fed_by reverse is a simple array of source playbook ids (the
|
|
277
|
-
// condition is per-edge; preserved on feeds_into, not duplicated on
|
|
278
|
-
// fed_by — operators read fed_by to find candidates, then look at the
|
|
279
|
-
// source playbook's feeds_into for the gating condition).
|
|
211
|
+
// targetId -> Set<sourceId>. A feeds_into entry carries `playbook_id` +
|
|
212
|
+
// `condition`; fed_by keeps only the ids, so the condition is not duplicated.
|
|
280
213
|
const index = new Map();
|
|
281
214
|
for (const { data } of playbookEntries) {
|
|
282
215
|
const meta = data._meta;
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Refreshes only the RFC catalog, leaving ATT&CK / ATLAS / D3FEND untouched.
|
|
5
|
+
* Logic lives in scripts/refresh-upstream-catalogs.js#refreshRfc.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* ATLAS / D3FEND.
|
|
10
|
-
*
|
|
11
|
-
* node scripts/refresh-rfc-index.js [--dry-run]
|
|
12
|
-
*
|
|
13
|
-
* Wired as `npm run refresh-rfc-index`.
|
|
7
|
+
* `CAP=<n>` bounds how many NEW rows one run may add — the upstream RFC index
|
|
8
|
+
* carries ~9000 entries, so an uncapped first run imports all of them at once.
|
|
9
|
+
* Context backfill onto rows already curated is never capped.
|
|
14
10
|
*/
|
|
15
11
|
const { refreshRfc } = require("./refresh-upstream-catalogs.js");
|
|
16
12
|
const dry = process.argv.includes("--dry-run");
|
|
17
|
-
|
|
13
|
+
const cap = Number(process.env.CAP || Infinity);
|
|
14
|
+
refreshRfc({ dry, cap }).catch((e) => { console.error("[err]", e); process.exit(1); });
|