mandrel-platform 1.10.0 → 1.12.0

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.
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * check-semgrep-lockfile.test.mjs — guards the SAST toolchain lockfile
4
+ * (Story #477).
5
+ *
6
+ * What this pins, and why each part earned a test:
7
+ *
8
+ * 1. **The advisories stay gone.** The lockfile carried protobuf 4.25.9
9
+ * (CVE-2026-0994, CVSS 8.2) and setuptools 80.9.0 (GHSA-h35f-9h28-mq5c).
10
+ * Neither could be fixed in place: every protobuf 4.x is affected, and
11
+ * semgrep 1.97.0's `opentelemetry-*~=1.25.0` pin capped protobuf below
12
+ * every patched release. A future bump that lands back inside an affected
13
+ * range would reintroduce a high with no other signal until the next
14
+ * scheduled OSV scan.
15
+ *
16
+ * 2. **The pin sites cannot drift.** The semgrep version lives in THREE
17
+ * places — this lockfile, `SEMGREP_PIN` in pr-quality.yml, and
18
+ * `DEFAULT_SEMGREP_PIN` in update-semgrep-rules.mjs — plus the
19
+ * `SEMGREP_HASHES` map that must carry digests for whatever the default is.
20
+ * Nothing detected disagreement between them before this file.
21
+ *
22
+ * 3. **`--require-hashes` stays satisfiable.** Every entry must be `==`-pinned
23
+ * with a sha256, or the install fails at CI time rather than here.
24
+ *
25
+ * The end-to-end proof (a real `pip install --require-hashes` on linux/cp312)
26
+ * cannot run in this suite — it needs that platform and a network. It is a
27
+ * `verify[]` step on the Story instead; these are the invariants checkable
28
+ * from the tree.
29
+ *
30
+ * Run: node --test scripts/check-semgrep-lockfile.test.mjs
31
+ */
32
+
33
+ import assert from "node:assert/strict";
34
+ import { test } from "node:test";
35
+ import { readFileSync } from "node:fs";
36
+
37
+ const LOCKFILE = "scripts/semgrep-requirements.txt";
38
+ const WORKFLOW = ".github/workflows/pr-quality.yml";
39
+ const UPDATER = "scripts/update-semgrep-rules.mjs";
40
+
41
+ const lockfile = readFileSync(LOCKFILE, "utf8");
42
+
43
+ /**
44
+ * Parse `name==version` requirement lines, ignoring comments and hash
45
+ * continuations. Keyed by lowercased name.
46
+ */
47
+ function requirements(text) {
48
+ /** @type {Map<string, string>} */
49
+ const out = new Map();
50
+ for (const line of text.split("\n")) {
51
+ const trimmed = line.trim();
52
+ if (trimmed === "" || trimmed.startsWith("#") || trimmed.startsWith("--hash")) {
53
+ continue;
54
+ }
55
+ const m = trimmed.match(/^([A-Za-z0-9._-]+)==([^\s\\]+)/);
56
+ if (m) {
57
+ out.set(m[1].toLowerCase(), m[2]);
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+
63
+ const REQS = requirements(lockfile);
64
+
65
+ /** Compare dotted release segments numerically. Returns -1 / 0 / 1. */
66
+ function compareVersions(a, b) {
67
+ const pa = a.split(".").map((n) => Number.parseInt(n, 10));
68
+ const pb = b.split(".").map((n) => Number.parseInt(n, 10));
69
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
70
+ const x = Number.isNaN(pa[i]) || pa[i] === undefined ? 0 : pa[i];
71
+ const y = Number.isNaN(pb[i]) || pb[i] === undefined ? 0 : pb[i];
72
+ if (x !== y) return x < y ? -1 : 1;
73
+ }
74
+ return 0;
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // 1. The advisories that put this Story on the board
79
+ // ---------------------------------------------------------------------------
80
+
81
+ test("protobuf is outside every CVE-2026-0994 affected range", () => {
82
+ // Affected: `< 5.29.6` and `>= 6.30.0rc1, <= 6.33.4`. Patched: 5.29.6, 6.33.5.
83
+ // A DoS in google.protobuf.json_format.ParseDict — 8.2, and the finding that
84
+ // opened tracking issue #472.
85
+ const version = REQS.get("protobuf");
86
+ assert.ok(version, `${LOCKFILE}: protobuf must be pinned`);
87
+ assert.doesNotMatch(version, /[a-zA-Z]/, "expected a final release, not a pre-release");
88
+
89
+ assert.ok(
90
+ compareVersions(version, "5.29.6") >= 0,
91
+ `protobuf ${version} is below the 5.29.6 patch — inside the "< 5.29.6" affected range`,
92
+ );
93
+ const inSixLine = compareVersions(version, "6.0.0") >= 0;
94
+ if (inSixLine) {
95
+ assert.ok(
96
+ compareVersions(version, "6.33.5") >= 0,
97
+ `protobuf ${version} is inside the ">= 6.30.0rc1, <= 6.33.4" affected range`,
98
+ );
99
+ }
100
+ });
101
+
102
+ test("setuptools is absent from the closure", () => {
103
+ // It was pinned to 80.9.0 only because semgrep 1.97.0's transitive
104
+ // opentelemetry-instrumentation imported pkg_resources at load. 0.58b0 does
105
+ // not, so the package — and its own advisory — leaves the graph entirely.
106
+ assert.equal(
107
+ REQS.has("setuptools"),
108
+ false,
109
+ "setuptools carries its own advisories and is no longer needed; do not re-add it without a stated reason",
110
+ );
111
+ });
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // 2. Drift between the three pin sites
115
+ // ---------------------------------------------------------------------------
116
+
117
+ test("the lockfile, the workflow, and the rules updater pin the same semgrep", () => {
118
+ const lockVersion = REQS.get("semgrep");
119
+ assert.ok(lockVersion, `${LOCKFILE}: semgrep must be pinned`);
120
+
121
+ const workflow = readFileSync(WORKFLOW, "utf8");
122
+ const wf = workflow.match(/SEMGREP_PIN='semgrep==([^']+)'/);
123
+ assert.ok(wf, `${WORKFLOW}: SEMGREP_PIN not found`);
124
+ assert.equal(wf[1], lockVersion, "workflow SEMGREP_PIN disagrees with the lockfile");
125
+
126
+ const updater = readFileSync(UPDATER, "utf8");
127
+ const up = updater.match(/const DEFAULT_SEMGREP_PIN = "semgrep==([^"]+)";/);
128
+ assert.ok(up, `${UPDATER}: DEFAULT_SEMGREP_PIN not found`);
129
+ assert.equal(up[1], lockVersion, "updater DEFAULT_SEMGREP_PIN disagrees with the lockfile");
130
+ });
131
+
132
+ test("the rules updater carries artifact hashes for the version it defaults to", () => {
133
+ // SEMGREP_HASHES is a fail-fast supply-chain guard: a version with no entry
134
+ // is rejected rather than installed unverified. A bump that moved the default
135
+ // without adding digests would turn that guard into a hard stop.
136
+ const updater = readFileSync(UPDATER, "utf8");
137
+ const version = REQS.get("semgrep");
138
+ const map = updater.slice(updater.indexOf("const SEMGREP_HASHES = {"));
139
+ const block = map.slice(0, map.indexOf("\n};"));
140
+ assert.ok(
141
+ block.includes(`"${version}": [`),
142
+ `${UPDATER}: SEMGREP_HASHES has no entry for ${version}`,
143
+ );
144
+ });
145
+
146
+ // ---------------------------------------------------------------------------
147
+ // 3. --require-hashes remains satisfiable
148
+ // ---------------------------------------------------------------------------
149
+
150
+ test("every requirement is == pinned and carries a sha256 hash", () => {
151
+ const pins = lockfile
152
+ .split("\n")
153
+ .filter((l) => /^[A-Za-z0-9._-]+==/.test(l.trim())).length;
154
+ const hashes = lockfile.split("\n").filter((l) => l.trim().startsWith("--hash=sha256:")).length;
155
+
156
+ assert.ok(pins > 0, "expected at least one pinned requirement");
157
+ assert.equal(REQS.size, pins, "every pinned line must parse to a requirement");
158
+ assert.ok(
159
+ hashes >= pins,
160
+ `${hashes} hash line(s) for ${pins} requirement(s) — --require-hashes needs at least one each`,
161
+ );
162
+ });
163
+
164
+ test("no requirement is pinned with a loose operator", () => {
165
+ // `--require-hashes` rejects these at install time; catching it here names
166
+ // the offending line instead of failing inside CI's pip.
167
+ //
168
+ // The operator is matched by string comparison, NOT by a regex alternating
169
+ // `<` and `>`. CodeQL reads such a pattern as an attempted HTML-tag filter
170
+ // and raises js/bad-tag-filter at HIGH — which blocks the merge, since
171
+ // code-scanning gates on high. Comparing prefixes says the same thing with
172
+ // nothing for that query to match on.
173
+ const LOOSE_OPERATORS = [">=", "<=", "~=", "!=", ">", "<"];
174
+ for (const line of lockfile.split("\n")) {
175
+ const trimmed = line.trim();
176
+ if (trimmed === "" || trimmed.startsWith("#") || trimmed.startsWith("--hash")) continue;
177
+ const name = trimmed.match(/^[A-Za-z0-9._-]+/);
178
+ if (!name) continue;
179
+ const operator = trimmed.slice(name[0].length).trimStart();
180
+ for (const loose of LOOSE_OPERATORS) {
181
+ assert.ok(
182
+ !operator.startsWith(loose),
183
+ `loose pin (${loose}) — --require-hashes needs an exact ==: ${trimmed}`,
184
+ );
185
+ }
186
+ }
187
+ });
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // 4. The regeneration trap
191
+ // ---------------------------------------------------------------------------
192
+
193
+ test("the header warns about the manylinux_2_34 wheel tag", () => {
194
+ // semgrep moved its Linux wheel tag after 1.157.0. A `pip download` whose
195
+ // --platform list omits the new tag resolves NOTHING newer and reports only
196
+ // "No matching distribution found", never naming the tag as the cause.
197
+ const header = lockfile.slice(0, lockfile.indexOf("\n\n\n") + 1 || 4000);
198
+ assert.match(header, /manylinux_2_34_x86_64/, "the header must name the current wheel tag");
199
+ assert.match(header, /1\.157\.0/, "the header must say which version the tag changed after");
200
+ assert.match(
201
+ lockfile,
202
+ /pip download semgrep/,
203
+ "the header must carry a regeneration command",
204
+ );
205
+ });
@@ -1,24 +1,51 @@
1
1
  # Hash-pinned lockfile for the pr-quality SAST (semgrep) step.
2
2
  #
3
3
  # Target platform : Linux x86_64 / CPython 3.12 (GitHub Actions ubuntu-latest)
4
- # Tool : semgrep 1.97.0 (+ complete transitive closure)
4
+ # Tool : semgrep 1.176.1 (+ complete transitive closure)
5
5
  # Consumed via : pip install --require-hashes -r scripts/semgrep-requirements.txt
6
6
  #
7
7
  # Every requirement below is pinned with `==` and carries at least one
8
8
  # sha256 hash, as `--require-hashes` demands. The closure was resolved for
9
9
  # the linux/cp312 target specifically (via `pip download` with explicit
10
- # --platform manylinux/musllinux + --python-version 3.12 --abi cp312), NOT
11
- # from the local interpreter, so the native wheels (semgrep, protobuf,
12
- # rpds-py, wrapt, charset-normalizer, ruamel.yaml.clib) are the linux x86_64
13
- # cp312-compatible artifacts.
10
+ # --platform manylinux + --python-version 3.12 --abi cp312), NOT from the
11
+ # local interpreter, so the native wheels (semgrep, protobuf, rpds-py, wrapt,
12
+ # charset-normalizer, ruamel.yaml.clib) are the linux x86_64 cp312-compatible
13
+ # artifacts.
14
14
  #
15
- # setuptools is intentionally included: the workflow installs it alongside
16
- # semgrep so opentelemetry's transitive `pkg_resources` import works on
17
- # Python >=3.12 (which no longer ships setuptools by default).
15
+ # WHEEL TAG — read before regenerating. semgrep moved its Linux wheel tag from
16
+ # `manylinux2014_x86_64` to `manylinux_2_34_x86_64` after 1.157.0. A
17
+ # `pip download` whose --platform list omits `manylinux_2_34_x86_64` silently
18
+ # resolves NOTHING newer than 1.157.0 and reports it as "no matching
19
+ # distribution" — it does not say the tag is the reason. glibc 2.34 is
20
+ # satisfied by ubuntu-latest (2.39) and by 22.04 (2.35). Keep the older tags
21
+ # listed too: the rest of the closure still ships manylinux2014 wheels
22
+ # (protobuf among them).
18
23
  #
19
- # Regenerate with `pip download semgrep==<ver> setuptools --only-binary=:all:
20
- # --platform manylinux2014_x86_64 --platform manylinux_2_17_x86_64
21
- # --platform any --python-version 3.12 --implementation cp --abi cp312`.
24
+ # setuptools is NO LONGER included, and its absence is deliberate. It used to
25
+ # be pinned to 80.9.0 because semgrep 1.97.0's transitive
26
+ # `opentelemetry-instrumentation==0.46b0` imported `pkg_resources` at load, and
27
+ # a py3.12 venv seeds no setuptools. `opentelemetry-instrumentation` 0.58b0
28
+ # (pulled by semgrep 1.176.1) no longer imports it, verified by installing this
29
+ # closure with --require-hashes in a clean python:3.12 container and running a
30
+ # real scan. Do not re-add setuptools without a reason: 80.9.0 was the last
31
+ # release shipping pkg_resources and it carries its own advisories.
32
+ #
33
+ # Regenerate with:
34
+ # pip download semgrep==<ver> \
35
+ # --only-binary=:all: \
36
+ # --platform manylinux_2_34_x86_64 \
37
+ # --platform manylinux2014_x86_64 --platform manylinux_2_17_x86_64 \
38
+ # --platform any \
39
+ # --python-version 3.12 --implementation cp --abi cp312 -d <dir>
40
+ # then emit `<Name>==<Version>` + the wheel's sha256 for each artifact, reading
41
+ # Name/Version from each wheel's own dist-info METADATA rather than parsing
42
+ # filenames (they normalize dots and hyphens differently).
43
+
44
+ annotated-types==0.8.0 \
45
+ --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0
46
+
47
+ anyio==4.15.1 \
48
+ --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101
22
49
 
23
50
  attrs==26.1.0 \
24
51
  --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309
@@ -26,14 +53,17 @@ attrs==26.1.0 \
26
53
  boltons==21.0.0 \
27
54
  --hash=sha256:b9bb7b58b2b420bbe11a6025fdef6d3e5edc9f76a42fb467afe7ca212ef9948b
28
55
 
29
- bracex==3.0 \
30
- --hash=sha256:3833e61c2f092d5aa0468fa2e6c6e990a306185abf763b6d122f0158e59c58a5
56
+ bracex==3.0.1 \
57
+ --hash=sha256:6523ad83aeb5098a4ee597cff0f964442ff74e460bd3fafaffab6a013ff2288c
58
+
59
+ certifi==2026.7.22 \
60
+ --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775
31
61
 
32
- certifi==2026.6.17 \
33
- --hash=sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db
62
+ cffi==2.1.1 \
63
+ --hash=sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf
34
64
 
35
- charset-normalizer==3.4.7 \
36
- --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116
65
+ charset-normalizer==3.5.1 \
66
+ --hash=sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08
37
67
 
38
68
  click==8.4.2 \
39
69
  --hash=sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76
@@ -44,11 +74,8 @@ click-option-group==0.5.9 \
44
74
  colorama==0.4.6 \
45
75
  --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
46
76
 
47
- defusedxml==0.7.1 \
48
- --hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61
49
-
50
- deprecated==1.3.1 \
51
- --hash=sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f
77
+ cryptography==50.0.1 \
78
+ --hash=sha256:51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a
52
79
 
53
80
  exceptiongroup==1.2.2 \
54
81
  --hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b
@@ -56,20 +83,32 @@ exceptiongroup==1.2.2 \
56
83
  face==26.0.1 \
57
84
  --hash=sha256:ab0a83c37c9789dce658a67a9a80eafaa113c9ec37c5a9d950ff5480542a062d
58
85
 
59
- glom==22.1.0 \
60
- --hash=sha256:5339da206bf3532e01a83a35aca202960ea885156986d190574b779598e9e772
86
+ glom==25.12.0 \
87
+ --hash=sha256:b9f21e77f71a6576a43864e85066b8cc3f0f778d0d50961563f8981377a6dcb1
88
+
89
+ googleapis-common-protos==1.75.3 \
90
+ --hash=sha256:a018d2bf098ca9fb6faa08d5bb780e2a2c2f73c566f069761331386c9596d3f2
91
+
92
+ h11==0.16.0 \
93
+ --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
94
+
95
+ httpcore==1.0.9 \
96
+ --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55
61
97
 
62
- googleapis-common-protos==1.75.0 \
63
- --hash=sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed
98
+ httpx==0.28.1 \
99
+ --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
64
100
 
65
- idna==3.18 \
66
- --hash=sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2
101
+ httpx-sse==0.4.3 \
102
+ --hash=sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc
67
103
 
68
- importlib-metadata==7.1.0 \
69
- --hash=sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570
104
+ idna==3.19 \
105
+ --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4
70
106
 
71
- jsonschema==4.26.0 \
72
- --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce
107
+ importlib_metadata==8.7.1 \
108
+ --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151
109
+
110
+ jsonschema==4.25.1 \
111
+ --hash=sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63
73
112
 
74
113
  jsonschema-specifications==2025.9.1 \
75
114
  --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe
@@ -77,47 +116,74 @@ jsonschema-specifications==2025.9.1 \
77
116
  markdown-it-py==4.2.0 \
78
117
  --hash=sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a
79
118
 
119
+ mcp==1.29.0 \
120
+ --hash=sha256:f5a075bb611f23d6f4d080c6a1699fa62772eebc562ba9e66b306ddde1c755f7
121
+
80
122
  mdurl==0.1.2 \
81
123
  --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8
82
124
 
83
- opentelemetry-api==1.25.0 \
84
- --hash=sha256:757fa1aa020a0f8fa139f8959e53dec2051cc26b832e76fa839a6d76ecefd737
125
+ opentelemetry-api==1.37.0 \
126
+ --hash=sha256:accf2024d3e89faec14302213bc39550ec0f4095d1cf5ca688e1bfb1c8612f47
127
+
128
+ opentelemetry-exporter-otlp-proto-common==1.37.0 \
129
+ --hash=sha256:53038428449c559b0c564b8d718df3314da387109c4d36bd1b94c9a641b0292e
85
130
 
86
- opentelemetry-exporter-otlp-proto-common==1.25.0 \
87
- --hash=sha256:15637b7d580c2675f70246563363775b4e6de947871e01d0f4e3881d1848d693
131
+ opentelemetry-exporter-otlp-proto-http==1.37.0 \
132
+ --hash=sha256:54c42b39945a6cc9d9a2a33decb876eabb9547e0dcb49df090122773447f1aef
88
133
 
89
- opentelemetry-exporter-otlp-proto-http==1.25.0 \
90
- --hash=sha256:2eca686ee11b27acd28198b3ea5e5863a53d1266b91cda47c839d95d5e0541a6
134
+ opentelemetry-instrumentation==0.58b0 \
135
+ --hash=sha256:50f97ac03100676c9f7fc28197f8240c7290ca1baa12da8bfbb9a1de4f34cc45
91
136
 
92
- opentelemetry-instrumentation==0.46b0 \
93
- --hash=sha256:89cd721b9c18c014ca848ccd11181e6b3fd3f6c7669e35d59c48dc527408c18b
137
+ opentelemetry-instrumentation-requests==0.58b0 \
138
+ --hash=sha256:672a0be0bb5b52bea0c11820b35e27edcf4cd22d34abe4afc59a92a80519f8a8
94
139
 
95
- opentelemetry-instrumentation-requests==0.46b0 \
96
- --hash=sha256:a8c2472800d8686f3f286cd524b8746b386154092e85a791ba14110d1acc9b81
140
+ opentelemetry-instrumentation-threading==0.58b0 \
141
+ --hash=sha256:eacc072881006aceb5b9b6831bcdce718c67ef6f31ac0b32bd6a23a94d979b4a
97
142
 
98
- opentelemetry-proto==1.25.0 \
99
- --hash=sha256:f07e3341c78d835d9b86665903b199893befa5e98866f63d22b00d0b7ca4972f
143
+ opentelemetry-proto==1.37.0 \
144
+ --hash=sha256:8ed8c066ae8828bbf0c39229979bdf583a126981142378a9cbe9d6fd5701c6e2
100
145
 
101
- opentelemetry-sdk==1.25.0 \
102
- --hash=sha256:d97ff7ec4b351692e9d5a15af570c693b8715ad78b8aafbec5c7100fe966b4c9
146
+ opentelemetry-sdk==1.37.0 \
147
+ --hash=sha256:8f3c3c22063e52475c5dbced7209495c2c16723d016d39287dfc215d1771257c
103
148
 
104
- opentelemetry-semantic-conventions==0.46b0 \
105
- --hash=sha256:6daef4ef9fa51d51855d9f8e0ccd3a1bd59e0e545abe99ac6203804e36ab3e07
149
+ opentelemetry-semantic-conventions==0.58b0 \
150
+ --hash=sha256:5564905ab1458b96684db1340232729fce3b5375a06e140e8904c78e4f815b28
106
151
 
107
- opentelemetry-util-http==0.46b0 \
108
- --hash=sha256:8dc1949ce63caef08db84ae977fdc1848fe6dc38e6bbaad0ae3e6ecd0d451629
152
+ opentelemetry-util-http==0.58b0 \
153
+ --hash=sha256:6c6b86762ed43025fbd593dc5f700ba0aa3e09711aedc36fd48a13b23d8cb1e7
109
154
 
110
- packaging==26.2 \
111
- --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e
155
+ packaging==26.3 \
156
+ --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c
112
157
 
113
158
  peewee==3.19.0 \
114
159
  --hash=sha256:de220b94766e6008c466e00ce4ba5299b9a832117d9eb36d45d0062f3cfd7417
115
160
 
116
- protobuf==4.25.9 \
117
- --hash=sha256:438c636de8fb706a0de94a12a268ef1ae8f5ba5ae655a7671fcda5968ba3c9be
161
+ protobuf==6.33.6 \
162
+ --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593
163
+
164
+ pycparser==3.0 \
165
+ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992
166
+
167
+ pydantic==2.13.5 \
168
+ --hash=sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73
118
169
 
119
- pygments==2.20.0 \
120
- --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
170
+ pydantic-settings==2.15.0 \
171
+ --hash=sha256:0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42
172
+
173
+ pydantic_core==2.46.5 \
174
+ --hash=sha256:0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2
175
+
176
+ Pygments==2.21.0 \
177
+ --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9
178
+
179
+ PyJWT==2.13.0 \
180
+ --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728
181
+
182
+ python-dotenv==1.2.3 \
183
+ --hash=sha256:904552145e8bfed22162c09dab1c2b9b54fefa7b23ba780f4f26ca0316b0f0d9
184
+
185
+ python-multipart==0.0.32 \
186
+ --hash=sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23
121
187
 
122
188
  referencing==0.37.0 \
123
189
  --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231
@@ -125,40 +191,45 @@ referencing==0.37.0 \
125
191
  requests==2.34.2 \
126
192
  --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0
127
193
 
128
- rich==13.5.3 \
129
- --hash=sha256:9257b468badc3d347e146a4faa268ff229039d4c2d176ab0cffb4c4fbc73d5d9
194
+ rich==15.0.0 \
195
+ --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb
130
196
 
131
197
  rpds-py==2026.6.3 \
132
198
  --hash=sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6
133
199
 
134
- ruamel.yaml==0.17.40 \
135
- --hash=sha256:b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c
200
+ ruamel.yaml==0.19.1 \
201
+ --hash=sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93
136
202
 
137
203
  ruamel.yaml.clib==0.2.15 \
138
204
  --hash=sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9
139
205
 
140
- semgrep==1.97.0 \
141
- --hash=sha256:996fe0b2bfac3a4d4511e470fdf5f3bca96b1f794f398e0336c8388802c218de
206
+ semantic-version==2.10.0 \
207
+ --hash=sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177
142
208
 
143
- # PINNED to 80.9.0 (the last release that still SHIPS `pkg_resources`):
144
- # setuptools 81+ removed the vendored `pkg_resources` module, but
145
- # semgrep 1.97.0's transitive `opentelemetry-instrumentation==0.46b0` imports
146
- # it at load. A py3.12 venv seeds no setuptools, so the lockfile must supply a
147
- # pkg_resources-bearing one — 83.0.0 broke the SAST step at runtime with
148
- # `ModuleNotFoundError: No module named 'pkg_resources'`. Do NOT bump past 80.x
149
- # without confirming pkg_resources is present (or bumping opentelemetry off it).
150
- setuptools==80.9.0 \
151
- --hash=sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922
209
+ semgrep==1.176.1 \
210
+ --hash=sha256:00bc0f167564443d7ce4ad7a896d2251ed3c5c17e0b176de9b39bd0fb6abc2a9
152
211
 
153
- tomli==2.0.2 \
154
- --hash=sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38
212
+ sse-starlette==3.4.11 \
213
+ --hash=sha256:c7b2244bdff016fe7f64e10075e89a3e6bbf899649cc89b0fe884b5545042453
155
214
 
156
- typing-extensions==4.16.0 \
215
+ starlette==1.6.0 \
216
+ --hash=sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c
217
+
218
+ tomli==2.4.1 \
219
+ --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5
220
+
221
+ typing-inspection==0.4.4 \
222
+ --hash=sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147
223
+
224
+ typing_extensions==4.16.0 \
157
225
  --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8
158
226
 
159
227
  urllib3==2.7.0 \
160
228
  --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897
161
229
 
230
+ uvicorn==0.52.4 \
231
+ --hash=sha256:f86e41a149d7d05a9969337e3946a9c171c06a5d42680896daaba624aeac8da1
232
+
162
233
  wcmatch==8.5.2 \
163
234
  --hash=sha256:17d3ad3758f9d0b5b4dedc770b65420d4dac62e680229c287bf24c9db856a478
164
235
 
@@ -167,3 +238,4 @@ wrapt==1.17.3 \
167
238
 
168
239
  zipp==4.1.0 \
169
240
  --hash=sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f
241
+
@@ -47,7 +47,7 @@
47
47
  *
48
48
  * Usage:
49
49
  * node scripts/update-semgrep-rules.mjs
50
- * node scripts/update-semgrep-rules.mjs --semgrep-pin semgrep==1.97.0
50
+ * node scripts/update-semgrep-rules.mjs --semgrep-pin semgrep==1.176.1
51
51
  * node scripts/update-semgrep-rules.mjs --out .semgrep/rules.json --dry-run
52
52
  *
53
53
  * Requires network egress to PyPI (to install the pinned `semgrep` package,
@@ -77,7 +77,7 @@ const REPO_ROOT = resolve(__dirname, "..");
77
77
  // resolved AGAINST this exact Semgrep version's registry-resolution logic,
78
78
  // so scanning with a different installed version than the one used to
79
79
  // generate the file is a (harmless but inconsistent) version skew.
80
- const DEFAULT_SEMGREP_PIN = "semgrep==1.97.0";
80
+ const DEFAULT_SEMGREP_PIN = "semgrep==1.176.1";
81
81
 
82
82
  // SHA-256 hashes for every `DEFAULT_SEMGREP_PIN` distribution published on
83
83
  // PyPI (the four platform wheels + the sdist). pip's `--require-hashes` mode
@@ -90,6 +90,19 @@ const DEFAULT_SEMGREP_PIN = "semgrep==1.97.0";
90
90
  // values) — a version with no hash entry here fails fast rather than
91
91
  // installing unverified.
92
92
  const SEMGREP_HASHES = {
93
+ "1.176.1": [
94
+ "sha256:e1f78275f13c11bd9b6af1143befd79d333d948bd4af96768be3494ab342d8f3",
95
+ "sha256:ce35dc0b9c34bb95e16f487699646a461e9b2aceda073e773526e0836dbf73ba",
96
+ "sha256:9baed01491cbe1de00f862f73349bf1c9045598bae2923f6a19ef453b3092015",
97
+ "sha256:00bc0f167564443d7ce4ad7a896d2251ed3c5c17e0b176de9b39bd0fb6abc2a9",
98
+ "sha256:2214b71919b825766844ce3dc8c868965d98c3109628882357461ba2a1b8def7",
99
+ "sha256:5f1127deb8df4e671bae2ec6c811cb069df29fae66bf4880bdad135f38629b23",
100
+ "sha256:5ca9822b9b8d645b6f078160139b966bd66a3829b87c4e63e1bb5f8d7f71732f",
101
+ "sha256:670e2dc84cc9b7a3b42e42e0b7137b06638cad49cb3166a57377257137d21bad",
102
+ ],
103
+ // Retained so an explicit `--semgrep-pin semgrep==1.97.0` still verifies
104
+ // rather than failing the fast-fail guard. Superseded as the default by
105
+ // 1.176.1 (Story #477).
93
106
  "1.97.0": [
94
107
  "sha256:0ddaa25ee45e669e1fef87e88dcef73b2aee0874b507e09f618862c42452a205",
95
108
  "sha256:9184500bf8c49ad19d0fb2d84923abb4aa53058b0ece7008b57a3b0b5e6ce3ee",