@blamejs/exceptd-skills 0.18.7 → 0.18.8

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/sbom.cdx.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.6",
4
- "serialNumber": "urn:uuid:83e5d526-412f-4ab5-a36b-aab5ec0748b0",
4
+ "serialNumber": "urn:uuid:d3a04de1-dca3-43af-bb7e-82b5a2d345d7",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2096-02-15T00:13:26.000Z",
7
+ "timestamp": "2138-07-06T17:31:13.000Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "blamejs",
11
11
  "name": "scripts/refresh-sbom.js",
12
- "version": "0.18.7"
12
+ "version": "0.18.8"
13
13
  }
14
14
  ],
15
15
  "component": {
16
- "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.18.7",
16
+ "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.18.8",
17
17
  "type": "application",
18
18
  "name": "@blamejs/exceptd-skills",
19
- "version": "0.18.7",
19
+ "version": "0.18.8",
20
20
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 51 skills, 11 catalogs (459 CVEs / 181 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.18.7",
28
+ "purl": "pkg:npm/%40blamejs/exceptd-skills@0.18.8",
29
29
  "hashes": [
30
30
  {
31
31
  "alg": "SHA-256",
32
- "content": "6b1c571cefd1ebbbca0d71a69a10d9a10507dd7229303d26cbd1064e45c73300"
32
+ "content": "923346e04414fdc62ec6843cc65edbb16118a1933b0c1529488ccf6670b29718"
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.18.7"
38
+ "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.18.8"
39
39
  },
40
40
  {
41
41
  "type": "vcs",
@@ -116,11 +116,11 @@
116
116
  "hashes": [
117
117
  {
118
118
  "alg": "SHA-256",
119
- "content": "d174478f25ec372b30b4bab09ff4b92a48a719ee77559ee9418588f8d53697da"
119
+ "content": "d24cc11d59cc361266e88cd8a23a2ec4884209c8f3f0ea6e9517ed3d8287b424"
120
120
  },
121
121
  {
122
122
  "alg": "SHA3-512",
123
- "content": "8676133b8544b18f91832d98363ddf8286bd415f41abdb63998f6479d54acdeee6cb233190438e1b7b286a86f81552793a1906e25739bffdce572c4b834f8414"
123
+ "content": "f481c943fe8b934e2afa5d4fbf8a23d4d4ae7f23c5218fc7ee042d92ea94feb5b322ad5bdc3d0153bfb45e6df2b7f4bbc110d19046ad64dd6adb7e5a2f254d5a"
124
124
  }
125
125
  ]
126
126
  },
@@ -281,11 +281,11 @@
281
281
  "hashes": [
282
282
  {
283
283
  "alg": "SHA-256",
284
- "content": "316ac6634c88db0909f427fc35d8e6a7087feb83e1dd7b00c5c06640e8b87473"
284
+ "content": "0ac3e38caabeeb444334c71c5e38dbb07e1f04130e24bde35ae056a690c88ab6"
285
285
  },
286
286
  {
287
287
  "alg": "SHA3-512",
288
- "content": "ec4fc7a80cfc1ccf2b99adba5939f5feb77008d2f709c0f1314f1404d84da604c2e4b643ccf1172f1f289e2f0ded2dd690adf6dda8ef3d6e56dde0a458142f79"
288
+ "content": "36f9c2d752cdead65c397d78cf7cc604d0da276b0d4a97173ec17e6a10b94cdff324413215c75d431c9f4912e7fca537280f408965b1206430a0d2e8e430b162"
289
289
  }
290
290
  ]
291
291
  },
@@ -1181,11 +1181,11 @@
1181
1181
  "hashes": [
1182
1182
  {
1183
1183
  "alg": "SHA-256",
1184
- "content": "2d6610ec87dbfd758ef5d25aedea05bae98dc78c228d79b169488edb95425fea"
1184
+ "content": "e19d0c1d04c2b8b83bd42d53a590fbb4b25bdf233acbdbd141b00045d4b94c82"
1185
1185
  },
1186
1186
  {
1187
1187
  "alg": "SHA3-512",
1188
- "content": "f24fd9739bafa29e98821f41fcd8f061e92247ce8493d0ebc5484a25f68669d313d624804a55853d2a2182a7cd992e83a1e6df9992d66177b264fdbe28544c08"
1188
+ "content": "cd7ee65f297b2d93460ba764719ffc14bdd6eb5754c27639105daf2454a752e313f6dc697a50c9a163cb00f065a7898609bfdf0e7c6b0087035cb033936b5c8d"
1189
1189
  }
1190
1190
  ]
1191
1191
  },
@@ -1466,11 +1466,11 @@
1466
1466
  "hashes": [
1467
1467
  {
1468
1468
  "alg": "SHA-256",
1469
- "content": "0a66df0455fef299fd4c7accdae10c529477f8272fe950eb473aa07706239130"
1469
+ "content": "3bfdebd57549c128f03a309d4c767b2506ed6376aadeb0b1335890ca227d7b91"
1470
1470
  },
1471
1471
  {
1472
1472
  "alg": "SHA3-512",
1473
- "content": "28e3d8e2a6f6893ff289c82987df247166d5255d71a29fece3a4c7a4e8d6bf1604a2321b4801077f30d253228996c468829bc821a9a8cf6303ccd0cfbb0cc1cd"
1473
+ "content": "659d295631ea6d19611fd60fe4e01c8c1745d67a0a095f56686d078f8dc57fa0017f747d3d10b31f078af758588cc06fd2aba383562de575f0d6abd73fc17927"
1474
1474
  }
1475
1475
  ]
1476
1476
  },
