muaddib-scanner 2.11.81 → 2.11.82

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.81",
3
+ "version": "2.11.82",
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-10T12:42:10.126Z",
3
+ "timestamp": "2026-06-10T12:51:04.328Z",
4
4
  "threats": [
5
5
  {
6
6
  "type": "string_mutation_obfuscation",
@@ -751,6 +751,75 @@ function mcpServerEnvAccess(result, meta) {
751
751
  return true;
752
752
  }
753
753
 
754
+ // ============================================================================
755
+ // Feature 15 — mcp_server_benign_lifecycle (AUDIT 2, 2026-06)
756
+ // ============================================================================
757
+ //
758
+ // Like F9 (mcpServerEnvAccess) but TOLERATES a benign install lifecycle. F9
759
+ // vetoes on ANY preinstall/install/postinstall (its C3), which makes it
760
+ // inoperative for the ~77% of legitimate MCP installers that ship a build/setup
761
+ // hook (`husky install`, `node build.js`, `tsc`). Those packages stack
762
+ // mcp_config_injection (CRIT) + suspicious_dataflow (CRIT, env→first-party POST)
763
+ // + env_access (HIGH) + lifecycle_script (MEDIUM) and score ~150 on `muaddib
764
+ // scan` — the recurring @recapp/mcp-style false positives in the daily report.
765
+ //
766
+ // F15 instead allows a lifecycle that is only flagged as a plain MEDIUM/LOW
767
+ // `lifecycle_script`, and vetoes the moment the lifecycle does anything
768
+ // malicious. Ground-truth safety (verified by replay before/after):
769
+ // GT-060 mcp-config-inject → vetoed by lifecycle_file_exec (malicious postinstall)
770
+ // GT-088 defi-threat-scanner → vetoed by HARD exfil (suspicious_domain) + cred files
771
+ // GT-066 ai-agent-exploit → never emits mcp_config_injection (C2 excludes it)
772
+ // GT-097 / GT-099 → HARD exfil / not an mcp_config_injection JS package
773
+ // Same cap (30 = MEDIUM) and identity/provider-key machinery as F9.
774
+ const F15_LIFECYCLE_MALICE_TYPES = new Set([
775
+ 'lifecycle_file_exec', // postinstall executes a file containing HIGH/CRIT threats
776
+ 'lifecycle_dataflow', // install-time credential read + network send (compound)
777
+ 'lifecycle_shell_pipe', // curl | sh during install
778
+ 'lifecycle_missing_script', // phantom install script (payload injected later)
779
+ 'intent_credential_exfil', // multi-file credential→network intent
780
+ 'intent_command_exfil',
781
+ 'detached_credential_exfil',
782
+ 'staged_payload'
783
+ ]);
784
+
785
+ function mcpServerBenignLifecycle(result, meta) {
786
+ // C1 — MCP identity (same as F9)
787
+ if (!_f9HasMcpIdentity(meta)) return false;
788
+ const threats = (result && result.threats) || [];
789
+ if (threats.length === 0) return false;
790
+ // C2 — mcp_config_injection present (proves real MCP work, not just a name claim)
791
+ if (!threats.some(t => t.type === 'mcp_config_injection')) return false;
792
+ // C3' (relaxed) — a lifecycle MAY exist, but it must be benign: no malicious
793
+ // lifecycle compound, and a plain lifecycle_script (if any) must not itself be
794
+ // HIGH/CRITICAL (a benign husky/build hook is MEDIUM/LOW).
795
+ for (const t of threats) {
796
+ if (F15_LIFECYCLE_MALICE_TYPES.has(t.type)) return false;
797
+ if (t.type === 'lifecycle_script' && (t.severity === 'HIGH' || t.severity === 'CRITICAL')) return false;
798
+ }
799
+ // C4 — env_access / credential threats cite ONLY known provider keys or infra
800
+ // vars; never credential file paths (same machinery as F9).
801
+ for (const t of threats) {
802
+ if (t.type !== 'env_access' && t.type !== 'credential_regex_harvest' &&
803
+ t.type !== 'env_charcode_reconstruction') continue;
804
+ const msg = String(t.message || '');
805
+ if (F9_CREDENTIAL_FILE_RE.test(msg)) return false;
806
+ const candidates = msg.match(/\b[A-Z][A-Z0-9_]{2,}\b/g);
807
+ if (!candidates) continue;
808
+ for (const v of candidates) {
809
+ if (KNOWN_PROVIDER_KEYS_LITERAL.has(v)) continue;
810
+ if (PROVIDER_KEY_SUFFIX_RE.test(v)) continue;
811
+ if (F9_INFRA_KEYS.has(v)) continue;
812
+ return false;
813
+ }
814
+ }
815
+ // C5 — no HARD third-party exfil capability (SOFT suspicious_dataflow to a
816
+ // first-party endpoint is intrinsic to MCP installers — see F9/F14)
817
+ for (const t of threats) {
818
+ if (HARD_EXFIL_TYPES.has(t.type)) return false;
819
+ }
820
+ return true;
821
+ }
822
+
754
823
  // ============================================================================
755
824
  // Feature 10 — vendor_cli_sdk (v2.11.23, audit week3 cluster, 96 FP)
756
825
  // ============================================================================
@@ -1426,6 +1495,7 @@ module.exports = {
1426
1495
  placeholderAntiDepConfusion,
1427
1496
  installScriptNoNetworkEgress,
1428
1497
  mcpServerEnvAccess,
1498
+ mcpServerBenignLifecycle,
1429
1499
  vendorCliSdk,
1430
1500
  aiAgentBot,
1431
1501
  vendorMinifiedBundle,
package/src/scoring.js CHANGED
@@ -1506,6 +1506,7 @@ const {
1506
1506
  obfuscationWithoutVector,
1507
1507
  placeholderAntiDepConfusion,
1508
1508
  mcpServerEnvAccess,
1509
+ mcpServerBenignLifecycle,
1509
1510
  vendorCliSdk,
1510
1511
  aiAgentBot,
1511
1512
  vendorMinifiedBundle,
@@ -1559,6 +1560,13 @@ function applyContextualFPCaps(result, pkgMeta) {
1559
1560
  if (mcpServerEnvAccess(result, meta)) {
1560
1561
  applied.push({ feature: 'mcp_server_env_access', cap: 30 });
1561
1562
  }
1563
+ // F15: legit MCP installer/server WITH a benign install lifecycle (AUDIT 2) →
1564
+ // MAX 30. Extends F9 to the ~77% of MCP installers that ship a build/setup hook
1565
+ // (husky install, node build.js). Vetoes on malicious lifecycle (lifecycle_file_exec
1566
+ // etc.), HARD exfil, or credential-file access — so GT MCP malware stays uncapped.
1567
+ if (mcpServerBenignLifecycle(result, meta)) {
1568
+ applied.push({ feature: 'mcp_server_benign_lifecycle', cap: 30 });
1569
+ }
1562
1570
  // F2: binary installer from GitHub Releases → MAX 35
1563
1571
  if (installUrlGithubReleases(result)) {
1564
1572
  applied.push({ feature: 'install_url_github_releases', cap: 35 });