@blamejs/exceptd-skills 0.16.29 → 0.16.30
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 +14 -0
- package/bin/exceptd.js +212 -12
- package/data/_indexes/_meta.json +2 -2
- package/data/playbooks/crypto.json +6 -0
- package/lib/collectors/README.md +3 -2
- package/lib/cross-ref-api.js +96 -31
- package/lib/playbook-runner.js +247 -48
- package/lib/prefetch.js +78 -6
- package/lib/refresh-external.js +103 -3
- package/lib/scoring.js +49 -5
- package/lib/validate-cve-catalog.js +14 -2
- package/lib/validate-playbooks.js +133 -38
- package/manifest.json +53 -53
- package/package.json +1 -1
- package/sbom.cdx.json +34 -34
- package/scripts/run-e2e-scenarios.js +41 -11
package/sbom.cdx.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.6",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:a7c95fa5-56fc-47e1-9190-3c7136d591ca",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "
|
|
7
|
+
"timestamp": "2115-03-16T22:18:13.000Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "blamejs",
|
|
11
11
|
"name": "scripts/refresh-sbom.js",
|
|
12
|
-
"version": "0.16.
|
|
12
|
+
"version": "0.16.30"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"component": {
|
|
16
|
-
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.16.
|
|
16
|
+
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.16.30",
|
|
17
17
|
"type": "application",
|
|
18
18
|
"name": "@blamejs/exceptd-skills",
|
|
19
|
-
"version": "0.16.
|
|
19
|
+
"version": "0.16.30",
|
|
20
20
|
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 51 skills, 11 catalogs (439 CVEs / 177 CWEs / 805 ATT&CK + ICS / 170 ATLAS / 468 D3FEND / 8888 RFCs), 35 jurisdictions, 10-class catalog gap detector + budget gate, real XML parser + canonical-form diff + content-pattern regression detection, Ed25519-signed.",
|
|
21
21
|
"licenses": [
|
|
22
22
|
{
|
|
@@ -25,17 +25,17 @@
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.16.
|
|
28
|
+
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.16.30",
|
|
29
29
|
"hashes": [
|
|
30
30
|
{
|
|
31
31
|
"alg": "SHA-256",
|
|
32
|
-
"content": "
|
|
32
|
+
"content": "c1bd30e9ed811a98d591a42f6afbfbaa84fd44ef183e7897ad80551fc28b9736"
|
|
33
33
|
}
|
|
34
34
|
],
|
|
35
35
|
"externalReferences": [
|
|
36
36
|
{
|
|
37
37
|
"type": "distribution",
|
|
38
|
-
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.16.
|
|
38
|
+
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.16.30"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"type": "vcs",
|
|
@@ -116,11 +116,11 @@
|
|
|
116
116
|
"hashes": [
|
|
117
117
|
{
|
|
118
118
|
"alg": "SHA-256",
|
|
119
|
-
"content": "
|
|
119
|
+
"content": "673ca71c12d4d15633bb8aef96c9894e153d2ef878c51ace03de99e3ef59e924"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"alg": "SHA3-512",
|
|
123
|
-
"content": "
|
|
123
|
+
"content": "ae11ee07cfeff030fa9cab9d3a668b160a8859d27fa15abad8c2fa9f63f39eaeb45806cd64cc45f23b919a74b682ca0122df10270b3bc10cdb3c56c470f0da5e"
|
|
124
124
|
}
|
|
125
125
|
]
|
|
126
126
|
},
|
|
@@ -281,11 +281,11 @@
|
|
|
281
281
|
"hashes": [
|
|
282
282
|
{
|
|
283
283
|
"alg": "SHA-256",
|
|
284
|
-
"content": "
|
|
284
|
+
"content": "17553af5707a5bf163f76eb28c62bca472d7d9cb74c5d2fb5eb7bf18ab63bde8"
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
"alg": "SHA3-512",
|
|
288
|
-
"content": "
|
|
288
|
+
"content": "05a98e18e6e13d94ee77b95bcb5e22288b5780c1279d89549e9683fce3661165c2e9578ac08ef0732543fb51f59e9588fe6a0141a498a5e91e9afa07766f21f0"
|
|
289
289
|
}
|
|
290
290
|
]
|
|
291
291
|
},
|
|
@@ -566,11 +566,11 @@
|
|
|
566
566
|
"hashes": [
|
|
567
567
|
{
|
|
568
568
|
"alg": "SHA-256",
|
|
569
|
-
"content": "
|
|
569
|
+
"content": "68e5e75851ba4023585726e3c95c8eed651e0e35c8519edf9399addedbbef25b"
|
|
570
570
|
},
|
|
571
571
|
{
|
|
572
572
|
"alg": "SHA3-512",
|
|
573
|
-
"content": "
|
|
573
|
+
"content": "11b1935f75c8b77b50f73cf64ac5e8aafeeddae172d6cdd9e036c6e6348ad3587dd0a173243f0db4c579dae2fef9bf5eeaeb742271588b644ad5edc814ef5493"
|
|
574
574
|
}
|
|
575
575
|
]
|
|
576
576
|
},
|
|
@@ -1031,11 +1031,11 @@
|
|
|
1031
1031
|
"hashes": [
|
|
1032
1032
|
{
|
|
1033
1033
|
"alg": "SHA-256",
|
|
1034
|
-
"content": "
|
|
1034
|
+
"content": "f8b834f2b9615aef61f2ab07c9ff4c1242c9634a3636eec166fbdcdca7c616f0"
|
|
1035
1035
|
},
|
|
1036
1036
|
{
|
|
1037
1037
|
"alg": "SHA3-512",
|
|
1038
|
-
"content": "
|
|
1038
|
+
"content": "667b0cac8ce4d14ce648611de8696165fa64bdf6147d34144252c96edd4dfc6d01fce3be4825c6fabc00ba27a49a431da0a48e9cb4c69f50534ac7aabdc1053e"
|
|
1039
1039
|
}
|
|
1040
1040
|
]
|
|
1041
1041
|
},
|
|
@@ -1271,11 +1271,11 @@
|
|
|
1271
1271
|
"hashes": [
|
|
1272
1272
|
{
|
|
1273
1273
|
"alg": "SHA-256",
|
|
1274
|
-
"content": "
|
|
1274
|
+
"content": "503037e4f50d6fc4ff6ebb972b3b4c3663003c5d9902df52aec0c3f3408f9e47"
|
|
1275
1275
|
},
|
|
1276
1276
|
{
|
|
1277
1277
|
"alg": "SHA3-512",
|
|
1278
|
-
"content": "
|
|
1278
|
+
"content": "c3dde0efa5762c8466eb45ebe93ba61f0f7c0772ec87731b9f7c7994b687ef8deacc5a952f9fd68b96c658b9fd21ee7418845fdbadd4df0228e8e6e22c46d9c7"
|
|
1279
1279
|
}
|
|
1280
1280
|
]
|
|
1281
1281
|
},
|
|
@@ -1466,11 +1466,11 @@
|
|
|
1466
1466
|
"hashes": [
|
|
1467
1467
|
{
|
|
1468
1468
|
"alg": "SHA-256",
|
|
1469
|
-
"content": "
|
|
1469
|
+
"content": "59179357f3ebc1cae07ca63f6e53441e1f5ce001491b0a71c03b00270403a7c7"
|
|
1470
1470
|
},
|
|
1471
1471
|
{
|
|
1472
1472
|
"alg": "SHA3-512",
|
|
1473
|
-
"content": "
|
|
1473
|
+
"content": "b2f8c4ebda7ada3125f8b6cb56fedf7b6072606130cba59c29679df22161162774d7a43e9c3a1b8860ada5320f18ee9d771cd20b48a324c4f2218afd42b0a03d"
|
|
1474
1474
|
}
|
|
1475
1475
|
]
|
|
1476
1476
|
},
|
|
@@ -1481,11 +1481,11 @@
|
|
|
1481
1481
|
"hashes": [
|
|
1482
1482
|
{
|
|
1483
1483
|
"alg": "SHA-256",
|
|
1484
|
-
"content": "
|
|
1484
|
+
"content": "f9c0e300bcce3371b036f7c578470ee80b6bcf33fc9e618e4377c527b889f0a0"
|
|
1485
1485
|
},
|
|
1486
1486
|
{
|
|
1487
1487
|
"alg": "SHA3-512",
|
|
1488
|
-
"content": "
|
|
1488
|
+
"content": "045c5a90c33b7bff92f1fbfb783e573e1819ab8a5980a2bd2c9347502b0b02e87176aa1ac1238c030018cb8d50c4bd1484c434c82e2b3882e2826b45b976f000"
|
|
1489
1489
|
}
|
|
1490
1490
|
]
|
|
1491
1491
|
},
|
|
@@ -1496,11 +1496,11 @@
|
|
|
1496
1496
|
"hashes": [
|
|
1497
1497
|
{
|
|
1498
1498
|
"alg": "SHA-256",
|
|
1499
|
-
"content": "
|
|
1499
|
+
"content": "ad595c63a137b7bb85ff581897cfc8bbdf7e7f46a247e40245a14f564b8a1c07"
|
|
1500
1500
|
},
|
|
1501
1501
|
{
|
|
1502
1502
|
"alg": "SHA3-512",
|
|
1503
|
-
"content": "
|
|
1503
|
+
"content": "8cfd603e5d873730e393d678380568d29725e05d37f1d6cf876e54ea186c0912ad30aa0f5135a23fa959fe141b40034f08742e5aac07b0303e4a728e192b8070"
|
|
1504
1504
|
}
|
|
1505
1505
|
]
|
|
1506
1506
|
},
|
|
@@ -1601,11 +1601,11 @@
|
|
|
1601
1601
|
"hashes": [
|
|
1602
1602
|
{
|
|
1603
1603
|
"alg": "SHA-256",
|
|
1604
|
-
"content": "
|
|
1604
|
+
"content": "83d6e9b8afc4c9158ec49a641d6838e06d4189dd0b3e7e8e96ab013a54d05d97"
|
|
1605
1605
|
},
|
|
1606
1606
|
{
|
|
1607
1607
|
"alg": "SHA3-512",
|
|
1608
|
-
"content": "
|
|
1608
|
+
"content": "fe6a305dca12f5ee5c8f51f13aed88307a7ab87ec8f800fe4cf12e7aefe5e2db6a2efc25fc8297f61072d0af4f010fec813211e5cb0778fa7f8ce14a4e5c34af"
|
|
1609
1609
|
}
|
|
1610
1610
|
]
|
|
1611
1611
|
},
|
|
@@ -1736,11 +1736,11 @@
|
|
|
1736
1736
|
"hashes": [
|
|
1737
1737
|
{
|
|
1738
1738
|
"alg": "SHA-256",
|
|
1739
|
-
"content": "
|
|
1739
|
+
"content": "f5ec0535d2e0712654a6dff2952e66b7de2044afb03ab6422ba0b1597779d782"
|
|
1740
1740
|
},
|
|
1741
1741
|
{
|
|
1742
1742
|
"alg": "SHA3-512",
|
|
1743
|
-
"content": "
|
|
1743
|
+
"content": "17a5d85d096aa1744202eb076c72ac8f9ba61a595795d45a06ff9f4628a21f654b38752ee04beb13dc6048945fa9f89f9b204e386503bb200e4a31d93206ab99"
|
|
1744
1744
|
}
|
|
1745
1745
|
]
|
|
1746
1746
|
},
|
|
@@ -1781,11 +1781,11 @@
|
|
|
1781
1781
|
"hashes": [
|
|
1782
1782
|
{
|
|
1783
1783
|
"alg": "SHA-256",
|
|
1784
|
-
"content": "
|
|
1784
|
+
"content": "dd4c2bdee021c4a2c7f0b7c50dc287838e1be60d4e85bb7447ca4575bf0b93f1"
|
|
1785
1785
|
},
|
|
1786
1786
|
{
|
|
1787
1787
|
"alg": "SHA3-512",
|
|
1788
|
-
"content": "
|
|
1788
|
+
"content": "b52322b974b16541d67339a317a72e37c90b52b089776d55686e174de9075bbdb8543469533011446b1f04c46312bd1be438cacb1b3e3e6ec14d92900040bfcd"
|
|
1789
1789
|
}
|
|
1790
1790
|
]
|
|
1791
1791
|
},
|
|
@@ -1901,11 +1901,11 @@
|
|
|
1901
1901
|
"hashes": [
|
|
1902
1902
|
{
|
|
1903
1903
|
"alg": "SHA-256",
|
|
1904
|
-
"content": "
|
|
1904
|
+
"content": "70fea689545e1145b6e4e55621b07083d54e657372d1293406697cbcdd216fbb"
|
|
1905
1905
|
},
|
|
1906
1906
|
{
|
|
1907
1907
|
"alg": "SHA3-512",
|
|
1908
|
-
"content": "
|
|
1908
|
+
"content": "1bf4b7ec8dfa0661f5219a20572816d8bfc9b3c4bb83651f4ba07959063d28099ea519ca0401fcc1d38d5fb7658699453ba1ed71fa6dfdaca7fd615b056ffadc"
|
|
1909
1909
|
}
|
|
1910
1910
|
]
|
|
1911
1911
|
},
|
|
@@ -2651,11 +2651,11 @@
|
|
|
2651
2651
|
"hashes": [
|
|
2652
2652
|
{
|
|
2653
2653
|
"alg": "SHA-256",
|
|
2654
|
-
"content": "
|
|
2654
|
+
"content": "fbf31669b2d177ae643e480f4fad5ea60b7af8ab32617add0350757bd9dcce9e"
|
|
2655
2655
|
},
|
|
2656
2656
|
{
|
|
2657
2657
|
"alg": "SHA3-512",
|
|
2658
|
-
"content": "
|
|
2658
|
+
"content": "5e6b83c43ae77337b7b600d54ce6ecc168ee6fe7401aa154f0da58d13b1f21cf784221dda481895c7721595160d6a8b59093fdb9ea2dccb2eed1fa34f1aaea46"
|
|
2659
2659
|
}
|
|
2660
2660
|
]
|
|
2661
2661
|
},
|
|
@@ -55,6 +55,27 @@ function getJsonPath(obj, dotted) {
|
|
|
55
55
|
return dotted.split(".").reduce((acc, key) => acc?.[key], obj);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// Evaluate the negative stderr guard against raw stderr text. Lives here, NOT
|
|
59
|
+
// inside diffExpect, because the ban must hold regardless of whether stdout
|
|
60
|
+
// parsed as JSON — a scenario whose stdout is a human banner (no JSON body,
|
|
61
|
+
// only an expect_exit assertion) must still enforce a forbidden-token ban on
|
|
62
|
+
// stderr. evaluateScenario calls this unconditionally.
|
|
63
|
+
function stderrBanFailures(expect, stderr) {
|
|
64
|
+
const failures = [];
|
|
65
|
+
if (expect.stderr_must_not_match) {
|
|
66
|
+
for (const regex of expect.stderr_must_not_match) {
|
|
67
|
+
if (new RegExp(regex).test(stderr || "")) {
|
|
68
|
+
failures.push(`stderr_must_not_match /${regex}/: stderr contains it`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return failures;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Diff a parsed JSON body against the positive expect matchers. The negative
|
|
76
|
+
// stderr guard is NOT evaluated here (see stderrBanFailures); this function
|
|
77
|
+
// only inspects the JSON body so it cannot be silently skipped when stdout
|
|
78
|
+
// fails to parse.
|
|
58
79
|
function diffExpect(jsonBody, expect, ctx) {
|
|
59
80
|
const failures = [];
|
|
60
81
|
if (expect.json_path_equals) {
|
|
@@ -89,23 +110,26 @@ function diffExpect(jsonBody, expect, ctx) {
|
|
|
89
110
|
}
|
|
90
111
|
}
|
|
91
112
|
}
|
|
92
|
-
if (expect.stderr_must_not_match) {
|
|
93
|
-
for (const regex of expect.stderr_must_not_match) {
|
|
94
|
-
if (new RegExp(regex).test(ctx.stderr)) {
|
|
95
|
-
failures.push(`stderr_must_not_match /${regex}/: stderr contains it`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
113
|
return failures;
|
|
100
114
|
}
|
|
101
115
|
|
|
102
116
|
function tryParseJson(s) {
|
|
103
117
|
if (!s) return null;
|
|
104
|
-
try {
|
|
105
|
-
|
|
118
|
+
try {
|
|
119
|
+
const v = JSON.parse(s.trim());
|
|
120
|
+
if (v && typeof v === "object") return v;
|
|
121
|
+
} catch { /* ignore */ }
|
|
122
|
+
// Some verbs may emit trailing logs; pick the LAST complete JSON object or
|
|
123
|
+
// array on stdout. A verb envelope is always an object/array, so bare
|
|
124
|
+
// scalars (a trailing JSON-parseable "done"/42/true log line) are skipped —
|
|
125
|
+
// binding assertions against a trailing scalar would silently test the
|
|
126
|
+
// wrong value.
|
|
106
127
|
const lines = s.trim().split("\n");
|
|
107
128
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
108
|
-
try {
|
|
129
|
+
try {
|
|
130
|
+
const v = JSON.parse(lines[i]);
|
|
131
|
+
if (v && typeof v === "object") return v;
|
|
132
|
+
} catch { /* keep looking */ }
|
|
109
133
|
}
|
|
110
134
|
return null;
|
|
111
135
|
}
|
|
@@ -148,6 +172,12 @@ function evaluateScenario(scenario, expect, res) {
|
|
|
148
172
|
failures.push(`stdout did not parse as JSON; first 200 chars: ${stdout.slice(0, 200)}`);
|
|
149
173
|
}
|
|
150
174
|
if (body) failures.push(...diffExpect(body, expect, { stdout, stderr, status }));
|
|
175
|
+
|
|
176
|
+
// The forbidden-token ban on stderr runs unconditionally — it does not
|
|
177
|
+
// depend on stdout parsing as JSON. A scenario with only an expect_exit
|
|
178
|
+
// assertion (human-banner stdout) must still fail if stderr carries a banned
|
|
179
|
+
// token.
|
|
180
|
+
failures.push(...stderrBanFailures(expect, stderr));
|
|
151
181
|
return failures;
|
|
152
182
|
}
|
|
153
183
|
|
|
@@ -266,6 +296,6 @@ function main() {
|
|
|
266
296
|
process.exit(failed.length === 0 ? 0 : 1);
|
|
267
297
|
}
|
|
268
298
|
|
|
269
|
-
module.exports = { evaluateScenario, diffExpect, runScenario };
|
|
299
|
+
module.exports = { evaluateScenario, diffExpect, tryParseJson, stderrBanFailures, runScenario, SCENARIO_DIR };
|
|
270
300
|
|
|
271
301
|
if (require.main === module) main();
|