circle-ir 3.177.0 → 3.179.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,214 @@
1
+ /**
2
+ * Dependency-manifest helpers for the deserialization-safety-gate pass.
3
+ *
4
+ * cognium-dev #258 — dependency-version-aware sink gating. The engine
5
+ * never reads the filesystem (Pillar I / browser-safety); the caller
6
+ * (cognium-dev CLI or `analyzeProject`) reads the manifest and passes
7
+ * it as raw text via `AnalyzerOptions.dependencyContext`. Helpers here
8
+ * turn that raw text into the boolean predicates the gate needs.
9
+ *
10
+ * All parsers here are string-scoped: regex over the raw manifest
11
+ * rather than a full XML/JSON tree. That is deliberate — the gate only
12
+ * needs a handful of narrow signals (Fastjson version, presence of a
13
+ * safe classifier), and every existing runtime dep in circle-ir is
14
+ * either `web-tree-sitter` or `yaml`. Pulling in an XML parser purely
15
+ * for one Fastjson property would violate the minimal-dependencies
16
+ * guardrail.
17
+ */
18
+ /**
19
+ * Result of extracting the effective Fastjson coordinate from a
20
+ * `pom.xml` string. `version` is the raw value as declared (including
21
+ * any classifier suffix); `noneAutotype` is true when the version
22
+ * literally matches Alibaba's `_noneautotype` hardened build family
23
+ * (any patch level, e.g. `1.2.83_noneautotype`, `1.2.85_noneautotype`).
24
+ */
25
+ export interface FastjsonPomResolution {
26
+ version: string;
27
+ noneAutotype: boolean;
28
+ }
29
+ /**
30
+ * Extract the effective Fastjson version from a `pom.xml`. Two sources
31
+ * are consulted:
32
+ *
33
+ * 1. A `<properties>` entry named exactly `<fastjson.version>` — the
34
+ * idiomatic way Maven projects centralise a dep version they
35
+ * reference in a `<dependencies>` block. This is where
36
+ * alibaba/Sentinel pins `1.2.83_noneautotype`.
37
+ * 2. A `<dependency>` block whose `<groupId>` is `com.alibaba` and
38
+ * `<artifactId>` is `fastjson` — read as a fallback when the
39
+ * version is declared inline rather than as a property.
40
+ *
41
+ * Returns `null` when no Fastjson coordinate can be resolved from the
42
+ * pom (no properties entry AND no matching dependency block, or the
43
+ * dependency uses a `${...}` reference the properties block doesn't
44
+ * define).
45
+ */
46
+ export declare function resolveFastjsonFromPom(pomXml: string): FastjsonPomResolution | null;
47
+ /**
48
+ * Extract the effective Fastjson version from a Gradle build script
49
+ * (`build.gradle` Groovy DSL or `build.gradle.kts` Kotlin DSL).
50
+ *
51
+ * cognium-dev #261 (Gradle-first slice extending #258's pom.xml gate).
52
+ *
53
+ * Recognises three declaration shapes:
54
+ *
55
+ * 1. Direct literal — the classic Groovy / Kotlin single-string form:
56
+ * implementation 'com.alibaba:fastjson:1.2.83_noneautotype'
57
+ * implementation "com.alibaba:fastjson:1.2.83_noneautotype"
58
+ * implementation("com.alibaba:fastjson:1.2.83_noneautotype")
59
+ * Also `api`, `compile`, `runtimeOnly`, `testImplementation`, … — the
60
+ * configuration keyword is not part of the regex; only the
61
+ * `group:artifact:version` triple is matched, so any dependency-
62
+ * configuration prefix works.
63
+ *
64
+ * 2. Groovy interpolation — `implementation "com.alibaba:fastjson:${fastjsonVersion}"`
65
+ * with the property defined via `ext { fastjsonVersion = '1.2.83_noneautotype' }`,
66
+ * `def fastjsonVersion = '1.2.83_noneautotype'`, or top-level `fastjsonVersion = '…'`.
67
+ *
68
+ * 3. Kotlin interpolation — `implementation("com.alibaba:fastjson:$fastjsonVersion")`
69
+ * with the property defined via `val fastjsonVersion = "1.2.83_noneautotype"`
70
+ * or `const val fastjsonVersion = "…"`.
71
+ *
72
+ * Returns `null` on miss (no direct declaration AND no resolvable
73
+ * property reference). The `DeserializationSafetyGatePass` then falls
74
+ * through to its default "do not drop" behaviour on the sink.
75
+ *
76
+ * NOT recognised in this MVP (deferred, follow-ups on #261):
77
+ * - `platform(...)` / `enforcedPlatform(...)` BOM version imports
78
+ * - Version-catalog `libs.versions.toml` references (`libs.fastjson`)
79
+ * - `constraints { }` block versions
80
+ * - `subprojects { }` / `allprojects { }` conditional declarations
81
+ */
82
+ export declare function resolveFastjsonFromGradle(buildGradle: string): FastjsonPomResolution | null;
83
+ /**
84
+ * Result of extracting the effective PyYAML version. `safeByDefault` is
85
+ * true when the parsed version is ≥ 6.0; at that point pyyaml.load()
86
+ * without an explicit `Loader=` keyword argument raises TypeError
87
+ * instead of silently invoking the unsafe default Loader.
88
+ *
89
+ * See https://github.com/yaml/pyyaml/blob/master/CHANGES for the full
90
+ * 6.0 breakage story. The gate consumer must ALSO check the call site
91
+ * for an explicit `Loader=` keyword (fileHasUnsafePyYamlLoader below)
92
+ * — a caller under pyyaml ≥ 6.0 that explicitly passes
93
+ * `Loader=yaml.Loader` or `Loader=yaml.UnsafeLoader` is still
94
+ * dangerous regardless of the version pin.
95
+ */
96
+ export interface PyYamlResolution {
97
+ version: string;
98
+ safeByDefault: boolean;
99
+ }
100
+ /**
101
+ * Extract the effective PyYAML version from a `requirements.txt` file.
102
+ * Recognises the standard PEP 508 shapes (`==`, `>=`, `~=`, `>`,
103
+ * exact pins with trailing modifiers). Package name matched
104
+ * case-insensitively (both `PyYAML` and `pyyaml` are common in the
105
+ * wild).
106
+ *
107
+ * Returns null when no pyyaml line is found or the version string
108
+ * cannot be parsed as `M.m[.p]`. Non-strict on trailing environment
109
+ * markers (` ; python_version >= '3.6'`), which are common on
110
+ * requirements.txt lines.
111
+ */
112
+ export declare function resolvePyYamlFromRequirements(requirementsTxt: string): PyYamlResolution | null;
113
+ /**
114
+ * Extract the effective PyYAML version from a `pyproject.toml`.
115
+ * Recognises the common Poetry and PEP 621 shapes:
116
+ *
117
+ * Poetry — under `[tool.poetry.dependencies]`:
118
+ * PyYAML = "6.0"
119
+ * pyyaml = "^6.0.1"
120
+ * pyyaml = { version = "6.0", extras = ["..."] }
121
+ *
122
+ * PEP 621 — under `[project]` `dependencies` array:
123
+ * dependencies = [ "PyYAML>=6.0", ... ]
124
+ *
125
+ * As with the requirements.txt resolver, non-strict on markers /
126
+ * modifiers; regex-based rather than a full TOML parser to keep the
127
+ * runtime-dep list minimal (Pillar I minimal-dependencies principle).
128
+ */
129
+ export declare function resolvePyYamlFromPyproject(pyprojectToml: string): PyYamlResolution | null;
130
+ /**
131
+ * True when `version` (as `M.m[.p]`) is ≥ 6.0. Handles bare `6`,
132
+ * `6.0`, `6.0.1`, `7`, `10.0`, etc. Returns false on malformed input
133
+ * (defensive — the gate defaults to *do not drop* on missing signal).
134
+ */
135
+ export declare function isPyYamlVersionSafeByDefault(version: string): boolean;
136
+ /**
137
+ * Return true when the file contains an explicit unsafe `Loader=`
138
+ * keyword argument on a `yaml.load(...)` call at or shortly after
139
+ * `sinkLine`. Recognises the well-known unsafe loaders and their
140
+ * qualified forms:
141
+ *
142
+ * Loader=Loader Loader=yaml.Loader
143
+ * Loader=UnsafeLoader Loader=yaml.UnsafeLoader
144
+ * Loader=FullLoader Loader=yaml.FullLoader (technically safer
145
+ * than Loader; still
146
+ * permits arbitrary
147
+ * Python object types)
148
+ *
149
+ * SafeLoader / CSafeLoader / BaseLoader / CBaseLoader are all safe and
150
+ * are NOT matched here.
151
+ *
152
+ * Scans `sinkLine` through the next 9 lines to catch multi-line call
153
+ * shapes; if the closing `)` appears before the window ends the scan
154
+ * stops there. Regex-based (no AST inspection) to stay consistent with
155
+ * the sink-filter-pass conventions and avoid pulling parse state into
156
+ * the gate.
157
+ */
158
+ export declare function fileHasUnsafePyYamlLoader(sourceLines: string[], sinkLine: number): boolean;
159
+ /**
160
+ * Return true when the given source text contains an in-file call that
161
+ * re-enables Fastjson autotype. Even a `_noneautotype` build does not
162
+ * protect against code that programmatically re-enables the feature
163
+ * (which the hardened build documents as impossible, but the classifier
164
+ * is a build-time strip, not a runtime lock). Defense-in-depth for the
165
+ * `resolveFastjsonFromPom` gate.
166
+ */
167
+ export declare function fileReenablesFastjsonAutotype(source: string): boolean;
168
+ /**
169
+ * Return true when the given Java source contains a call that enables
170
+ * Jackson polymorphic type handling — either the legacy
171
+ * `enableDefaultTyping(...)` (deprecated in Jackson 2.10+ but still
172
+ * shipped) or the current `activateDefaultTyping(...)`.
173
+ *
174
+ * When neither is present in the file (and no `@JsonTypeInfo` is used
175
+ * on the target type — best-effort scan below), Jackson's default
176
+ * behaviour since 2.10 is safe: `ObjectMapper.readValue(json,
177
+ * targetType)` cannot instantiate arbitrary classes. The gate uses
178
+ * this to distinguish a genuine `readValue` sink from a safely-
179
+ * configured one.
180
+ *
181
+ * `@JsonTypeInfo` scan is intentionally file-local. A `@JsonTypeInfo`
182
+ * annotation on a target type in a different file would still allow
183
+ * polymorphic construction, but the engine treats that as unknown
184
+ * risk and preserves the sink (the gate only fires when the *file
185
+ * itself* provides positive evidence of a safe configuration).
186
+ */
187
+ export declare function fileEnablesJacksonPolymorphism(source: string): boolean;
188
+ /**
189
+ * Return true when the given Java source contains a
190
+ * `new Yaml(new SafeConstructor(...))` or an equivalent hardened
191
+ * SnakeYAML constructor. When any `Yaml` instance in the file is
192
+ * built with the safe constructor family, we assume the file's
193
+ * `Yaml.load(...)` calls are safely configured; the gate then drops
194
+ * the deserialization sink for those calls.
195
+ *
196
+ * Recognised safe constructor classes (SnakeYAML 1.x + 2.x):
197
+ * - SafeConstructor — canonical safe loader
198
+ * - SafeSchema (rare, YAML 1.2) — safe schema-based loader
199
+ *
200
+ * Recognised safe factory calls (SnakeYAML 2.x LoaderOptions API):
201
+ * - Yaml.load() with a Constructor that extends SafeConstructor
202
+ * (heuristic: any `SafeConstructor`-typed variable is safe)
203
+ *
204
+ * NOTE: A file that mixes `new Yaml(new SafeConstructor())` with
205
+ * `new Yaml(new Constructor(SomeClass.class))` on separate call sites
206
+ * would over-suppress the unsafe site. In practice this is rare (SAST
207
+ * teams write one wrapper per file), and the current sink-filter
208
+ * stage 9b handles the analogous compiled-template case with the same
209
+ * file-scoped heuristic. If it becomes a problem, tighten to
210
+ * receiver-scoped: check the specific `Yaml` receiver that carries
211
+ * the load() call.
212
+ */
213
+ export declare function fileConfiguresSnakeYamlSafely(source: string): boolean;
214
+ //# sourceMappingURL=dependency-versions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-versions.d.ts","sourceRoot":"","sources":["../../src/analysis/dependency-versions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CA8BnF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAsC3F;AAMD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,MAAM,GACtB,gBAAgB,GAAG,IAAI,CAuBzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,GACpB,gBAAgB,GAAG,IAAI,CAiCzB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAIrE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EAAE,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAkBT;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAKrE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAUtE;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAOrE"}
@@ -0,0 +1,352 @@
1
+ /**
2
+ * Dependency-manifest helpers for the deserialization-safety-gate pass.
3
+ *
4
+ * cognium-dev #258 — dependency-version-aware sink gating. The engine
5
+ * never reads the filesystem (Pillar I / browser-safety); the caller
6
+ * (cognium-dev CLI or `analyzeProject`) reads the manifest and passes
7
+ * it as raw text via `AnalyzerOptions.dependencyContext`. Helpers here
8
+ * turn that raw text into the boolean predicates the gate needs.
9
+ *
10
+ * All parsers here are string-scoped: regex over the raw manifest
11
+ * rather than a full XML/JSON tree. That is deliberate — the gate only
12
+ * needs a handful of narrow signals (Fastjson version, presence of a
13
+ * safe classifier), and every existing runtime dep in circle-ir is
14
+ * either `web-tree-sitter` or `yaml`. Pulling in an XML parser purely
15
+ * for one Fastjson property would violate the minimal-dependencies
16
+ * guardrail.
17
+ */
18
+ /**
19
+ * Extract the effective Fastjson version from a `pom.xml`. Two sources
20
+ * are consulted:
21
+ *
22
+ * 1. A `<properties>` entry named exactly `<fastjson.version>` — the
23
+ * idiomatic way Maven projects centralise a dep version they
24
+ * reference in a `<dependencies>` block. This is where
25
+ * alibaba/Sentinel pins `1.2.83_noneautotype`.
26
+ * 2. A `<dependency>` block whose `<groupId>` is `com.alibaba` and
27
+ * `<artifactId>` is `fastjson` — read as a fallback when the
28
+ * version is declared inline rather than as a property.
29
+ *
30
+ * Returns `null` when no Fastjson coordinate can be resolved from the
31
+ * pom (no properties entry AND no matching dependency block, or the
32
+ * dependency uses a `${...}` reference the properties block doesn't
33
+ * define).
34
+ */
35
+ export function resolveFastjsonFromPom(pomXml) {
36
+ if (!pomXml)
37
+ return null;
38
+ const propMatch = pomXml.match(/<fastjson\.version>\s*([^<\s]+)\s*<\/fastjson\.version>/);
39
+ if (propMatch) {
40
+ const version = propMatch[1];
41
+ return { version, noneAutotype: /_noneautotype/i.test(version) };
42
+ }
43
+ // Fallback: scan every <dependency>...</dependency> block for
44
+ // com.alibaba:fastjson (or fastjson2). Non-greedy so we don't fuse
45
+ // sibling blocks. Case-sensitive on artifact / group; Maven's own
46
+ // resolution is case-sensitive too.
47
+ const depRe = /<dependency>[\s\S]*?<\/dependency>/g;
48
+ let m;
49
+ while ((m = depRe.exec(pomXml)) !== null) {
50
+ const block = m[0];
51
+ const gid = block.match(/<groupId>\s*([^<\s]+)\s*<\/groupId>/)?.[1];
52
+ const aid = block.match(/<artifactId>\s*([^<\s]+)\s*<\/artifactId>/)?.[1];
53
+ if (gid !== 'com.alibaba')
54
+ continue;
55
+ if (aid !== 'fastjson' && aid !== 'fastjson2')
56
+ continue;
57
+ const ver = block.match(/<version>\s*([^<\s]+)\s*<\/version>/)?.[1];
58
+ if (!ver)
59
+ continue;
60
+ // `${fastjson.version}` reference the properties block did not
61
+ // resolve — treat as unknown so the gate defaults to fire.
62
+ if (/^\$\{/.test(ver))
63
+ return null;
64
+ return { version: ver, noneAutotype: /_noneautotype/i.test(ver) };
65
+ }
66
+ return null;
67
+ }
68
+ /**
69
+ * Extract the effective Fastjson version from a Gradle build script
70
+ * (`build.gradle` Groovy DSL or `build.gradle.kts` Kotlin DSL).
71
+ *
72
+ * cognium-dev #261 (Gradle-first slice extending #258's pom.xml gate).
73
+ *
74
+ * Recognises three declaration shapes:
75
+ *
76
+ * 1. Direct literal — the classic Groovy / Kotlin single-string form:
77
+ * implementation 'com.alibaba:fastjson:1.2.83_noneautotype'
78
+ * implementation "com.alibaba:fastjson:1.2.83_noneautotype"
79
+ * implementation("com.alibaba:fastjson:1.2.83_noneautotype")
80
+ * Also `api`, `compile`, `runtimeOnly`, `testImplementation`, … — the
81
+ * configuration keyword is not part of the regex; only the
82
+ * `group:artifact:version` triple is matched, so any dependency-
83
+ * configuration prefix works.
84
+ *
85
+ * 2. Groovy interpolation — `implementation "com.alibaba:fastjson:${fastjsonVersion}"`
86
+ * with the property defined via `ext { fastjsonVersion = '1.2.83_noneautotype' }`,
87
+ * `def fastjsonVersion = '1.2.83_noneautotype'`, or top-level `fastjsonVersion = '…'`.
88
+ *
89
+ * 3. Kotlin interpolation — `implementation("com.alibaba:fastjson:$fastjsonVersion")`
90
+ * with the property defined via `val fastjsonVersion = "1.2.83_noneautotype"`
91
+ * or `const val fastjsonVersion = "…"`.
92
+ *
93
+ * Returns `null` on miss (no direct declaration AND no resolvable
94
+ * property reference). The `DeserializationSafetyGatePass` then falls
95
+ * through to its default "do not drop" behaviour on the sink.
96
+ *
97
+ * NOT recognised in this MVP (deferred, follow-ups on #261):
98
+ * - `platform(...)` / `enforcedPlatform(...)` BOM version imports
99
+ * - Version-catalog `libs.versions.toml` references (`libs.fastjson`)
100
+ * - `constraints { }` block versions
101
+ * - `subprojects { }` / `allprojects { }` conditional declarations
102
+ */
103
+ export function resolveFastjsonFromGradle(buildGradle) {
104
+ if (!buildGradle)
105
+ return null;
106
+ // Shape 1 — direct literal declaration. Accepts single-quoted,
107
+ // double-quoted, or paren-wrapped ("Kotlin") forms. The version is
108
+ // any non-quote / non-`$` / non-`)` / non-whitespace run.
109
+ const directRe = /['"(]\s*com\.alibaba:fastjson:([^'"$\s)]+)\s*['")]/;
110
+ const direct = buildGradle.match(directRe);
111
+ if (direct) {
112
+ const version = direct[1];
113
+ return { version, noneAutotype: /_noneautotype/i.test(version) };
114
+ }
115
+ // Shapes 2 + 3 — property reference. Capture the property name (either
116
+ // `${name}` Groovy form or `$name` bare form; Kotlin uses the same
117
+ // string-template syntax as Groovy for this case).
118
+ const propRefRe = /['"(]\s*com\.alibaba:fastjson:\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?\s*['")]/;
119
+ const propRef = buildGradle.match(propRefRe);
120
+ if (!propRef)
121
+ return null;
122
+ const propName = propRef[1];
123
+ // Property definition — accept:
124
+ // `fastjsonVersion = 'X'` (Groovy top-level or ext)
125
+ // `fastjsonVersion = "X"` (Groovy interpolated)
126
+ // `def fastjsonVersion = 'X'` (Groovy local)
127
+ // `val fastjsonVersion = "X"` (Kotlin)
128
+ // `const val fastjsonVersion = "X"` (Kotlin)
129
+ // `fastjsonVersion: 'X'` (Groovy map-syntax, rare)
130
+ // Any leading whitespace / newlines / `=` / `:` before the quote.
131
+ const defRe = new RegExp(`\\b${propName}\\s*[=:]\\s*['"]([^'"\\s]+)['"]`);
132
+ const def = buildGradle.match(defRe);
133
+ if (!def)
134
+ return null;
135
+ const version = def[1];
136
+ return { version, noneAutotype: /_noneautotype/i.test(version) };
137
+ }
138
+ /**
139
+ * Extract the effective PyYAML version from a `requirements.txt` file.
140
+ * Recognises the standard PEP 508 shapes (`==`, `>=`, `~=`, `>`,
141
+ * exact pins with trailing modifiers). Package name matched
142
+ * case-insensitively (both `PyYAML` and `pyyaml` are common in the
143
+ * wild).
144
+ *
145
+ * Returns null when no pyyaml line is found or the version string
146
+ * cannot be parsed as `M.m[.p]`. Non-strict on trailing environment
147
+ * markers (` ; python_version >= '3.6'`), which are common on
148
+ * requirements.txt lines.
149
+ */
150
+ export function resolvePyYamlFromRequirements(requirementsTxt) {
151
+ if (!requirementsTxt)
152
+ return null;
153
+ // Iterate line-by-line so a fatal parse error on one line doesn't
154
+ // sink the whole file. Comment lines and `-r other.txt` includes are
155
+ // ignored (we don't recurse into included files here — the caller
156
+ // controls what manifest string is passed).
157
+ for (const rawLine of requirementsTxt.split(/\r?\n/)) {
158
+ const line = rawLine.replace(/#.*$/, '').trim();
159
+ if (!line)
160
+ continue;
161
+ if (line.startsWith('-'))
162
+ continue;
163
+ // Match: `PyYAML==6.0`, `pyyaml>=6.0.1`, `PyYAML ~= 6.0`,
164
+ // `pyyaml>6`, `PyYAML==6.0 ; python_version>='3.6'`
165
+ const m = line.match(/^(pyyaml)\s*(==|>=|~=|>|===)\s*(\d+(?:\.\d+)*)/i);
166
+ if (!m)
167
+ continue;
168
+ const version = m[3];
169
+ return { version, safeByDefault: isPyYamlVersionSafeByDefault(version) };
170
+ }
171
+ return null;
172
+ }
173
+ /**
174
+ * Extract the effective PyYAML version from a `pyproject.toml`.
175
+ * Recognises the common Poetry and PEP 621 shapes:
176
+ *
177
+ * Poetry — under `[tool.poetry.dependencies]`:
178
+ * PyYAML = "6.0"
179
+ * pyyaml = "^6.0.1"
180
+ * pyyaml = { version = "6.0", extras = ["..."] }
181
+ *
182
+ * PEP 621 — under `[project]` `dependencies` array:
183
+ * dependencies = [ "PyYAML>=6.0", ... ]
184
+ *
185
+ * As with the requirements.txt resolver, non-strict on markers /
186
+ * modifiers; regex-based rather than a full TOML parser to keep the
187
+ * runtime-dep list minimal (Pillar I minimal-dependencies principle).
188
+ */
189
+ export function resolvePyYamlFromPyproject(pyprojectToml) {
190
+ if (!pyprojectToml)
191
+ return null;
192
+ // Poetry key = string form.
193
+ // PyYAML = "6.0" PyYAML = "^6.0" pyyaml = "~=6.0"
194
+ const poetryLine = pyprojectToml.match(/^\s*(pyyaml)\s*=\s*"([\^~=<>]*)(\d+(?:\.\d+)*)"/im);
195
+ if (poetryLine) {
196
+ const version = poetryLine[3];
197
+ return { version, safeByDefault: isPyYamlVersionSafeByDefault(version) };
198
+ }
199
+ // Poetry table form: `pyyaml = { version = "6.0", … }`
200
+ const poetryTable = pyprojectToml.match(/^\s*(pyyaml)\s*=\s*\{[^}]*\bversion\s*=\s*"([\^~=<>]*)(\d+(?:\.\d+)*)"/im);
201
+ if (poetryTable) {
202
+ const version = poetryTable[3];
203
+ return { version, safeByDefault: isPyYamlVersionSafeByDefault(version) };
204
+ }
205
+ // PEP 621 array element: within a `dependencies = [ ... ]` block, a
206
+ // string literal like `"PyYAML>=6.0"` or `"pyyaml==6.0.1"`.
207
+ const pep621 = pyprojectToml.match(/"(pyyaml)\s*(==|>=|~=|>|===)\s*(\d+(?:\.\d+)*)/i);
208
+ if (pep621) {
209
+ const version = pep621[3];
210
+ return { version, safeByDefault: isPyYamlVersionSafeByDefault(version) };
211
+ }
212
+ return null;
213
+ }
214
+ /**
215
+ * True when `version` (as `M.m[.p]`) is ≥ 6.0. Handles bare `6`,
216
+ * `6.0`, `6.0.1`, `7`, `10.0`, etc. Returns false on malformed input
217
+ * (defensive — the gate defaults to *do not drop* on missing signal).
218
+ */
219
+ export function isPyYamlVersionSafeByDefault(version) {
220
+ const parts = version.split('.').map((s) => Number.parseInt(s, 10));
221
+ if (parts.length === 0 || !Number.isFinite(parts[0]))
222
+ return false;
223
+ return parts[0] >= 6;
224
+ }
225
+ /**
226
+ * Return true when the file contains an explicit unsafe `Loader=`
227
+ * keyword argument on a `yaml.load(...)` call at or shortly after
228
+ * `sinkLine`. Recognises the well-known unsafe loaders and their
229
+ * qualified forms:
230
+ *
231
+ * Loader=Loader Loader=yaml.Loader
232
+ * Loader=UnsafeLoader Loader=yaml.UnsafeLoader
233
+ * Loader=FullLoader Loader=yaml.FullLoader (technically safer
234
+ * than Loader; still
235
+ * permits arbitrary
236
+ * Python object types)
237
+ *
238
+ * SafeLoader / CSafeLoader / BaseLoader / CBaseLoader are all safe and
239
+ * are NOT matched here.
240
+ *
241
+ * Scans `sinkLine` through the next 9 lines to catch multi-line call
242
+ * shapes; if the closing `)` appears before the window ends the scan
243
+ * stops there. Regex-based (no AST inspection) to stay consistent with
244
+ * the sink-filter-pass conventions and avoid pulling parse state into
245
+ * the gate.
246
+ */
247
+ export function fileHasUnsafePyYamlLoader(sourceLines, sinkLine) {
248
+ const start = Math.max(0, sinkLine - 1);
249
+ const end = Math.min(sourceLines.length, start + 10);
250
+ const unsafeRe = /\bLoader\s*=\s*(?:yaml\s*\.\s*)?(?:Loader|UnsafeLoader|FullLoader)\b/;
251
+ for (let i = start; i < end; i++) {
252
+ const ln = sourceLines[i] ?? '';
253
+ if (unsafeRe.test(ln))
254
+ return true;
255
+ // Stop once the call closes — the `)` at the top-level. Fine
256
+ // approximation: any bare `)` at the START of a stripped line, or
257
+ // trailing `)` at end of a stripped line, signals close.
258
+ const stripped = ln.trim();
259
+ if (i > start && (stripped === ')' || stripped.endsWith(')'))) {
260
+ // Also test the current line before we stop, in case Loader= is
261
+ // on the same line as the closing paren.
262
+ return false;
263
+ }
264
+ }
265
+ return false;
266
+ }
267
+ /**
268
+ * Return true when the given source text contains an in-file call that
269
+ * re-enables Fastjson autotype. Even a `_noneautotype` build does not
270
+ * protect against code that programmatically re-enables the feature
271
+ * (which the hardened build documents as impossible, but the classifier
272
+ * is a build-time strip, not a runtime lock). Defense-in-depth for the
273
+ * `resolveFastjsonFromPom` gate.
274
+ */
275
+ export function fileReenablesFastjsonAutotype(source) {
276
+ if (!source)
277
+ return false;
278
+ // ParserConfig.getGlobalInstance().setAutoTypeSupport(true)
279
+ // ParserConfig.setAutoTypeSupport(true) (static-import form)
280
+ return /\bsetAutoTypeSupport\s*\(\s*true\b/.test(source);
281
+ }
282
+ /**
283
+ * Return true when the given Java source contains a call that enables
284
+ * Jackson polymorphic type handling — either the legacy
285
+ * `enableDefaultTyping(...)` (deprecated in Jackson 2.10+ but still
286
+ * shipped) or the current `activateDefaultTyping(...)`.
287
+ *
288
+ * When neither is present in the file (and no `@JsonTypeInfo` is used
289
+ * on the target type — best-effort scan below), Jackson's default
290
+ * behaviour since 2.10 is safe: `ObjectMapper.readValue(json,
291
+ * targetType)` cannot instantiate arbitrary classes. The gate uses
292
+ * this to distinguish a genuine `readValue` sink from a safely-
293
+ * configured one.
294
+ *
295
+ * `@JsonTypeInfo` scan is intentionally file-local. A `@JsonTypeInfo`
296
+ * annotation on a target type in a different file would still allow
297
+ * polymorphic construction, but the engine treats that as unknown
298
+ * risk and preserves the sink (the gate only fires when the *file
299
+ * itself* provides positive evidence of a safe configuration).
300
+ */
301
+ export function fileEnablesJacksonPolymorphism(source) {
302
+ if (!source)
303
+ return false;
304
+ if (/\benableDefaultTyping\s*\(/.test(source))
305
+ return true;
306
+ if (/\bactivateDefaultTyping\s*\(/.test(source))
307
+ return true;
308
+ // @JsonTypeInfo(use = Id.CLASS) / (use = Id.MINIMAL_CLASS) / (use = Id.NAME)
309
+ // enables polymorphic type handling on the annotated field or type.
310
+ // The exact `use` argument doesn't matter for the gate — any
311
+ // @JsonTypeInfo signals polymorphism is in play somewhere.
312
+ if (/@JsonTypeInfo\b/.test(source))
313
+ return true;
314
+ return false;
315
+ }
316
+ /**
317
+ * Return true when the given Java source contains a
318
+ * `new Yaml(new SafeConstructor(...))` or an equivalent hardened
319
+ * SnakeYAML constructor. When any `Yaml` instance in the file is
320
+ * built with the safe constructor family, we assume the file's
321
+ * `Yaml.load(...)` calls are safely configured; the gate then drops
322
+ * the deserialization sink for those calls.
323
+ *
324
+ * Recognised safe constructor classes (SnakeYAML 1.x + 2.x):
325
+ * - SafeConstructor — canonical safe loader
326
+ * - SafeSchema (rare, YAML 1.2) — safe schema-based loader
327
+ *
328
+ * Recognised safe factory calls (SnakeYAML 2.x LoaderOptions API):
329
+ * - Yaml.load() with a Constructor that extends SafeConstructor
330
+ * (heuristic: any `SafeConstructor`-typed variable is safe)
331
+ *
332
+ * NOTE: A file that mixes `new Yaml(new SafeConstructor())` with
333
+ * `new Yaml(new Constructor(SomeClass.class))` on separate call sites
334
+ * would over-suppress the unsafe site. In practice this is rare (SAST
335
+ * teams write one wrapper per file), and the current sink-filter
336
+ * stage 9b handles the analogous compiled-template case with the same
337
+ * file-scoped heuristic. If it becomes a problem, tighten to
338
+ * receiver-scoped: check the specific `Yaml` receiver that carries
339
+ * the load() call.
340
+ */
341
+ export function fileConfiguresSnakeYamlSafely(source) {
342
+ if (!source)
343
+ return false;
344
+ if (/\bnew\s+SafeConstructor\s*\(/.test(source))
345
+ return true;
346
+ // Explicit typed declaration form: `SafeConstructor sc = new SafeConstructor()`
347
+ // or `SafeConstructor sc = ...` used in a Yaml constructor.
348
+ if (/\bSafeConstructor\s+\w+\s*=/.test(source))
349
+ return true;
350
+ return false;
351
+ }
352
+ //# sourceMappingURL=dependency-versions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-versions.js","sourceRoot":"","sources":["../../src/analysis/dependency-versions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Note-level finding coalescer — cognium-dev #143.
3
+ *
4
+ * Groups `SastFinding[]` by `(file, line)` and folds any group of two or
5
+ * more `level === 'note'` findings into a single record. The primary
6
+ * finding is picked deterministically (lexicographic `rule_id`) and
7
+ * the co-located rule_ids are attached as `labels[]`. Every other
8
+ * (`level === 'warning' | 'error'`) finding passes through untouched.
9
+ *
10
+ * Empirical basis (from the OWASP-Benchmark instrumentation capture on
11
+ * cognium-dev#145, comment 2026-07-03):
12
+ * - 5,481 `(file, line)` locations (31.3% of files) were hit by ≥ 2
13
+ * distinct advisory rules.
14
+ * - Most-common pairs:
15
+ * missing-public-doc + naming-convention × 2,740
16
+ * missing-csp-frame-ancestors + missing-x-frame-options × 2,740
17
+ * unused-variable + variable-shadowing (co-located)
18
+ * - HIGH-severity: 0 co-locations in the same capture.
19
+ *
20
+ * Design invariants:
21
+ * - Additive: consumers that key on `rule_id` continue to work
22
+ * unchanged. Consumers that surface every co-located rule read
23
+ * both `rule_id` and `labels`.
24
+ * - Level-gated: only fires when EVERY finding in the group has
25
+ * `level === 'note'`. If any group member is `warning` or `error`,
26
+ * the group passes through un-coalesced — visibility of higher-
27
+ * severity findings is never diminished.
28
+ * - Deterministic: sort by `rule_id` inside a group before picking
29
+ * the primary, so the same input always produces the same output
30
+ * (test-friendly, diff-friendly, cache-friendly).
31
+ * - Message preservation: the primary finding's `message` is kept
32
+ * verbatim. The `labels[]` field is the sole signal that more
33
+ * rules co-located there.
34
+ *
35
+ * This is the MVP of the reopen. Follow-ups on #143 include a full
36
+ * instrumentation rerun on multi-severity data + a broader
37
+ * medium-severity coalesce policy — both deferred until the data
38
+ * capture is redone.
39
+ */
40
+ import type { SastFinding } from '../types/index.js';
41
+ /**
42
+ * Coalesce note-level findings at the same `(file, line)` location.
43
+ * Returns a new array; the input is not mutated.
44
+ */
45
+ export declare function coalesceNoteLevelFindings(findings: readonly SastFinding[]): SastFinding[];
46
+ //# sourceMappingURL=note-coalescer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"note-coalescer.d.ts","sourceRoot":"","sources":["../../src/analysis/note-coalescer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC/B,WAAW,EAAE,CAkEf"}