@@ -1481,11 +1481,11 @@
1481
1481
  "hashes": [
1482
1482
  {
1483
1483
  "alg": "SHA-256",
1484
- "content": "9d102c3d855b3136d483f216506f4ea85430c5520f142d81e652385d24a2ef09"
1484
+ "content": "326fc776527f54bb46cc74bb29ddade5c453a6b94a8c6b19b51657f15e994261"
1485
1485
  },
1486
1486
  {
1487
1487
  "alg": "SHA3-512",
1488
- "content": "b9644b8277479a9a2be6e94178edb88d6b2899313c445160b2f553c2378e1b2985f434a05d9c01c56678815377b91bce8e0c67add74d10d6deb7cbeb674bb060"
1488
+ "content": "386355bec846a1d55cd3543fd786d843be4b167c1bf37fe4b2094580a5f4ff52b5c68f5215f28a95cc5fb5714b09a4c7bc9d4bb028f864bd09cb1d4082954c16"
1489
1489
  }
1490
1490
  ]
1491
1491
  },
@@ -1511,11 +1511,11 @@
1511
1511
  "hashes": [
1512
1512
  {
1513
1513
  "alg": "SHA-256",
1514
- "content": "cc12e7f5ba80bc5cb45419a111252303c87d44a365699606153e6a17b8e76bb5"
1514
+ "content": "56b41c1923358f7d0fc19a94ba945f50c9572780254d503d7e2d8201cf6f40f5"
1515
1515
  },
1516
1516
  {
1517
1517
  "alg": "SHA3-512",
1518
- "content": "245c34749a10e7ee53fc36da050ca02728ae0406ebab0ecceba5633de04deaf9c33520109ef5522bb2b2327ddced6709a233a0b45f132bf52b1f5a637c6e62a3"
1518
+ "content": "9de3a0801976d42d9bdd347ca7c548bdbdcdaa4d636e27292c85b0d99cce3db7491adfb7b88c992b81d875ec683bd9725b86068a2b83998117c1100f2c8a8531"
1519
1519
  }
1520
1520
  ]
1521
1521
  },
@@ -1601,11 +1601,11 @@
1601
1601
  "hashes": [
1602
1602
  {
1603
1603
  "alg": "SHA-256",
1604
- "content": "94e71ce271178b8e126ef62dc007ead5b25be05493c1c3464e6154ce8b1318a0"
1604
+ "content": "015638fdb3d2fd8eae932befd1b87df21092f60e784f3c3658883107de699141"
1605
1605
  },
1606
1606
  {
1607
1607
  "alg": "SHA3-512",
1608
- "content": "9aa347953d197c54493c055828cddbb506084f7627e544eb7e0a12e026aa09e853ae48e901d5a9625ee3944a8c0ba8a8252540997a79a237816cc7ea12bcd9fc"
1608
+ "content": "3504e914988c461b8c8efc37e54fbdd16e319a9810dc5d8ff2223329a1bc71189a94610bdce230f7ed325408af899f485d2ff1d459cc0295ae926a5ca30e7e23"
1609
1609
  }
1610
1610
  ]
