muaddib-scanner 2.11.113 → 2.11.114

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.11.113",
3
+ "version": "2.11.114",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "target": "node_modules",
3
- "timestamp": "2026-06-14T08:06:18.378Z",
3
+ "timestamp": "2026-06-14T12:30:49.542Z",
4
4
  "threats": [
5
5
  {
6
6
  "type": "string_mutation_obfuscation",
@@ -244,7 +244,7 @@
244
244
  },
245
245
  {
246
246
  "type": "credential_regex_harvest",
247
- "severity": "HIGH",
247
+ "severity": "LOW",
248
248
  "message": "Credential regex patterns (token/password/secret/Bearer) + network call in same file — stream data credential harvesting.",
249
249
  "file": "web-tree-sitter/web-tree-sitter.cjs",
250
250
  "count": 1,
@@ -252,6 +252,11 @@
252
252
  {
253
253
  "rule": "count_threshold_floor",
254
254
  "note": "retained one instance at original severity"
255
+ },
256
+ {
257
+ "rule": "sink_coupling",
258
+ "from": "HIGH",
259
+ "to": "LOW"
255
260
  }
256
261
  ],
257
262
  "originalSeverity": "HIGH",
@@ -266,7 +271,7 @@
266
271
  ],
267
272
  "mitre": "T1552",
268
273
  "playbook": "Code contient des regex de detection de credentials (Bearer, password, token, API key) combine avec un appel reseau. Technique de harvesting: scanne les donnees en transit (streams HTTP, fichiers) pour extraire des secrets et les exfiltrer. Supprimer le package. Auditer le trafic reseau sortant.",
269
- "points": 10
274
+ "points": 1
270
275
  },
