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.
@@ -37000,6 +37000,211 @@ var goStructTagInconsistencyRule = createRule({
37000
37000
  }
37001
37001
  });
37002
37002
 
37003
+ // src/rules/java/command-injection.ts
37004
+ var COMMAND_INVOCATION_REGEX = /\b(?:Runtime\.exec|Runtime\.getRuntime\(\)\.exec|ProcessBuilder)\s*\(/;
37005
+ var STRING_CONCAT_REGEX = /["'][^"']*["']\s*\+/;
37006
+ var javaCommandInjectionRule = createRule({
37007
+ id: "java/command-injection",
37008
+ category: "security",
37009
+ severity: "high",
37010
+ aiSpecific: false,
37011
+ description: "Runtime.exec() or ProcessBuilder with string concat \u2014 use List<String> args + validation",
37012
+ create(_context) {
37013
+ return {};
37014
+ },
37015
+ analyze(_context, facts) {
37016
+ const issues = [];
37017
+ const source = facts.v2?._source;
37018
+ if (!source) return issues;
37019
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37020
+ const lines = source.split("\n");
37021
+ for (let i = 0; i < lines.length; i++) {
37022
+ const line = lines[i];
37023
+ if (!COMMAND_INVOCATION_REGEX.test(line)) continue;
37024
+ if (!STRING_CONCAT_REGEX.test(line)) continue;
37025
+ issues.push({
37026
+ ruleId: "java/command-injection",
37027
+ category: "security",
37028
+ severity: "high",
37029
+ aiSpecific: false,
37030
+ message: `Command invocation with string concat at line ${i + 1}`,
37031
+ line: i + 1,
37032
+ column: 1,
37033
+ 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)."
37034
+ });
37035
+ }
37036
+ return issues;
37037
+ }
37038
+ });
37039
+
37040
+ // src/rules/java/hardcoded-credential.ts
37041
+ var KEY_REGEX = /\b(api[_-]?key|apikey|secret|token|password|auth|access[_-]?key|client[_-]?secret|private[_-]?key)\b\s*[=:]/i;
37042
+ var VALUE_REGEX = /["']([A-Za-z0-9_\-+/=.@*!]{16,})["']/;
37043
+ var FALSE_POSITIVES = /* @__PURE__ */ new Set([
37044
+ "passwordless",
37045
+ "tokenize",
37046
+ "tokenizer",
37047
+ "authorizationrequired",
37048
+ "authenticated",
37049
+ "authenticatortoken",
37050
+ "authtoken",
37051
+ "placeholder",
37052
+ "changeme"
37053
+ ]);
37054
+ var javaHardcodedCredentialRule = createRule({
37055
+ id: "java/hardcoded-credential",
37056
+ category: "security",
37057
+ severity: "high",
37058
+ aiSpecific: false,
37059
+ description: "Hardcoded credential literal \u2014 use env vars or a secrets manager",
37060
+ create(_context) {
37061
+ return {};
37062
+ },
37063
+ analyze(_context, facts) {
37064
+ const issues = [];
37065
+ const source = facts.v2?._source;
37066
+ if (!source) return issues;
37067
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37068
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
37069
+ const lines = source.split("\n");
37070
+ for (let i = 0; i < lines.length; i++) {
37071
+ const line = lines[i];
37072
+ if (!KEY_REGEX.test(line)) continue;
37073
+ const valueMatch = VALUE_REGEX.exec(line);
37074
+ if (!valueMatch) continue;
37075
+ const value = valueMatch[1];
37076
+ if (FALSE_POSITIVES.has(value.toLowerCase())) continue;
37077
+ if (!/[a-zA-Z]/.test(value) || !/[0-9]/.test(value)) continue;
37078
+ if (value.startsWith("$")) continue;
37079
+ issues.push({
37080
+ ruleId: "java/hardcoded-credential",
37081
+ category: "security",
37082
+ severity: "high",
37083
+ aiSpecific: false,
37084
+ message: `Hardcoded credential at line ${i + 1}`,
37085
+ line: i + 1,
37086
+ column: 1,
37087
+ 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)."
37088
+ });
37089
+ }
37090
+ return issues;
37091
+ }
37092
+ });
37093
+
37094
+ // src/rules/java/sql-string-concat.ts
37095
+ var SQL_KEYWORD_REGEX = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|CREATE\s+TABLE|DROP\s+TABLE|ALTER\s+TABLE)\b/i;
37096
+ var UNSAFE_REGEX = /\+/;
37097
+ var SAFE_REGEX = /(?:PreparedStatement|setParameter|setString|setInt|setLong|createQuery.*:.*\b(?:set|bind)|:name\b|\?\s*,)/;
37098
+ var javaSqlStringConcatRule = createRule({
37099
+ id: "java/sql-string-concat",
37100
+ category: "security",
37101
+ severity: "high",
37102
+ aiSpecific: false,
37103
+ description: "SQL query built via string concat \u2014 use PreparedStatement or setParameter()",
37104
+ create(_context) {
37105
+ return {};
37106
+ },
37107
+ analyze(_context, facts) {
37108
+ const issues = [];
37109
+ const source = facts.v2?._source;
37110
+ if (!source) return issues;
37111
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37112
+ const lines = source.split("\n");
37113
+ for (let i = 0; i < lines.length; i++) {
37114
+ const line = lines[i];
37115
+ if (!SQL_KEYWORD_REGEX.test(line)) continue;
37116
+ if (!UNSAFE_REGEX.test(line)) continue;
37117
+ if (SAFE_REGEX.test(line)) continue;
37118
+ issues.push({
37119
+ ruleId: "java/sql-string-concat",
37120
+ category: "security",
37121
+ severity: "high",
37122
+ aiSpecific: false,
37123
+ message: `SQL query built via string concat at line ${i + 1}`,
37124
+ line: i + 1,
37125
+ column: 1,
37126
+ 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).'
37127
+ });
37128
+ }
37129
+ return issues;
37130
+ }
37131
+ });
37132
+
37133
+ // src/rules/java/system-out-println.ts
37134
+ var SYSTEM_OUT_REGEX = /\bSystem\.out\.println\s*\(/g;
37135
+ var REAL_LOGGING_IMPORT_REGEX = /\bimport\s+(?:org\.slf4j\.|org\.apache\.logging\.log4j|org\.apache\.log4j|java\.util\.logging|com\.google\.common\.logging)/;
37136
+ var javaSystemOutPrintlnRule = createRule({
37137
+ id: "java/system-out-println",
37138
+ category: "logic",
37139
+ severity: "low",
37140
+ aiSpecific: false,
37141
+ description: "System.out.println() in a file that imports SLF4J/Log4j2 \u2014 use the declared logger instead",
37142
+ create(_context) {
37143
+ return {};
37144
+ },
37145
+ analyze(_context, facts) {
37146
+ const issues = [];
37147
+ const source = facts.v2?._source;
37148
+ if (!source) return issues;
37149
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37150
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
37151
+ if (!REAL_LOGGING_IMPORT_REGEX.test(source)) return issues;
37152
+ let m;
37153
+ SYSTEM_OUT_REGEX.lastIndex = 0;
37154
+ while ((m = SYSTEM_OUT_REGEX.exec(source)) !== null) {
37155
+ const line = source.slice(0, m.index).split("\n").length;
37156
+ issues.push({
37157
+ ruleId: "java/system-out-println",
37158
+ category: "logic",
37159
+ severity: "low",
37160
+ aiSpecific: false,
37161
+ message: `System.out.println() at line ${line} (file imports a real logger)`,
37162
+ line,
37163
+ column: 1,
37164
+ 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)."
37165
+ });
37166
+ }
37167
+ return issues;
37168
+ }
37169
+ });
37170
+
37171
+ // src/rules/java/thread-sleep-in-loop.ts
37172
+ var THREAD_SLEEP_REGEX = /\bThread\.sleep\s*\(/g;
37173
+ var javaThreadSleepInLoopRule = createRule({
37174
+ id: "java/thread-sleep-in-loop",
37175
+ category: "perf",
37176
+ severity: "medium",
37177
+ aiSpecific: false,
37178
+ description: "Thread.sleep() in a loop \u2014 use ScheduledExecutorService or BlockingQueue",
37179
+ create(_context) {
37180
+ return {};
37181
+ },
37182
+ analyze(_context, facts) {
37183
+ const issues = [];
37184
+ const source = facts.v2?._source;
37185
+ if (!source) return issues;
37186
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37187
+ if (!/\bThread\.sleep\s*\(/.test(source)) return issues;
37188
+ if (!/\b(?:for|while|do)\b/.test(source)) return issues;
37189
+ let m;
37190
+ THREAD_SLEEP_REGEX.lastIndex = 0;
37191
+ while ((m = THREAD_SLEEP_REGEX.exec(source)) !== null) {
37192
+ const line = source.slice(0, m.index).split("\n").length;
37193
+ issues.push({
37194
+ ruleId: "java/thread-sleep-in-loop",
37195
+ category: "perf",
37196
+ severity: "medium",
37197
+ aiSpecific: false,
37198
+ message: `Thread.sleep() at line ${line}`,
37199
+ line,
37200
+ column: 1,
37201
+ 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.'
37202
+ });
37203
+ }
37204
+ return issues;
37205
+ }
37206
+ });
37207
+
37003
37208
  // src/rules/kotlin/coroutine-global-scope.ts
37004
37209
  var GLOBAL_SCOPE_REGEX = /GlobalScope\s*\.\s*(?:launch|async|runBlocking)\s*[({]/g;
37005
37210
  var kotlinCoroutineGlobalScopeRule = createRule({
@@ -37132,9 +37337,9 @@ var kotlinForceUnwrapRule = createRule({
37132
37337
  });
37133
37338
 
37134
37339
  // src/rules/kotlin/hardcoded-credential.ts
37135
- var KEY_REGEX = /\b(api[_-]?key|apikey|secret|token|password|auth|access[_-]?key|client[_-]?secret|private[_-]?key)\b\s*[=:]/i;
37136
- var VALUE_REGEX = /["']([A-Za-z0-9_\-+/=.@*!]{16,})["']/;
37137
- var FALSE_POSITIVES = /* @__PURE__ */ new Set([
37340
+ var KEY_REGEX2 = /\b(api[_-]?key|apikey|secret|token|password|auth|access[_-]?key|client[_-]?secret|private[_-]?key)\b\s*[=:]/i;
37341
+ var VALUE_REGEX2 = /["']([A-Za-z0-9_\-+/=.@*!]{16,})["']/;
37342
+ var FALSE_POSITIVES2 = /* @__PURE__ */ new Set([
37138
37343
  "passwordless",
37139
37344
  "tokenize",
37140
37345
  "tokenizer",
@@ -37164,11 +37369,11 @@ var kotlinHardcodedCredentialRule = createRule({
37164
37369
  const lines = source.split("\n");
37165
37370
  for (let i = 0; i < lines.length; i++) {
37166
37371
  const line = lines[i];
37167
- if (!KEY_REGEX.test(line)) continue;
37168
- const valueMatch = VALUE_REGEX.exec(line);
37372
+ if (!KEY_REGEX2.test(line)) continue;
37373
+ const valueMatch = VALUE_REGEX2.exec(line);
37169
37374
  if (!valueMatch) continue;
37170
37375
  const value = valueMatch[1];
37171
- if (FALSE_POSITIVES.has(value.toLowerCase())) continue;
37376
+ if (FALSE_POSITIVES2.has(value.toLowerCase())) continue;
37172
37377
  if (!/[a-zA-Z]/.test(value) || !/[0-9]/.test(value)) continue;
37173
37378
  if (value.startsWith("$")) continue;
37174
37379
  if (/\/test\//i.test(facts.filePath) || /\.test\.kts?$/i.test(facts.filePath)) continue;
@@ -37239,7 +37444,7 @@ var kotlinObjectSingletonMisuseRule = createRule({
37239
37444
 
37240
37445
  // src/rules/kotlin/println-as-log.ts
37241
37446
  var PRINTLN_REGEX = /\bprintln\s*\(/g;
37242
- 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)/;
37447
+ 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)/;
37243
37448
  var kotlinPrintlnAsLogRule = createRule({
37244
37449
  id: "kotlin/println-as-log",
37245
37450
  category: "logic",
@@ -37255,7 +37460,7 @@ var kotlinPrintlnAsLogRule = createRule({
37255
37460
  if (!source) return issues;
37256
37461
  if (!/\.kts?$/i.test(facts.filePath)) return issues;
37257
37462
  if (/\/test\//i.test(facts.filePath) || /\.test\.kts?$/i.test(facts.filePath)) return issues;
37258
- if (REAL_LOGGING_IMPORT_REGEX.test(source)) return issues;
37463
+ if (REAL_LOGGING_IMPORT_REGEX2.test(source)) return issues;
37259
37464
  let m;
37260
37465
  PRINTLN_REGEX.lastIndex = 0;
37261
37466
  while ((m = PRINTLN_REGEX.exec(source)) !== null) {
@@ -37355,9 +37560,9 @@ var kotlinRunBlockingMisuseRule = createRule({
37355
37560
  });
37356
37561
 
37357
37562
  // src/rules/kotlin/sql-string-concat.ts
37358
- var SQL_KEYWORD_REGEX = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|CREATE\s+TABLE|DROP\s+TABLE|ALTER\s+TABLE)\b/i;
37359
- var UNSAFE_REGEX = /(?:\+|\$\{)/;
37360
- var SAFE_REGEX = /(?:PreparedStatement|setParameter|setString|setInt|setLong|bind|:name|:\\?\\?|\?\\s*,)/;
37563
+ var SQL_KEYWORD_REGEX2 = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|CREATE\s+TABLE|DROP\s+TABLE|ALTER\s+TABLE)\b/i;
37564
+ var UNSAFE_REGEX2 = /(?:\+|\$\{)/;
37565
+ var SAFE_REGEX2 = /(?:PreparedStatement|setParameter|setString|setInt|setLong|bind|:name|:\\?\\?|\?\\s*,)/;
37361
37566
  var kotlinSqlStringConcatRule = createRule({
37362
37567
  id: "kotlin/sql-string-concat",
37363
37568
  category: "security",
@@ -37375,9 +37580,9 @@ var kotlinSqlStringConcatRule = createRule({
37375
37580
  const lines = source.split("\n");
37376
37581
  for (let i = 0; i < lines.length; i++) {
37377
37582
  const line = lines[i];
37378
- if (!SQL_KEYWORD_REGEX.test(line)) continue;
37379
- if (!UNSAFE_REGEX.test(line)) continue;
37380
- if (SAFE_REGEX.test(line)) continue;
37583
+ if (!SQL_KEYWORD_REGEX2.test(line)) continue;
37584
+ if (!UNSAFE_REGEX2.test(line)) continue;
37585
+ if (SAFE_REGEX2.test(line)) continue;
37381
37586
  issues.push({
37382
37587
  ruleId: "kotlin/sql-string-concat",
37383
37588
  category: "security",
@@ -37394,7 +37599,7 @@ var kotlinSqlStringConcatRule = createRule({
37394
37599
  });
37395
37600
 
37396
37601
  // src/rules/kotlin/string-concat-loop.ts
37397
- var STRING_CONCAT_REGEX = /\b(\w+)\s*=\s*\1\s*\+\s*[^;}]+[;}\n]/g;
37602
+ var STRING_CONCAT_REGEX2 = /\b(\w+)\s*=\s*\1\s*\+\s*[^;}]+[;}\n]/g;
37398
37603
  var kotlinStringConcatLoopRule = createRule({
37399
37604
  id: "kotlin/string-concat-loop",
37400
37605
  category: "perf",
@@ -37411,8 +37616,8 @@ var kotlinStringConcatLoopRule = createRule({
37411
37616
  if (!/\.kts?$/i.test(facts.filePath)) return issues;
37412
37617
  if (!/\b(?:for|while|repeat|forEach)\b/.test(source)) return issues;
37413
37618
  let m;
37414
- STRING_CONCAT_REGEX.lastIndex = 0;
37415
- while ((m = STRING_CONCAT_REGEX.exec(source)) !== null) {
37619
+ STRING_CONCAT_REGEX2.lastIndex = 0;
37620
+ while ((m = STRING_CONCAT_REGEX2.exec(source)) !== null) {
37416
37621
  const line = source.slice(0, m.index).split("\n").length;
37417
37622
  const lineText = source.slice(0, m.index).split("\n").pop() ?? "";
37418
37623
  if (/\.append\s*\(/.test(lineText)) continue;
@@ -42953,6 +43158,11 @@ var builtinRules = [
42953
43158
  goErrorWrapWithoutContextRule,
42954
43159
  goNilSliceVsEmptyRule,
42955
43160
  goStructTagInconsistencyRule,
43161
+ javaCommandInjectionRule,
43162
+ javaHardcodedCredentialRule,
43163
+ javaSqlStringConcatRule,
43164
+ javaSystemOutPrintlnRule,
43165
+ javaThreadSleepInLoopRule,
42956
43166
  kotlinCoroutineGlobalScopeRule,
42957
43167
  kotlinDataClassDefaultsOveruseRule,
42958
43168
  kotlinForceUnwrapRule,
@@ -45260,6 +45470,86 @@ var signal_strength_default = {
45260
45470
  _v9Precision: 0.0271,
45261
45471
  defaultOff: true
45262
45472
  },
45473
+ "java/sql-string-concat": {
45474
+ recall: 0.01116,
45475
+ fpRate: 0.01892,
45476
+ ratio: 0.59,
45477
+ precision: 0.0691,
45478
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45479
+ verdict: "DORMANT",
45480
+ _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.",
45481
+ aiSpecific: false,
45482
+ _v9Verdict: "DORMANT",
45483
+ _v9Lift: 0.59,
45484
+ _v9Recall: 0.01116,
45485
+ _v9FpRate: 0.01892,
45486
+ _v9Precision: 0.0691,
45487
+ defaultOff: true
45488
+ },
45489
+ "java/hardcoded-credential": {
45490
+ recall: 0,
45491
+ fpRate: 4e-5,
45492
+ ratio: 0,
45493
+ precision: 0,
45494
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45495
+ verdict: "DORMANT",
45496
+ _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.",
45497
+ aiSpecific: false,
45498
+ _v9Verdict: "DORMANT",
45499
+ _v9Lift: 0,
45500
+ _v9Recall: 0,
45501
+ _v9FpRate: 4e-5,
45502
+ _v9Precision: 0,
45503
+ defaultOff: true
45504
+ },
45505
+ "java/thread-sleep-in-loop": {
45506
+ recall: 0.0228,
45507
+ fpRate: 0.02343,
45508
+ ratio: 0.97,
45509
+ precision: 0.1091,
45510
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45511
+ verdict: "DORMANT",
45512
+ _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.",
45513
+ aiSpecific: false,
45514
+ _v9Verdict: "DORMANT",
45515
+ _v9Lift: 0.97,
45516
+ _v9Recall: 0.0228,
45517
+ _v9FpRate: 0.02343,
45518
+ _v9Precision: 0.1091,
45519
+ defaultOff: true
45520
+ },
45521
+ "java/system-out-println": {
45522
+ recall: 116e-5,
45523
+ fpRate: 67e-5,
45524
+ ratio: 1.73,
45525
+ precision: 0.1791,
45526
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45527
+ verdict: "OK",
45528
+ _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%).",
45529
+ aiSpecific: false,
45530
+ _v9Verdict: "OK",
45531
+ _v9Lift: 1.73,
45532
+ _v9Recall: 116e-5,
45533
+ _v9FpRate: 67e-5,
45534
+ _v9Precision: 0.1791,
45535
+ defaultOff: true
45536
+ },
45537
+ "java/command-injection": {
45538
+ recall: 0,
45539
+ fpRate: 1e-4,
45540
+ ratio: 0,
45541
+ precision: 0,
45542
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45543
+ verdict: "DORMANT",
45544
+ _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.",
45545
+ aiSpecific: false,
45546
+ _v9Verdict: "DORMANT",
45547
+ _v9Lift: 0,
45548
+ _v9Recall: 0,
45549
+ _v9FpRate: 1e-4,
45550
+ _v9Precision: 0,
45551
+ defaultOff: true
45552
+ },
45263
45553
  "swift/force-unwrap": {
45264
45554
  recall: 0,
45265
45555
  fpRate: 0,