1611
1611
  },
@@ -1736,11 +1736,11 @@
1736
1736
  "hashes": [
1737
1737
  {
1738
1738
  "alg": "SHA-256",
1739
- "content": "d7746d47921f7fbc46d48e48c128e5ee30970a80ce174795ec9e9c53a1380266"
1739
+ "content": "3d9c9ab2546998544854d9c42a2381d356df83d48505f4805addd12585ed9988"
1740
1740
  },
1741
1741
  {
1742
1742
  "alg": "SHA3-512",
1743
- "content": "85a58947ba1beac4fe0c2fb878813a2b85196468f893db1df5380632945cd71cb1bd8d9895af8c4f3275e8eb0ae2d04188043df1cd2c914eebca33b6bf18cb70"
1743
+ "content": "2afdbfa32e7ea3ccc7a769d02fc19a3173200e2069a0a5a6ba73e1a90536833ad0ba54259ec435663d8f43a46534ef6c729cbce7cbf6092d85d5ffdae3ebb2e7"
1744
1744
  }
1745
1745
  ]
1746
1746
  },
@@ -1751,11 +1751,11 @@
1751
1751
  "hashes": [
1752
1752
  {
1753
1753
  "alg": "SHA-256",
1754
- "content": "d14f1e2d33a0ab92939eb9cb08119af927f318c5c43f601959dc86a0527ff1f1"
1754
+ "content": "2d6dd20de946bb82de68612f82a8d3ee1c1c541163e60ce0807975e8cffdb1f3"
1755
1755
  },
1756
1756
  {
1757
1757
  "alg": "SHA3-512",
1758
- "content": "5492dfe5010491b17cb97f7b16cbcb24f6b4e9eb943ac90c5da4d5bde6b246cda887a41a4d79ba45ec205f9f997428a17ab02176aaa23a0686ad7ac939f20242"
1758
+ "content": "3f214b4b269b35f70c8605aeef7870ac5dc1b369ba7ba0bec07ba3f1b03b03449bd22f8d4b3e280a2554925d0df241b9c0102d7c2822a27253cbedd5463719f7"
1759
1759
  }
1760
1760
  ]
1761
1761
  },
@@ -1781,11 +1781,11 @@
1781
1781
  "hashes": [
1782
1782
  {
1783
1783
  "alg": "SHA-256",
1784
- "content": "aafd95f45bb355c03853bf99436cea3eb962f5ca5d4e9ba97f0a9ea0e09fbb2a"
1784
+ "content": "3550944a125f1b819f618f5992af7e616ee1318a679c2ba5d1d3b86153fe9364"
1785
1785
  },
1786
1786
  {
1787
1787
  "alg": "SHA3-512",
1788
- "content": "8b2d6aeb376a02c55e42602435c49ae421f6ac34b4c8c36dc6a4b8b83b0810ef871a06b8683d1f5e442aaac36eb2c35ab60465d2eb8a96db56038491bc3f87a8"
1788
+ "content": "5663da9cfec3dc343c3aacc7c62f90a52679e0ae050c92c497d349ffca72952d5f434f9e76018fb675d8a038cf75dcd6f3d3e9240c139e947f4997c4f2ab347c"
1789
1789
  }
1790
1790
  ]
1791
1791
  },
@@ -1811,11 +1811,11 @@
1811
1811
  "hashes": [
1812
1812
  {
1813
1813
  "alg": "SHA-256",
1814
- "content": "daea15b9bc962fad92105fc99c2885c99e07e2de0bb6316e976316c3a2928fb2"
1814
+ "content": "206abe5fa863fa8d032ba256e8a8b5808f44e1b31dc6ceef48f08a783bcc6815"
1815
1815
  },
1816
1816
  {
1817
1817
  "alg": "SHA3-512",
1818
- "content": "a333b3208019e58a4397f9b0bed14609c0702fc52ec099e2c8f50753df335d27f274b247836e55e9b427d21017cc85705e4605b3fe3e3d93829ca90855d51a36"
1818
+ "content": "61836d3d62c94d91a8ca62c6d5ee413b187377ba1926b1222e578dae866e9d798f97d893c91588371d1bb7504972b8632c5153b36a72ac1e81b58dffebc4e3be"
1819
1819
  }
1820
1820
  ]
