@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.
Files changed (119) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/bin/exceptd.js +895 -2828
  3. package/data/_indexes/_meta.json +2 -2
  4. package/lib/auto-discovery.js +56 -286
  5. package/lib/canonical-eq.js +7 -40
  6. package/lib/citation-resolve.js +22 -70
  7. package/lib/collectors/ai-api.js +170 -76
  8. package/lib/collectors/cicd-pipeline-compromise.js +113 -136
  9. package/lib/collectors/citation-hygiene.js +72 -210
  10. package/lib/collectors/containers.js +41 -130
  11. package/lib/collectors/cred-stores.js +31 -115
  12. package/lib/collectors/crypto-codebase.js +55 -138
  13. package/lib/collectors/crypto.js +24 -54
  14. package/lib/collectors/hardening.js +20 -78
  15. package/lib/collectors/kernel.js +16 -46
  16. package/lib/collectors/library-author.js +198 -211
  17. package/lib/collectors/mcp.js +24 -70
  18. package/lib/collectors/runtime.js +24 -86
  19. package/lib/collectors/sbom.js +130 -118
  20. package/lib/collectors/scan-excludes.js +33 -139
  21. package/lib/collectors/secrets.js +62 -178
  22. package/lib/cross-ref-api.js +39 -123
  23. package/lib/currency-severity.js +8 -27
  24. package/lib/cve-batch.js +13 -21
  25. package/lib/cve-cli.js +13 -20
  26. package/lib/cve-curation.js +72 -239
  27. package/lib/cve-regression-watcher.js +29 -155
  28. package/lib/cvss.js +13 -54
  29. package/lib/doctor-bucketing.js +3 -19
  30. package/lib/exit-codes.js +10 -42
  31. package/lib/flag-suggest.js +7 -25
  32. package/lib/framework-gap.js +39 -113
  33. package/lib/gap-detectors.js +37 -159
  34. package/lib/id-validation.js +9 -30
  35. package/lib/job-queue.js +13 -36
  36. package/lib/lint-skills.js +88 -236
  37. package/lib/playbook-runner.js +759 -2107
  38. package/lib/prefetch.js +101 -376
  39. package/lib/refresh-external.js +199 -633
  40. package/lib/refresh-network.js +78 -311
  41. package/lib/rfc-cli.js +23 -68
  42. package/lib/scoring.js +85 -146
  43. package/lib/sign.js +43 -229
  44. package/lib/source-advisories.js +43 -194
  45. package/lib/source-ghsa.js +37 -120
  46. package/lib/source-osv.js +94 -266
  47. package/lib/ttp-mapper.js +28 -27
  48. package/lib/upstream-check-cli.js +36 -29
  49. package/lib/upstream-check.js +19 -44
  50. package/lib/validate-catalog-meta.js +17 -61
  51. package/lib/validate-cve-catalog.js +52 -121
  52. package/lib/validate-indexes.js +25 -76
  53. package/lib/validate-package.js +16 -62
  54. package/lib/validate-playbooks.js +78 -286
  55. package/lib/validate-vendor.js +16 -49
  56. package/lib/verify.js +56 -286
  57. package/lib/version-pins.js +5 -34
  58. package/lib/worker-pool.js +11 -30
  59. package/lib/xml-tokenizer.js +47 -152
  60. package/manifest.json +53 -53
  61. package/orchestrator/dispatcher.js +17 -68
  62. package/orchestrator/event-bus.js +11 -74
  63. package/orchestrator/index.js +138 -413
  64. package/orchestrator/pipeline.js +28 -85
  65. package/orchestrator/scanner.js +34 -138
  66. package/orchestrator/scheduler.js +20 -84
  67. package/package.json +1 -1
  68. package/sbom.cdx.json +242 -242
  69. package/scripts/audit-catalog-gaps.js +9 -62
  70. package/scripts/audit-cross-skill.js +5 -31
  71. package/scripts/audit-perf.js +29 -28
  72. package/scripts/backfill-theater-test.js +7 -64
  73. package/scripts/bootstrap.js +12 -44
  74. package/scripts/build-indexes.js +40 -154
  75. package/scripts/builders/activity-feed.js +4 -14
  76. package/scripts/builders/catalog-summaries.js +3 -10
  77. package/scripts/builders/currency.js +7 -20
  78. package/scripts/builders/cwe-chains.js +7 -30
  79. package/scripts/builders/did-ladders.js +6 -13
  80. package/scripts/builders/frequency.js +5 -19
  81. package/scripts/builders/jurisdiction-clocks.js +6 -25
  82. package/scripts/builders/recipes.js +6 -14
  83. package/scripts/builders/section-offsets.js +13 -51
  84. package/scripts/builders/stale-content.js +7 -28
  85. package/scripts/builders/summary-cards.js +8 -29
  86. package/scripts/builders/theater-fingerprints.js +21 -31
  87. package/scripts/builders/token-budget.js +4 -31
  88. package/scripts/check-agents-md-collectors.js +26 -57
  89. package/scripts/check-catalog-gap-budget.js +15 -32
  90. package/scripts/check-changelog-extract.js +18 -48
  91. package/scripts/check-codebase-patterns-currency.js +6 -22
  92. package/scripts/check-codebase-patterns.js +63 -143
  93. package/scripts/check-epss-consistency.js +9 -64
  94. package/scripts/check-framework-gap-coverage.js +13 -31
  95. package/scripts/check-manifest-snapshot.js +62 -81
  96. package/scripts/check-sbom-currency.js +44 -142
  97. package/scripts/check-test-count.js +15 -52
  98. package/scripts/check-test-coverage.js +83 -198
  99. package/scripts/check-test-subjects.js +21 -62
  100. package/scripts/check-ttp-references.js +14 -38
  101. package/scripts/check-ttp-upstream.js +8 -40
  102. package/scripts/check-version-bump.js +9 -61
  103. package/scripts/check-version-tags.js +20 -121
  104. package/scripts/predeploy.js +38 -184
  105. package/scripts/refresh-manifest-snapshot.js +16 -38
  106. package/scripts/refresh-mitre-atlas.js +7 -8
  107. package/scripts/refresh-mitre-attack.js +1 -8
  108. package/scripts/refresh-mitre-d3fend.js +3 -9
  109. package/scripts/refresh-mitre-ics-attack.js +7 -8
  110. package/scripts/refresh-reverse-refs.js +27 -94
  111. package/scripts/refresh-rfc-index.js +7 -10
  112. package/scripts/refresh-sbom.js +31 -161
  113. package/scripts/refresh-upstream-catalogs.js +63 -148
  114. package/scripts/release.js +69 -234
  115. package/scripts/run-e2e-scenarios.js +26 -73
  116. package/scripts/sync-manifest-metadata.js +10 -34
  117. package/scripts/sync-package-description.js +8 -17
  118. package/scripts/validate-vendor-online.js +13 -44
  119. package/scripts/verify-shipped-tarball.js +35 -141
