slopbrick 0.29.0 → 0.31.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.
@@ -36971,6 +36971,211 @@ var goStructTagInconsistencyRule = createRule({
36971
36971
  }
36972
36972
  });
36973
36973
 
36974
+ // src/rules/java/command-injection.ts
36975
+ var COMMAND_INVOCATION_REGEX = /\b(?:Runtime\.exec|Runtime\.getRuntime\(\)\.exec|ProcessBuilder)\s*\(/;
36976
+ var STRING_CONCAT_REGEX = /["'][^"']*["']\s*\+/;
36977
+ var javaCommandInjectionRule = createRule({
36978
+ id: "java/command-injection",
36979
+ category: "security",
36980
+ severity: "high",
36981
+ aiSpecific: false,
36982
+ description: "Runtime.exec() or ProcessBuilder with string concat \u2014 use List<String> args + validation",
36983
+ create(_context) {
36984
+ return {};
36985
+ },
36986
+ analyze(_context, facts) {
36987
+ const issues = [];
36988
+ const source = facts.v2?._source;
36989
+ if (!source) return issues;
36990
+ if (!/\.java$/i.test(facts.filePath)) return issues;
36991
+ const lines = source.split("\n");
36992
+ for (let i = 0; i < lines.length; i++) {
36993
+ const line = lines[i];
36994
+ if (!COMMAND_INVOCATION_REGEX.test(line)) continue;
36995
+ if (!STRING_CONCAT_REGEX.test(line)) continue;
36996
+ issues.push({
36997
+ ruleId: "java/command-injection",
36998
+ category: "security",
36999
+ severity: "high",
37000
+ aiSpecific: false,
37001
+ message: `Command invocation with string concat at line ${i + 1}`,
37002
+ line: i + 1,
37003
+ column: 1,
37004
+ advice: "Use ProcessBuilder with a List<String> of args (no shell parsing) and validate each arg against a whitelist. For shell commands, use bash -c only with a fixed string (no concatenation). String concat into Runtime.exec() is the canonical command-injection pattern \u2014 attackers can break out with `; rm -rf /` or `$(...)`. Reference: java/command-injection v0.30 (OWASP A03:2021)."
37005
+ });
37006
+ }
37007
+ return issues;
37008
+ }
37009
+ });
37010
+
37011
+ // src/rules/java/hardcoded-credential.ts
37012
+ var KEY_REGEX = /\b(api[_-]?key|apikey|secret|token|password|auth|access[_-]?key|client[_-]?secret|private[_-]?key)\b\s*[=:]/i;
37013
+ var VALUE_REGEX = /["']([A-Za-z0-9_\-+/=.@*!]{16,})["']/;
37014
+ var FALSE_POSITIVES = /* @__PURE__ */ new Set([
37015
+ "passwordless",
37016
+ "tokenize",
37017
+ "tokenizer",
37018
+ "authorizationrequired",
37019
+ "authenticated",
37020
+ "authenticatortoken",
37021
+ "authtoken",
37022
+ "placeholder",
37023
+ "changeme"
37024
+ ]);
37025
+ var javaHardcodedCredentialRule = createRule({
37026
+ id: "java/hardcoded-credential",
37027
+ category: "security",
37028
+ severity: "high",
37029
+ aiSpecific: false,
37030
+ description: "Hardcoded credential literal \u2014 use env vars or a secrets manager",
37031
+ create(_context) {
37032
+ return {};
37033
+ },
37034
+ analyze(_context, facts) {
37035
+ const issues = [];
37036
+ const source = facts.v2?._source;
37037
+ if (!source) return issues;
37038
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37039
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
37040
+ const lines = source.split("\n");
37041
+ for (let i = 0; i < lines.length; i++) {
37042
+ const line = lines[i];
37043
+ if (!KEY_REGEX.test(line)) continue;
37044
+ const valueMatch = VALUE_REGEX.exec(line);
37045
+ if (!valueMatch) continue;
37046
+ const value = valueMatch[1];
37047
+ if (FALSE_POSITIVES.has(value.toLowerCase())) continue;
37048
+ if (!/[a-zA-Z]/.test(value) || !/[0-9]/.test(value)) continue;
37049
+ if (value.startsWith("$")) continue;
37050
+ issues.push({
37051
+ ruleId: "java/hardcoded-credential",
37052
+ category: "security",
37053
+ severity: "high",
37054
+ aiSpecific: false,
37055
+ message: `Hardcoded credential at line ${i + 1}`,
37056
+ line: i + 1,
37057
+ column: 1,
37058
+ advice: "Move the credential to an environment variable, a .env file that is .gitignore'd, or a secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager). Hardcoded credentials are the #1 source of secret leaks on GitHub. Reference: java/hardcoded-credential v0.30 (OWASP A07:2021 \u2014 Identification and Authentication Failures)."
37059
+ });
37060
+ }
37061
+ return issues;
37062
+ }
37063
+ });
37064
+
37065
+ // src/rules/java/sql-string-concat.ts
37066
+ var SQL_KEYWORD_REGEX = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|CREATE\s+TABLE|DROP\s+TABLE|ALTER\s+TABLE)\b/i;
37067
+ var UNSAFE_REGEX = /\+/;
37068
+ var SAFE_REGEX = /(?:PreparedStatement|setParameter|setString|setInt|setLong|createQuery.*:.*\b(?:set|bind)|:name\b|\?\s*,)/;
37069
+ var javaSqlStringConcatRule = createRule({
37070
+ id: "java/sql-string-concat",
37071
+ category: "security",
37072
+ severity: "high",
37073
+ aiSpecific: false,
37074
+ description: "SQL query built via string concat \u2014 use PreparedStatement or setParameter()",
37075
+ create(_context) {
37076
+ return {};
37077
+ },
37078
+ analyze(_context, facts) {
37079
+ const issues = [];
37080
+ const source = facts.v2?._source;
37081
+ if (!source) return issues;
37082
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37083
+ const lines = source.split("\n");
37084
+ for (let i = 0; i < lines.length; i++) {
37085
+ const line = lines[i];
37086
+ if (!SQL_KEYWORD_REGEX.test(line)) continue;
37087
+ if (!UNSAFE_REGEX.test(line)) continue;
37088
+ if (SAFE_REGEX.test(line)) continue;
37089
+ issues.push({
37090
+ ruleId: "java/sql-string-concat",
37091
+ category: "security",
37092
+ severity: "high",
37093
+ aiSpecific: false,
37094
+ message: `SQL query built via string concat at line ${i + 1}`,
37095
+ line: i + 1,
37096
+ column: 1,
37097
+ advice: 'Use a PreparedStatement (JDBC), setParameter (jOOQ), or an ORM (Hibernate, MyBatis with #{} binding). String concatenation into a SQL query is the canonical SQL-injection pattern \u2014 even "trusted" inputs (signed JWT, internal config) can be influenced by an attacker. Reference: java/sql-string-concat v0.30 (OWASP A03:2021).'
37098
+ });
37099
+ }
37100
+ return issues;
37101
+ }
37102
+ });
37103
+
37104
+ // src/rules/java/system-out-println.ts
37105
+ var SYSTEM_OUT_REGEX = /\bSystem\.out\.println\s*\(/g;
37106
+ var REAL_LOGGING_IMPORT_REGEX = /\bimport\s+(?:org\.slf4j\.|org\.apache\.logging\.log4j|org\.apache\.log4j|java\.util\.logging|com\.google\.common\.logging)/;
37107
+ var javaSystemOutPrintlnRule = createRule({
37108
+ id: "java/system-out-println",
37109
+ category: "logic",
37110
+ severity: "low",
37111
+ aiSpecific: false,
37112
+ description: "System.out.println() in a file that imports SLF4J/Log4j2 \u2014 use the declared logger instead",
37113
+ create(_context) {
37114
+ return {};
37115
+ },
37116
+ analyze(_context, facts) {
37117
+ const issues = [];
37118
+ const source = facts.v2?._source;
37119
+ if (!source) return issues;
37120
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37121
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
37122
+ if (!REAL_LOGGING_IMPORT_REGEX.test(source)) return issues;
37123
+ let m;
37124
+ SYSTEM_OUT_REGEX.lastIndex = 0;
37125
+ while ((m = SYSTEM_OUT_REGEX.exec(source)) !== null) {
37126
+ const line = source.slice(0, m.index).split("\n").length;
37127
+ issues.push({
37128
+ ruleId: "java/system-out-println",
37129
+ category: "logic",
37130
+ severity: "low",
37131
+ aiSpecific: false,
37132
+ message: `System.out.println() at line ${line} (file imports a real logger)`,
37133
+ line,
37134
+ column: 1,
37135
+ advice: "The file imports a real logging library (SLF4J, Log4j2, or java.util.logging) but uses System.out.println here. Replace `System.out.println(x)` with `log.info(x)` (or log.debug/warn/error as appropriate). The log object is already declared at the top of the file. Reference: java/system-out-println v0.31 (refined from v0.30)."
37136
+ });
37137
+ }
37138
+ return issues;
37139
+ }
37140
+ });
37141
+
37142
+ // src/rules/java/thread-sleep-in-loop.ts
37143
+ var THREAD_SLEEP_REGEX = /\bThread\.sleep\s*\(/g;
37144
+ var javaThreadSleepInLoopRule = createRule({
37145
+ id: "java/thread-sleep-in-loop",
37146
+ category: "perf",
37147
+ severity: "medium",
37148
+ aiSpecific: false,
37149
+ description: "Thread.sleep() in a loop \u2014 use ScheduledExecutorService or BlockingQueue",
37150
+ create(_context) {
37151
+ return {};
37152
+ },
37153
+ analyze(_context, facts) {
37154
+ const issues = [];
37155
+ const source = facts.v2?._source;
37156
+ if (!source) return issues;
37157
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37158
+ if (!/\bThread\.sleep\s*\(/.test(source)) return issues;
37159
+ if (!/\b(?:for|while|do)\b/.test(source)) return issues;
37160
+ let m;
37161
+ THREAD_SLEEP_REGEX.lastIndex = 0;
37162
+ while ((m = THREAD_SLEEP_REGEX.exec(source)) !== null) {
37163
+ const line = source.slice(0, m.index).split("\n").length;
37164
+ issues.push({
37165
+ ruleId: "java/thread-sleep-in-loop",
37166
+ category: "perf",
37167
+ severity: "medium",
37168
+ aiSpecific: false,
37169
+ message: `Thread.sleep() at line ${line}`,
37170
+ line,
37171
+ column: 1,
37172
+ advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.30.'
37173
+ });
37174
+ }
37175
+ return issues;
37176
+ }
37177
+ });
37178
+
36974
37179
  // src/rules/kotlin/coroutine-global-scope.ts
36975
37180
  var GLOBAL_SCOPE_REGEX = /GlobalScope\s*\.\s*(?:launch|async|runBlocking)\s*[({]/g;
36976
37181
  var kotlinCoroutineGlobalScopeRule = createRule({
@@ -37103,9 +37308,9 @@ var kotlinForceUnwrapRule = createRule({
37103
37308
  });
37104
37309
 
37105
37310
  // src/rules/kotlin/hardcoded-credential.ts
37106
- var KEY_REGEX = /\b(api[_-]?key|apikey|secret|token|password|auth|access[_-]?key|client[_-]?secret|private[_-]?key)\b\s*[=:]/i;
37107
- var VALUE_REGEX = /["']([A-Za-z0-9_\-+/=.@*!]{16,})["']/;
37108
- var FALSE_POSITIVES = /* @__PURE__ */ new Set([
37311
+ var KEY_REGEX2 = /\b(api[_-]?key|apikey|secret|token|password|auth|access[_-]?key|client[_-]?secret|private[_-]?key)\b\s*[=:]/i;
37312
+ var VALUE_REGEX2 = /["']([A-Za-z0-9_\-+/=.@*!]{16,})["']/;
37313
+ var FALSE_POSITIVES2 = /* @__PURE__ */ new Set([
37109
37314
  "passwordless",
37110
37315
  "tokenize",
37111
37316
  "tokenizer",
@@ -37135,11 +37340,11 @@ var kotlinHardcodedCredentialRule = createRule({
37135
37340
  const lines = source.split("\n");
37136
37341
  for (let i = 0; i < lines.length; i++) {
37137
37342
  const line = lines[i];
37138
- if (!KEY_REGEX.test(line)) continue;
37139
- const valueMatch = VALUE_REGEX.exec(line);
37343
+ if (!KEY_REGEX2.test(line)) continue;
37344
+ const valueMatch = VALUE_REGEX2.exec(line);
37140
37345
  if (!valueMatch) continue;
37141
37346
  const value = valueMatch[1];
37142
- if (FALSE_POSITIVES.has(value.toLowerCase())) continue;
37347
+ if (FALSE_POSITIVES2.has(value.toLowerCase())) continue;
37143
37348
  if (!/[a-zA-Z]/.test(value) || !/[0-9]/.test(value)) continue;
37144
37349
  if (value.startsWith("$")) continue;
37145
37350
  if (/\/test\//i.test(facts.filePath) || /\.test\.kts?$/i.test(facts.filePath)) continue;
@@ -37210,7 +37415,7 @@ var kotlinObjectSingletonMisuseRule = createRule({
37210
37415
 
37211
37416
  // src/rules/kotlin/println-as-log.ts
37212
37417
  var PRINTLN_REGEX = /\bprintln\s*\(/g;
37213
- var REAL_LOGGING_IMPORT_REGEX = /\bimport\s+(?:android\.util\.Log|org\.slf4j\.|io\.github\.oshai\.kotlinlogging|kotlin\.logging|co\.touchlab\.kermit|com\.github\.ajalt\.timber|org\.apache\.logging\.log4j)/;
37418
+ var REAL_LOGGING_IMPORT_REGEX2 = /\bimport\s+(?:android\.util\.Log|org\.slf4j\.|io\.github\.oshai\.kotlinlogging|kotlin\.logging|co\.touchlab\.kermit|com\.github\.ajalt\.timber|org\.apache\.logging\.log4j)/;
37214
37419
  var kotlinPrintlnAsLogRule = createRule({
37215
37420
  id: "kotlin/println-as-log",
37216
37421
  category: "logic",
@@ -37226,7 +37431,7 @@ var kotlinPrintlnAsLogRule = createRule({
37226
37431
  if (!source) return issues;
37227
37432
  if (!/\.kts?$/i.test(facts.filePath)) return issues;
37228
37433
  if (/\/test\//i.test(facts.filePath) || /\.test\.kts?$/i.test(facts.filePath)) return issues;
37229
- if (REAL_LOGGING_IMPORT_REGEX.test(source)) return issues;
37434
+ if (REAL_LOGGING_IMPORT_REGEX2.test(source)) return issues;
37230
37435
  let m;
37231
37436
  PRINTLN_REGEX.lastIndex = 0;
37232
37437
  while ((m = PRINTLN_REGEX.exec(source)) !== null) {
@@ -37326,9 +37531,9 @@ var kotlinRunBlockingMisuseRule = createRule({
37326
37531
  });
37327
37532
 
37328
37533
  // src/rules/kotlin/sql-string-concat.ts
37329
- var SQL_KEYWORD_REGEX = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|CREATE\s+TABLE|DROP\s+TABLE|ALTER\s+TABLE)\b/i;
37330
- var UNSAFE_REGEX = /(?:\+|\$\{)/;
37331
- var SAFE_REGEX = /(?:PreparedStatement|setParameter|setString|setInt|setLong|bind|:name|:\\?\\?|\?\\s*,)/;
37534
+ var SQL_KEYWORD_REGEX2 = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|CREATE\s+TABLE|DROP\s+TABLE|ALTER\s+TABLE)\b/i;
37535
+ var UNSAFE_REGEX2 = /(?:\+|\$\{)/;
37536
+ var SAFE_REGEX2 = /(?:PreparedStatement|setParameter|setString|setInt|setLong|bind|:name|:\\?\\?|\?\\s*,)/;
37332
37537
  var kotlinSqlStringConcatRule = createRule({
37333
37538
  id: "kotlin/sql-string-concat",
37334
37539
  category: "security",
@@ -37346,9 +37551,9 @@ var kotlinSqlStringConcatRule = createRule({
37346
37551
  const lines = source.split("\n");
37347
37552
  for (let i = 0; i < lines.length; i++) {
37348
37553
  const line = lines[i];
37349
- if (!SQL_KEYWORD_REGEX.test(line)) continue;
37350
- if (!UNSAFE_REGEX.test(line)) continue;
37351
- if (SAFE_REGEX.test(line)) continue;
37554
+ if (!SQL_KEYWORD_REGEX2.test(line)) continue;
37555
+ if (!UNSAFE_REGEX2.test(line)) continue;
37556
+ if (SAFE_REGEX2.test(line)) continue;
37352
37557
  issues.push({
37353
37558
  ruleId: "kotlin/sql-string-concat",
37354
37559
  category: "security",
@@ -37365,7 +37570,7 @@ var kotlinSqlStringConcatRule = createRule({
37365
37570
  });
37366
37571
 
37367
37572
  // src/rules/kotlin/string-concat-loop.ts
37368
- var STRING_CONCAT_REGEX = /\b(\w+)\s*=\s*\1\s*\+\s*[^;}]+[;}\n]/g;
37573
+ var STRING_CONCAT_REGEX2 = /\b(\w+)\s*=\s*\1\s*\+\s*[^;}]+[;}\n]/g;
37369
37574
  var kotlinStringConcatLoopRule = createRule({
37370
37575
  id: "kotlin/string-concat-loop",
37371
37576
  category: "perf",
@@ -37382,8 +37587,8 @@ var kotlinStringConcatLoopRule = createRule({
37382
37587
  if (!/\.kts?$/i.test(facts.filePath)) return issues;
37383
37588
  if (!/\b(?:for|while|repeat|forEach)\b/.test(source)) return issues;
37384
37589
  let m;
37385
- STRING_CONCAT_REGEX.lastIndex = 0;
37386
- while ((m = STRING_CONCAT_REGEX.exec(source)) !== null) {
37590
+ STRING_CONCAT_REGEX2.lastIndex = 0;
37591
+ while ((m = STRING_CONCAT_REGEX2.exec(source)) !== null) {
37387
37592
  const line = source.slice(0, m.index).split("\n").length;
37388
37593
  const lineText = source.slice(0, m.index).split("\n").pop() ?? "";
37389
37594
  if (/\.append\s*\(/.test(lineText)) continue;
@@ -42924,6 +43129,11 @@ var builtinRules = [
42924
43129
  goErrorWrapWithoutContextRule,
42925
43130
  goNilSliceVsEmptyRule,
42926
43131
  goStructTagInconsistencyRule,
43132
+ javaCommandInjectionRule,
43133
+ javaHardcodedCredentialRule,
43134
+ javaSqlStringConcatRule,
43135
+ javaSystemOutPrintlnRule,
43136
+ javaThreadSleepInLoopRule,
42927
43137
  kotlinCoroutineGlobalScopeRule,
42928
43138
  kotlinDataClassDefaultsOveruseRule,
42929
43139
  kotlinForceUnwrapRule,
@@ -45231,6 +45441,86 @@ var signal_strength_default = {
45231
45441
  _v9Precision: 0.0271,
45232
45442
  defaultOff: true
45233
45443
  },
45444
+ "java/sql-string-concat": {
45445
+ recall: 0.01116,
45446
+ fpRate: 0.01892,
45447
+ ratio: 0.59,
45448
+ precision: 0.0691,
45449
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45450
+ verdict: "DORMANT",
45451
+ _calibrationNote: "v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.59 \u2014 fires 1.7x more on pre-2022 (neg) than post-2024 (pos). Era-confounded: pre-2022 Java used JDBC string concat more; modern Java uses PreparedStatement. Same direction as kotlin/sql-string-concat. 1664 total fires \u2014 Java uses ORMs (Hibernate) heavily so SQL concat is rare in both arms. defaultOff.",
45452
+ aiSpecific: false,
45453
+ _v9Verdict: "DORMANT",
45454
+ _v9Lift: 0.59,
45455
+ _v9Recall: 0.01116,
45456
+ _v9FpRate: 0.01892,
45457
+ _v9Precision: 0.0691,
45458
+ defaultOff: true
45459
+ },
45460
+ "java/hardcoded-credential": {
45461
+ recall: 0,
45462
+ fpRate: 4e-5,
45463
+ ratio: 0,
45464
+ precision: 0,
45465
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45466
+ verdict: "DORMANT",
45467
+ _calibrationNote: "v0.30: v9 Java calibration (81891 neg, 10305 pos). 3 fires total \u2014 only in pre-2022 (neg). Real secrets are in env vars / config files, not source. INSUFFICIENT_DATA: needs different corpus (CI configs, .env samples, leaked-secret datasets). defaultOff.",
45468
+ aiSpecific: false,
45469
+ _v9Verdict: "DORMANT",
45470
+ _v9Lift: 0,
45471
+ _v9Recall: 0,
45472
+ _v9FpRate: 4e-5,
45473
+ _v9Precision: 0,
45474
+ defaultOff: true
45475
+ },
45476
+ "java/thread-sleep-in-loop": {
45477
+ recall: 0.0228,
45478
+ fpRate: 0.02343,
45479
+ ratio: 0.97,
45480
+ precision: 0.1091,
45481
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45482
+ verdict: "DORMANT",
45483
+ _calibrationNote: "v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
45484
+ aiSpecific: false,
45485
+ _v9Verdict: "DORMANT",
45486
+ _v9Lift: 0.97,
45487
+ _v9Recall: 0.0228,
45488
+ _v9FpRate: 0.02343,
45489
+ _v9Precision: 0.1091,
45490
+ defaultOff: true
45491
+ },
45492
+ "java/system-out-println": {
45493
+ recall: 116e-5,
45494
+ fpRate: 67e-5,
45495
+ ratio: 1.73,
45496
+ precision: 0.1791,
45497
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45498
+ verdict: "OK",
45499
+ _calibrationNote: "v0.31: v9 Java calibration REFINED \u2014 rule now requires the file to import a real logger (SLF4J, Log4j2, java.util.logging) AND have System.out.println calls. This is the 'set up but didn't use' anti-pattern. ratio=1.73 (\u22651.5) \u2014 second positive-signal rule in v9 history. v0.30.0 unrefined version had ratio=3.29 but precision=29.3% (over 5800 fires diluted by legitimate System.out in non-slf4j files). v0.31.0 refined version: 12 TP, 55 FP \u2014 much more selective, but absolute count is too small for production-grade measurement. precision=17.91% (still below 50% USEFUL threshold). The refinement succeeded: the fires dropped 99% (5866\u219267) and precision went 29.3%\u219217.9% (a tradeoff \u2014 fewer fires but more targeted). The direction is preserved: post-2024 Java has slightly more 'slf4j import + System.out call' than pre-2022. defaultOff: still set true (precision below 50%).",
45500
+ aiSpecific: false,
45501
+ _v9Verdict: "OK",
45502
+ _v9Lift: 1.73,
45503
+ _v9Recall: 116e-5,
45504
+ _v9FpRate: 67e-5,
45505
+ _v9Precision: 0.1791,
45506
+ defaultOff: true
45507
+ },
45508
+ "java/command-injection": {
45509
+ recall: 0,
45510
+ fpRate: 1e-4,
45511
+ ratio: 0,
45512
+ precision: 0,
45513
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45514
+ verdict: "DORMANT",
45515
+ _calibrationNote: "v0.30: v9 Java calibration (81891 neg, 10305 pos). 8 fires total, all in neg. Command injection is rare in modern Java \u2014 most apps use ProcessBuilder with List<String> args, not Runtime.exec with concat. INSUFFICIENT_DATA: needs different corpus (security benchmarks, CVE samples). defaultOff.",
45516
+ aiSpecific: false,
45517
+ _v9Verdict: "DORMANT",
45518
+ _v9Lift: 0,
45519
+ _v9Recall: 0,
45520
+ _v9FpRate: 1e-4,
45521
+ _v9Precision: 0,
45522
+ defaultOff: true
45523
+ },
45234
45524
  "swift/force-unwrap": {
45235
45525
  recall: 0,
45236
45526
  fpRate: 0,