1821
1821
  },
@@ -1901,11 +1901,11 @@
1901
1901
  "hashes": [
1902
1902
  {
1903
1903
  "alg": "SHA-256",
1904
- "content": "6e8a5ac562c706b0142ad12b07a62982bb83d500870ca33a5d865bd17a16d23a"
1904
+ "content": "9e87cb1282ac35f2d0564d0eb4571583874e01fb74890989ced47bf50c099ab3"
1905
1905
  },
1906
1906
  {
1907
1907
  "alg": "SHA3-512",
1908
- "content": "05f0fbb1b9e45c7ab9453c372f2d69200ffa000f7f5e6e9c925f2d44a2c5bd9e69ca590ba6c68a9271b8778f291b0714c0ed3cd07ba4f6389a84eff27a399d98"
1908
+ "content": "5edffbc2d8dfb15f139d6a2d8127fc92938b2689dca49af91a76f684b4e6659694f5d958e6d2195e516a2b21991b413d952aa06fad0de7d96cad844fa6f52800"
1909
1909
  }
1910
1910
  ]
1911
1911
  },
@@ -1931,11 +1931,11 @@
1931
1931
  "hashes": [
1932
1932
  {
1933
1933
  "alg": "SHA-256",
1934
- "content": "64efb012ab72fd26d9fa335b2c9f125be12618280d48133f98532790398c62f7"
1934
+ "content": "20366bf67474697357fc7f9d4d5b5b8aab8d386101dd7524e99f05f8a2a81cbc"
1935
1935
  },
1936
1936
  {
1937
1937
  "alg": "SHA3-512",
1938
- "content": "57637f8de917e47d69bcc956d1e6c118b29ff966085da9476a76f7142611c9573348b661b00317a4ba6e195ea78606f6b6dc2abda54d65969b9a6657109a2615"
1938
+ "content": "c522f792dfe431e58dc32c3946a8a98574d1619b5ca8a456e7164be384ed0a678daeb160b7d4bfa6e155856bca19d96af4536c4ae16008169e8592f781d3db68"
1939
1939
  }
1940
1940
  ]
1941
1941
  },
@@ -1961,11 +1961,11 @@
1961
1961
  "hashes": [
1962
1962
  {
1963
1963
  "alg": "SHA-256",
1964
- "content": "a73bf2257f7913f35a676ac37989101d21da7e3811da5bbc3a32486c63afbf82"
1964
+ "content": "6ff9b3c288d784d3d958e72d639a7d4ed2751c6f86a4a7da8f52859bb84d270d"
1965
1965
  },
1966
1966
  {
1967
1967
  "alg": "SHA3-512",
1968
- "content": "57b8bcb9cb3446b63c1a47e7eb35c62a0df4f4b89b425c6be0423eb8830d320e9b45e0fe677cbea0f2ba2a43f0cdb50dd4d7291fe387bb61e1843f28b8878cdd"
1968
+ "content": "975f79dd69f62d300633f697252991f3f6e1b963f528bc34a69dbe8db5947e980a0c0b944244df6541d5dca05673e11e5bd4a40fc062177fd42ea21c6ae112e1"
1969
1969
  }
1970
1970
  ]
1971
1971
  },
@@ -1991,11 +1991,11 @@
1991
1991
  "hashes": [
1992
1992
  {
1993
1993
  "alg": "SHA-256",
1994
- "content": "0c2d66b550582c6cc999e8f51577daec4f5159f213f0c662a9eb2a3207bcca06"
1994
+ "content": "e4f02ab953b91cf815077dda42d2217b457e63742ea6a3e3fd2fb0297f1e2df6"
1995
1995
  },
1996
1996
  {
1997
1997
  "alg": "SHA3-512",
1998
- "content": "59b42b9a5da8a54ed01d6afb5c241fca09ff7a3b8bde91b4f4a98f7c3fe7f2dc697b666b587676032ce7f8edc88b549e1759d14fd68eae51e25e0be35c9693d1"
1998
+ "content": "f7b383b58c3cc9c058aab9081a7d5b62eda571661ac67941e6181c801108953b23d385320b91da607364a507afd26cfd5987dfe58f55602980be3f24cf5a6870"
1999
1999
  }
2000
2000
  ]