package/lib/rfc-cli.js CHANGED
@@ -2,21 +2,14 @@
2
2
  "use strict";
3
3
 
4
4
  /**
5
- * lib/rfc-cli.js — `exceptd rfc <number>` resolver.
6
- *
7
- * Local index (whole current RFC series, offline) -> resolved cache -> one
8
- * datatracker lookup to disambiguate obsoleted-vs-nonexistent. Resolves an RFC
9
- * number to its title + status so an agent can confirm a citation (e.g. "is
10
- * RFC 9404 the Sieve spec?") without the datatracker. Optional --check
11
- * "<claimed title>" reports whether the claimed title matches.
5
+ * `exceptd rfc <number>` — resolves an RFC number to its title and status:
6
+ * local index, then resolved cache, then one datatracker lookup to separate
7
+ * obsoleted from nonexistent.
12
8
  */
13
9
 
14
10
  const { resolveRfc } = require("./citation-resolve.js");
15
11
 
16
- // Stopwords that don't disambiguate one RFC title from another. A claimed title
17
- // run preceded by one of these in the index title is still a clean match; a run
18
- // preceded by a CONTENT word (e.g. "datagram" before "transport layer security")
19
- // is the tail of a more-specific title and must NOT be accepted as a match.
12
+ // Stopwords don't disambiguate one RFC title from another.
20
13
  const TITLE_STOPWORDS = new Set(["the", "a", "an", "of", "for", "to", "in", "on", "and", "or"]);
21
14
 
