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.
- package/README.md +14 -5
- package/package.json +4 -1
- package/scripts/audit-check.mjs +345 -106
- package/scripts/audit-check.test.mjs +208 -3
- package/scripts/check-advisory-scan-setup.test.mjs +247 -0
- package/scripts/check-coverage-threshold.mjs +252 -23
- package/scripts/check-coverage-threshold.test.mjs +292 -5
- package/scripts/check-semgrep-lockfile.test.mjs +205 -0
- package/scripts/semgrep-requirements.txt +146 -74
- package/scripts/update-semgrep-rules.mjs +15 -2
|
@@ -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.
|
|
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
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
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
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
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
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
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:
|
|
56
|
+
bracex==3.0.1 \
|
|
57
|
+
--hash=sha256:6523ad83aeb5098a4ee597cff0f964442ff74e460bd3fafaffab6a013ff2288c
|
|
58
|
+
|
|
59
|
+
certifi==2026.7.22 \
|
|
60
|
+
--hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775
|
|
31
61
|
|
|
32
|
-
|
|
33
|
-
--hash=sha256:
|
|
62
|
+
cffi==2.1.1 \
|
|
63
|
+
--hash=sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf
|
|
34
64
|
|
|
35
|
-
charset-normalizer==3.
|
|
36
|
-
--hash=sha256:
|
|
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
|
-
|
|
48
|
-
--hash=sha256:
|
|
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==
|
|
60
|
-
--hash=sha256:
|
|
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
|
-
|
|
63
|
-
--hash=sha256:
|
|
98
|
+
httpx==0.28.1 \
|
|
99
|
+
--hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
|
|
64
100
|
|
|
65
|
-
|
|
66
|
-
--hash=sha256:
|
|
101
|
+
httpx-sse==0.4.3 \
|
|
102
|
+
--hash=sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc
|
|
67
103
|
|
|
68
|
-
|
|
69
|
-
--hash=sha256:
|
|
104
|
+
idna==3.19 \
|
|
105
|
+
--hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4
|
|
70
106
|
|
|
71
|
-
|
|
72
|
-
--hash=sha256:
|
|
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.
|
|
84
|
-
--hash=sha256:
|
|
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-
|
|
87
|
-
--hash=sha256:
|
|
131
|
+
opentelemetry-exporter-otlp-proto-http==1.37.0 \
|
|
132
|
+
--hash=sha256:54c42b39945a6cc9d9a2a33decb876eabb9547e0dcb49df090122773447f1aef
|
|
88
133
|
|
|
89
|
-
opentelemetry-
|
|
90
|
-
--hash=sha256:
|
|
134
|
+
opentelemetry-instrumentation==0.58b0 \
|
|
135
|
+
--hash=sha256:50f97ac03100676c9f7fc28197f8240c7290ca1baa12da8bfbb9a1de4f34cc45
|
|
91
136
|
|
|
92
|
-
opentelemetry-instrumentation==0.
|
|
93
|
-
--hash=sha256:
|
|
137
|
+
opentelemetry-instrumentation-requests==0.58b0 \
|
|
138
|
+
--hash=sha256:672a0be0bb5b52bea0c11820b35e27edcf4cd22d34abe4afc59a92a80519f8a8
|
|
94
139
|
|
|
95
|
-
opentelemetry-instrumentation-
|
|
96
|
-
--hash=sha256:
|
|
140
|
+
opentelemetry-instrumentation-threading==0.58b0 \
|
|
141
|
+
--hash=sha256:eacc072881006aceb5b9b6831bcdce718c67ef6f31ac0b32bd6a23a94d979b4a
|
|
97
142
|
|
|
98
|
-
opentelemetry-proto==1.
|
|
99
|
-
--hash=sha256:
|
|
143
|
+
opentelemetry-proto==1.37.0 \
|
|
144
|
+
--hash=sha256:8ed8c066ae8828bbf0c39229979bdf583a126981142378a9cbe9d6fd5701c6e2
|
|
100
145
|
|
|
101
|
-
opentelemetry-sdk==1.
|
|
102
|
-
--hash=sha256:
|
|
146
|
+
opentelemetry-sdk==1.37.0 \
|
|
147
|
+
--hash=sha256:8f3c3c22063e52475c5dbced7209495c2c16723d016d39287dfc215d1771257c
|
|
103
148
|
|
|
104
|
-
opentelemetry-semantic-conventions==0.
|
|
105
|
-
--hash=sha256:
|
|
149
|
+
opentelemetry-semantic-conventions==0.58b0 \
|
|
150
|
+
--hash=sha256:5564905ab1458b96684db1340232729fce3b5375a06e140e8904c78e4f815b28
|
|
106
151
|
|
|
107
|
-
opentelemetry-util-http==0.
|
|
108
|
-
--hash=sha256:
|
|
152
|
+
opentelemetry-util-http==0.58b0 \
|
|
153
|
+
--hash=sha256:6c6b86762ed43025fbd593dc5f700ba0aa3e09711aedc36fd48a13b23d8cb1e7
|
|
109
154
|
|
|
110
|
-
packaging==26.
|
|
111
|
-
--hash=sha256:
|
|
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==
|
|
117
|
-
--hash=sha256:
|
|
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
|
-
|
|
120
|
-
--hash=sha256:
|
|
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==
|
|
129
|
-
--hash=sha256:
|
|
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.
|
|
135
|
-
--hash=sha256:
|
|
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
|
-
|
|
141
|
-
--hash=sha256:
|
|
206
|
+
semantic-version==2.10.0 \
|
|
207
|
+
--hash=sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177
|
|
142
208
|
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
154
|
-
--hash=sha256:
|
|
212
|
+
sse-starlette==3.4.11 \
|
|
213
|
+
--hash=sha256:c7b2244bdff016fe7f64e10075e89a3e6bbf899649cc89b0fe884b5545042453
|
|
155
214
|
|
|
156
|
-
|
|
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.
|
|
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.
|
|
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",
|