2001
2001
  },
@@ -2096,11 +2096,11 @@
2096
2096
  "hashes": [
2097
2097
  {
2098
2098
  "alg": "SHA-256",
2099
- "content": "571898677a84c2a42923eec1b5b61063c02574dc57b462828a6572c6fa0df5bc"
2099
+ "content": "853fbe91eb5742816d6376f7786a12d3caee07a9086ce021df88831723b12e18"
2100
2100
  },
2101
2101
  {
2102
2102
  "alg": "SHA3-512",
2103
- "content": "127a8ba473154ae727aab4d45591a66a380b55cd003352b2f7d3d398004800fc720ef175aab3cd2c70502919f1aa771e32a0cf4a8a201a1e9201b80afae01d83"
2103
+ "content": "78c8a460a2a8cdd09a515d104615d57f1b2c841880e187625bde263ce795b0f8e7562929661eb2bf3a8317abc10b0efa0e24ff213ce898eddf1e8acc76f59784"
2104
2104
  }
2105
2105
  ]
2106
2106
  },
@@ -2711,11 +2711,11 @@
2711
2711
  "hashes": [
2712
2712
  {
2713
2713
  "alg": "SHA-256",
2714
- "content": "f6eca9e0fbbd858985fc0796920270f18736928dbf8efcabeb6b5029fb16438e"
2714
+ "content": "2060e17d577c5f6b5c0a3402c8f884b8008a7fd8a95f98748672bec756353939"
2715
2715
  },
2716
2716
  {
2717
2717
  "alg": "SHA3-512",
2718
- "content": "1ca42b4770936791bb9d0c7ec05d10666ba38d86375bc61615fcd0097f8f3a5f83c7cd338edcd9e1b14f5f3f562541b678db760227da9acfe747b767aca16630"
2718
+ "content": "806e9b5d49b87539c404d765e9b903d0301385b2f8e34034236b38919f67428d2012765a15204f06815c19c05407d2836f11e7fae86a6de52f96d9d6cae5a096"
2719
2719
  }
2720
2720
  ]
2721
2721
  },
@@ -69,7 +69,14 @@ function sha256(buf) {
69
69
  }
70
70
 
71
71
  function writeJson(name, obj) {
72
- fs.writeFileSync(path.join(IDX, name), JSON.stringify(obj, null, 2) + "\n", "utf8");
72
+ // Atomic write: a crash / disk-full / SIGKILL mid-write would otherwise leave
73
+ // a truncated JSON output on disk. Write to a temp sibling and rename — rename
74
+ // is atomic on POSIX and Windows, so a reader (or the next build's
75
+ // parse-check) only ever sees the complete old or complete new file.
76
+ const abs = path.join(IDX, name);
77
+ const tmp = `${abs}.tmp-${process.pid}`;
78
+ fs.writeFileSync(tmp, JSON.stringify(obj, null, 2) + "\n", "utf8");
79
+ fs.renameSync(tmp, abs);
73
80
  }
74
81
 