271
276
  {
272
277
  "type": "proxy_data_intercept",
@@ -1823,18 +1828,18 @@
1823
1828
  "summary": {
1824
1829
  "total": 80,
1825
1830
  "critical": 7,
1826
- "high": 11,
1831
+ "high": 10,
1827
1832
  "medium": 32,
1828
- "low": 30,
1833
+ "low": 31,
1829
1834
  "riskScore": 35,
1830
1835
  "riskLevel": "MEDIUM",
1831
1836
  "globalRiskScore": 100,
1832
- "maxFileScore": 51,
1837
+ "maxFileScore": 42,
1833
1838
  "packageScore": 1,
1834
1839
  "mostSuspiciousFile": "web-tree-sitter/web-tree-sitter.cjs",
1835
1840
  "fileScores": {
1836
1841
  "esquery/parser.js": 5,
1837
- "web-tree-sitter/web-tree-sitter.cjs": 51,
1842
+ "web-tree-sitter/web-tree-sitter.cjs": 42,
1838
1843
  "web-tree-sitter/web-tree-sitter.js": 42,
1839
1844
  "ajv/lib/ajv.js": 25,
1840
1845
  "ajv/scripts/bundle.js": 10,
@@ -1927,12 +1932,6 @@
1927
1932
  "points": 10,
1928
1933
  "reason": "Binary file reference (.png/.jpg/.wasm/etc.) + eval() in same file — possible steganographic payload execution."
1929
1934
  },
1930
- {
1931
- "rule": "MUADDIB-AST-041",
1932
- "type": "credential_regex_harvest",
1933
- "points": 10,
1934
- "reason": "Credential regex patterns (token/password/secret/Bearer) + network call in same file — stream data credential harvesting."
1935
- },
1936
1935
  {
1937
1936
  "rule": "MUADDIB-AST-020",
1938
1937
  "type": "staged_binary_payload",
@@ -2185,6 +2184,12 @@
2185
2184
  "points": 1,
2186
2185
  "reason": "Dangerous call \"eval\" with dynamic expression detected."
2187
2186
  },
2187
+ {
2188
+ "rule": "MUADDIB-AST-041",
2189
+ "type": "credential_regex_harvest",
2190
+ "points": 1,
2191
+ "reason": "Credential regex patterns (token/password/secret/Bearer) + network call in same file — stream data credential harvesting."
2192
+ },
2188
2193
  {
2189
2194
  "rule": "MUADDIB-AST-043",
2190
2195
  "type": "proxy_data_intercept",
package/src/scoring.js CHANGED
@@ -1029,6 +1029,28 @@ const FRAMEWORK_PROTO_RE = new RegExp(
1029
1029
  '^(' + FRAMEWORK_PROTOTYPES.join('|') + ')\\.prototype\\.'
1030
1030
  );
1031
1031
 
1032
+ // FPR sink-coupling (chantier 2026-06): independent exfil / remote-exec sink signals pointing at
1033
+ // an ANOMALOUS destination. A credential_regex_harvest signal is only a true positive when one of
1034
+ // these co-occurs in the package. Deliberately EXCLUDES benign network capability — a bare
1035
+ // fetch/http.get, remote_code_load to a first-party CDN/model host, a local server, or a native
1036
+ // build are NOT sinks. Dataflow-PROVEN harvest signals (intent_credential_exfil, cross_file_dataflow)
1037
+ // are included so a genuine read→exfil taint keeps the signal HIGH regardless of host reputation
1038
+ // (anti-FN floor). See _hasExfilSink + the credential_regex_harvest gate in applyFPReductions.
1039
+ const EXFIL_SINK_TYPES = new Set([
1040
+ 'suspicious_domain', 'direct_ip_exfil', 'ioc_string_match', 'ioc_match',
1041
+ 'known_malicious_package', 'pypi_malicious_package', 'shai_hulud_marker',
1042
+ 'detached_credential_exfil', 'silent_stealth_process',
1043
+ 'curl_pipe_shell', 'curl_env_exfil', 'reverse_shell', 'dns_exfil', 'oast_callback',
1044
+ 'function_constructor_require', 'staged_remote_loader', 'staged_eval_decode',
1045
+ 'fetch_decrypt_exec', 'download_exec_binary', 'self_destruct_eval',
1046
+ 'newsletter_auto_follow', 'cross_file_dataflow', 'intent_credential_exfil',
1047
+ 'intent_command_exfil', 'sandbox_known_exfil_domain', 'sandbox_network_after_sensitive_read'
1048
+ ]);
1049
+ function _hasExfilSink(threats) {
1050
+ if (!Array.isArray(threats)) return false;
1051
+ return threats.some(t => EXFIL_SINK_TYPES.has(t.type) && t.severity !== 'LOW');
1052
+ }
1053
+
1032
1054
  function applyFPReductions(threats, reachableFiles, packageName, packageDeps, reachableFunctions) {
1033
1055
  // Initialize reductions audit trail on each threat
1034
1056
  // Store original severity before any FP reductions, so compound
@@ -1174,6 +1196,25 @@ function applyFPReductions(threats, reachableFiles, packageName, packageDeps, re
1174
1196
  }
1175
1197
  }
1176
1198
 
1199
+ // FPR sink-coupling gate (chantier 2026-06 — FPR-baseline-2026-06-14.md). credential_regex_harvest
1200
+ // is a weak signal alone: a credential-shaped regex co-located with a network call, with NO proof
1201
+ // the matched secret flows out and NO host-reputation check (ast.js:hasCredentialInsideRegex +
1202
+ // hasNetworkCallInFile). The blind FPR baseline measured 94.4% FP on it — it fires on nodemailer
1203
+ // SMTP code, redaction utilities in framework bundles, and SDKs that parse Authorization headers.
1204
+ // It is a real harvester ONLY when an independent exfil sink to an anomalous destination co-occurs
1205
+ // (suspicious_domain / direct_ip / ioc / detached-exfil / staged loader / curl exfil / dataflow-proven
1206
+ // taint ...). When no such sink is present, downgrade HIGH/CRITICAL → LOW. Runs after the dilution
1207
+ // floor so the floor's restored instance is also gated (the floor protects real exfil; with no sink
1208
+ // there is nothing to protect). No GT sample relies on credential_regex_harvest (verified).
1209
+ if (!_hasExfilSink(threats)) {
1210
+ for (const t of threats) {
1211
+ if (t.type === 'credential_regex_harvest' && (t.severity === 'HIGH' || t.severity === 'CRITICAL')) {
1212
+ t.reductions.push({ rule: 'sink_coupling', from: t.severity, to: 'LOW' });
1213
+ t.severity = 'LOW';
1214
+ }
1215
+ }
1216
+ }
1217
+
1177
1218
  for (const t of threats) {
1178
1219
 
1179
1220
  // Audit v3 B3: typosquat with LOW confidence → MEDIUM