22
15
  function normTitle(s) {
@@ -24,37 +17,19 @@ function normTitle(s) {
24
17
  }
25
18
 
26
19
  /**
27
- * Decide whether a claimed RFC title matches the authoritative index title.
28
- *
29
- * Replaces the old lenient bidirectional substring test (`a.includes(b) ||
30
- * b.includes(a)`), which let "TLS" match the DTLS title (substring of "dtls")
31
- * and let "Transport Layer Security" match the DTLS title (tail-of-phrase).
32
- * The comparison is now whole-word and phrase-aware:
33
- *
34
- * 1. Every claimed token must appear as a WHOLE word in the index title
35
- * (so "tls" never matches inside "dtls").
36
- * 2. The claimed token sequence must appear as a CONTIGUOUS run in the index
37
- * title, OR the claim must cover enough of the index title (containment
38
- * ratio floor) to be unambiguous.
39
- * 3. A contiguous run that is immediately preceded by a distinguishing
40
- * CONTENT word in the index title is rejected — it is the tail of a
41
- * more-specific title (the "datagram transport layer security" trap).
42
- *
43
- * Returns true / false. Only called when both a claim and an index title exist.
20
+ * Whole-word, phrase-aware comparison of a claimed RFC title against the
21
+ * authoritative index title. Called only when both exist.
44
22
  */
45
23
  function titleMatches(claimed, indexTitle) {
46
24
  const claimTokens = normTitle(claimed).split(" ").filter(Boolean);
47
25
  const titleTokens = normTitle(indexTitle).split(" ").filter(Boolean);
48
26
  if (claimTokens.length === 0 || titleTokens.length === 0) return false;
49
27
 
50
- // (1) Whole-word containment: every claimed token must be a standalone token
51
- // in the index title. Kills the tls-inside-dtls substring false positive.
52
28
  const titleSet = new Set(titleTokens);
53
29
  for (const t of claimTokens) {
54
30
  if (!titleSet.has(t)) return false;
55
31
  }
56
32
 
57
- // Find every contiguous run of the claim inside the index title.
58
33
  const runStarts = [];
59
34
  for (let i = 0; i + claimTokens.length <= titleTokens.length; i++) {
60
35
  let hit = true;
@@ -65,16 +40,12 @@ function titleMatches(claimed, indexTitle) {
65
40
  }
66
41
 
67
42
  if (runStarts.length > 0) {
68
- // A single-token claim that is a whole word in the title is unambiguous on
69
- // its own — the whole-word check above already excluded the substring trap
70
- // (e.g. "tls" is NOT a token inside "dtls"), so "TLS" correctly matches the
71
- // 8446 title (standalone "tls" token) but not the 9147 DTLS title.
43
+ // A single-token claim that survived the whole-word check is unambiguous on
44
+ // its own — "tls" is not a token inside "dtls".
72
45
  if (claimTokens.length === 1) return true;
73
- // (3) For a MULTI-token run, accept only if at least one occurrence is NOT
74
- // preceded by a distinguishing content word — i.e. it begins the title
75
- // or is preceded only by a stopword. A run preceded solely by a content
76
- // qualifier (e.g. "datagram" before "transport layer security") is the
77
- // tail of a more-specific title and must not be accepted as a match.
46
+ // A multi-token run counts only where some occurrence begins the title or
47
+ // is preceded by a stopword. Preceded by a content qualifier ("datagram"
48
+ // before "transport layer security") it is the tail of a different title.
78
49
  for (const start of runStarts) {
79
50
  if (start === 0) return true;
80
51
  const prev = titleTokens[start - 1];
@@ -83,13 +54,9 @@ function titleMatches(claimed, indexTitle) {
83
54
  return false;
84
55
  }
85
56
 
86
- // No contiguous run, but all tokens present out of order. Accept only when the
87
- // claim covers a strong majority of the index title's tokens (containment
88
- // ratio floor) — a few scattered tokens against a long title is ambiguous,
89
- // not a match. Count DISTINCT claim tokens that appear in the title: counting
90
- // non-distinct tokens lets a repeated-token claim (e.g. "security security
91
- // security security") inflate the ratio past the floor and falsely match an
92
- // unrelated title.
57
+ // No contiguous run, all tokens present out of order: accept only when the
58
+ // claim covers a strong majority of the title's tokens. DISTINCT claim tokens
59
+ // are counted, or a repeated-token claim inflates the ratio past the floor.
93
60
  const distinct = new Set(claimTokens);
94
61
  const present = [...distinct].filter((t) => titleSet.has(t)).length;
95
62
  const ratio = present / titleTokens.length;
@@ -99,9 +66,8 @@ function titleMatches(claimed, indexTitle) {
99
66
  async function main() {
100
67
  const argv = process.argv.slice(2);
101
68
  const flags = new Set(argv.filter((a) => a.startsWith("--")));
102
- // Reject unknown flags (same contract as the in-process verbs). `--check`
103
- // consumes the following token as its value; that value is a positional, not
104
- // a flag, so it isn't checked here.
69
+ // Unknown flags are rejected, as in the in-process verbs. `--check` consumes
70
+ // the following token, which is a positional and so isn't checked here.
105
71
  const KNOWN = new Set(["--json", "--pretty", "--air-gap", "--no-network", "--check", "--help", "-h"]);
106
72
  const unknown = [...flags].filter((f) => !KNOWN.has(f));
107
73
  if (unknown.length > 0) {
@@ -112,10 +78,8 @@ async function main() {
112
78
  process.exitCode = 1;
113
79
  return;
114
80
  }
115
- // --check "<claimed title>" consumes the FOLLOWING token as its value. Exclude
116
- // that value token by INDEX from the positional pool before selecting id, so
117
- // the RFC number resolves correctly regardless of flag order
118
- // (`rfc --check "Some Title" 9404` reads id=9404, not id="Some Title").
81
+ // The `--check` value token is excluded from the positional pool by INDEX, so
82
+ // `rfc --check "Some Title" 9404` reads id=9404, not id="Some Title".
119
83
  const checkIdx = argv.indexOf("--check");
120
84
  const checkValueIdx = (checkIdx !== -1 && argv[checkIdx + 1] && !argv[checkIdx + 1].startsWith("--")) ? checkIdx + 1 : -1;
121
85
  const positionals = argv.filter((a, i) => !a.startsWith("--") && i !== checkValueIdx);
@@ -123,9 +87,7 @@ async function main() {
123
87
  const pretty = flags.has("--pretty");
124
88
  const json = flags.has("--json") || pretty;
125
89
 
126
- // The claimed title is exactly the excluded value token (kept in lockstep with
127
- // checkValueIdx so the two never diverge); a trailing `--check` with no value
128
- // leaves it null.
90
+ // Exactly the excluded value token; a trailing `--check` with no value is null.
129
91
  let claimedTitle = null;
130
92
  if (checkValueIdx !== -1) claimedTitle = argv[checkValueIdx];
131
93
 
@@ -143,10 +105,7 @@ async function main() {
143
105
  if (claimedTitle && r.title) {
144
106
  titleMatch = titleMatches(claimedTitle, r.title);
145
107
  }
146
- // Derive `ok` from the resolved status + title-check the same way the exit
147
- // code is derived below — a non-zero exit (status nonexistent OR an explicit
148
- // title mismatch) must carry ok:false, not the inverted ok:true the envelope
149
- // previously hardcoded.
108
+ // `ok` derives from the same condition as the exit code below.
150
109
  const fails = r.status === "nonexistent" || titleMatch === false;
151
110
  const body = { verb: "rfc", ...r, ...(claimedTitle ? { claimed_title: claimedTitle, title_match: titleMatch } : {}), ok: !fails };
152
111
 
@@ -171,15 +130,11 @@ async function main() {
171
130
  if (fails) process.exitCode = 2;
172
131
  }
173
132
 
174
- // Only run the CLI when invoked directly (`exceptd rfc ...`). When required by a
175
- // test the IIFE must not fire — it would read process.argv and write to stdout —
176
- // so the pure title-match helper can be exercised in-process.
133
+ // Under require it would read process.argv and write stdout; keep it testable.
177
134
  if (require.main === module) {
178
135
  main().catch((err) => {
179
- // A corrupt/unreadable RFC index (or any unexpected throw inside the async
180
- // body) becomes a rejected promise. Emit the documented {ok:false,error}
181
- // envelope rather than crashing with a raw stack trace, and signal failure
182
- // via exitCode so the event loop drains stderr before exit.
136
+ // Any unexpected throw becomes the {ok:false,error} envelope, not a raw
137
+ // stack; `process.exitCode`, not `process.exit()`, so stderr drains.
183
138
  process.stderr.write(JSON.stringify({ ok: false, verb: "rfc", error: String((err && err.message) || err) }) + "\n");
184
139
  process.exitCode = 1;
185
140
  });