75
82
  function readJson(p) {
@@ -662,10 +669,23 @@ async function runBuilders(ctx, names, opts) {
662
669
  return results;
663
670
  }
664
671
 
665
- function writeMeta(ctx, results) {
672
+ function writeMeta(ctx, results, opts = {}) {
673
+ const prior = loadPriorMeta();
666
674
  const sourceFiles = [...liveSourceSet(ctx)];
667
- const sourceHashes = {};
668
- for (const p of sourceFiles) sourceHashes[p] = sha256(fs.readFileSync(ABS(p)));
675
+ const computedSourceHashes = {};
676
+ for (const p of sourceFiles) computedSourceHashes[p] = sha256(fs.readFileSync(ABS(p)));
677
+
678
+ // A `--only` build regenerates an ARBITRARY subset of outputs (not the set
679
+ // implied by source changes), so the un-rebuilt outputs are not guaranteed to
680
+ // be consistent with the current sources. Preserve the prior source_hashes in
681
+ // that case, so validate-indexes still detects drift and demands a full
682
+ // rebuild — overwriting them with current hashes would fail OPEN, recording
683
+ // stale outputs as fresh. (--changed rebuilds exactly the change-affected
684
+ // outputs and a full build rebuilds all, so their current hashes are accurate.)
685
+ const partial = !!(opts && opts.only);
686
+ const sourceHashes = (partial && prior && prior.source_hashes && typeof prior.source_hashes === "object")
687
+ ? prior.source_hashes
688
+ : computedSourceHashes;
669
689
 
670
690
  const outputsList = OUTPUTS.map((o) => o.file).sort();
671
691
 
@@ -678,7 +698,6 @@ function writeMeta(ctx, results) {
678
698
  // contract for --changed (and the idempotence the predeploy gate relies on).
679
699
  // Reuse the prior timestamp on a genuine no-op; mint a new one only when the
680
700
  // hashed surface actually moved (or there is no prior meta to inherit from).
681
- const prior = loadPriorMeta();
682
701
  const sourcesUnchanged = prior && prior.source_hashes &&
683
702
  JSON.stringify(prior.source_hashes) === JSON.stringify(sourceHashes) &&
684
703
  JSON.stringify(prior.outputs || []) === JSON.stringify(outputsList);
@@ -723,7 +742,7 @@ function writeMeta(ctx, results) {
723
742
  schema_version: "1.1.0",
724
743
  generated_at: generatedAt,
725
744
  generator: "scripts/build-indexes.js",
726
- source_count: sourceFiles.length,
745
+ source_count: Object.keys(sourceHashes).length,
727
746
  source_hashes: sourceHashes,
728
747
  skill_count: ctx.skills.length,
729
748
  catalog_count: ctx.catalogFiles.length,
@@ -801,7 +820,7 @@ async function main() {
801
820
  // byte-identical, so a genuine no-op leaves the file unchanged — no
802
821
  // spurious timestamp-only diff. Re-stamping would add no freshness signal
803
822
  // (validate-indexes keys on source_hashes/outputs, not the timestamp).
804
- writeMeta(ctx, {});
823
+ writeMeta(ctx, {}, opts);
805
824
  return;
806
825
  }
807
826
  } else {
@@ -811,7 +830,7 @@ async function main() {
811
830
  log(`build-indexes — ${chosen.size} output(s) ${opts.parallel ? "in parallel" : "sequential"}${opts.changed ? " (--changed)" : ""}${opts.only ? ` (--only ${opts.only})` : ""}`);
812
831
 
813
832
  const results = await runBuilders(ctx, chosen, opts);
814
- writeMeta(ctx, results);
833
+ writeMeta(ctx, results, opts);
815
834
 
816
835
  log(`build-indexes — done`);
817
836
  }
@@ -66,6 +66,14 @@ function rawUrlForPin(sourceRepo, commit, upstreamPath) {
66
66
  if (!m) return null;
67
67
  const [, owner, repo] = m;
68
68
  const cleanPath = String(upstreamPath || "").replace(/^\/+/, "");
69
+ // Validate every file-derived component before embedding it in the fetch URL,
70
+ // so a tampered _PROVENANCE.json cannot steer the request: owner/repo are
71
+ // GitHub-name-shaped, the commit is a hex git object id, and the path is a
72
+ // relative, traversal-free repo path. With the host a string literal, the
73
+ // fetch destination is fully constrained — not metadata-controlled.
74
+ if (!/^[A-Za-z0-9._-]+$/.test(owner) || !/^[A-Za-z0-9._-]+$/.test(repo)) return null;
75
+ if (!/^[0-9a-fA-F]{7,64}$/.test(String(commit || ""))) return null;
76
+ if (!/^[A-Za-z0-9._/-]+$/.test(cleanPath) || cleanPath.includes("..")) return null;
69
77
  return `https://raw.githubusercontent.com/${owner}/${repo}/${commit}/${cleanPath}`;
70
78
  }
71
79