slopbrick 0.34.10 → 0.35.1

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.
@@ -37139,6 +37139,67 @@ var javaHardcodedCredentialRule = createRule({
37139
37139
  }
37140
37140
  });
37141
37141
 
37142
+ // src/rules/java/lost-stack-trace.ts
37143
+ var THROW_NEW_REGEX = /throw\s+new\s+(\w+(?:Exception|Error|Throwable))\s*\(([^)]*)\)/g;
37144
+ var CATCH_REGEX = /catch\s*\(\s*(?:final\s+)?[\w<>,\s]+\s+(\w+)\s*\)\s*\{/g;
37145
+ var javaLostStackTraceRule = createRule({
37146
+ id: "java/lost-stack-trace",
37147
+ category: "logic",
37148
+ severity: "medium",
37149
+ aiSpecific: false,
37150
+ description: "catch block throws a new exception without the original cause \u2014 stack trace is lost",
37151
+ create(_context) {
37152
+ return {};
37153
+ },
37154
+ analyze(_context, facts) {
37155
+ const issues = [];
37156
+ const source = facts.v2?._source;
37157
+ if (!source) return issues;
37158
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37159
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
37160
+ const catchBlocks = [];
37161
+ let cm;
37162
+ CATCH_REGEX.lastIndex = 0;
37163
+ while ((cm = CATCH_REGEX.exec(source)) !== null) {
37164
+ const catchStart = cm.index;
37165
+ const exVar = cm[1];
37166
+ let depth = 1;
37167
+ let i = cm.index + cm[0].length;
37168
+ while (i < source.length && depth > 0) {
37169
+ const ch = source[i];
37170
+ if (ch === "{") depth++;
37171
+ else if (ch === "}") depth--;
37172
+ i++;
37173
+ }
37174
+ catchBlocks.push({ start: catchStart, end: i, exVar });
37175
+ }
37176
+ let m;
37177
+ THROW_NEW_REGEX.lastIndex = 0;
37178
+ while ((m = THROW_NEW_REGEX.exec(source)) !== null) {
37179
+ const throwPos = m.index;
37180
+ const args = m[2].trim();
37181
+ const enclosing = catchBlocks.find(
37182
+ (cb) => cb.start < throwPos && throwPos < cb.end
37183
+ );
37184
+ if (!enclosing) continue;
37185
+ const exVarPattern = new RegExp(`\\b${enclosing.exVar}\\b`);
37186
+ if (exVarPattern.test(args)) continue;
37187
+ const line = source.slice(0, throwPos).split("\n").length;
37188
+ issues.push({
37189
+ ruleId: "java/lost-stack-trace",
37190
+ category: "logic",
37191
+ severity: "medium",
37192
+ aiSpecific: false,
37193
+ message: `throw new ${m[1]}(${args}) \u2014 original exception \`${enclosing.exVar}\` is not included as cause`,
37194
+ line,
37195
+ column: 1,
37196
+ advice: `The catch block declares exception variable \`${enclosing.exVar}\` but the throw statement doesn't include it as a cause. The original stack trace is lost. Fix: \`throw new ${m[1]}("...", ${enclosing.exVar})\` \u2014 the second argument to the exception constructor is the cause, which Java's Throwable framework preserves in the stack trace chain. Reference: java/lost-stack-trace v0.35.1 (Raidar-inspired content-based detection of AI-polished error handling).`
37197
+ });
37198
+ }
37199
+ return issues;
37200
+ }
37201
+ });
37202
+
37142
37203
  // src/rules/java/sql-string-concat.ts
37143
37204
  var SQL_KEYWORD_REGEX = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|CREATE\s+TABLE|DROP\s+TABLE|ALTER\s+TABLE)\b/i;
37144
37205
  var UNSAFE_REGEX = /\+/;
@@ -37185,6 +37246,61 @@ var javaSqlStringConcatRule = createRule({
37185
37246
  }
37186
37247
  });
37187
37248
 
37249
+ // src/rules/java/suspicious-implementation.ts
37250
+ var STRONG_VERB_REGEX = /(?:validate|validates|validated|verifies?|verify|check|checks?|checked|ensures?|ensure|ensured|verifies|verified|sanitize|sanitizes|sanitized|escape|escapes|escaped|authenticate|authenticates|authenticated|authorize|authorizes|authorized|audit|audits|audited|inspect|inspects|inspected|filter|filters?|filtered|normalize|normalizes|normalized|encrypt|encrypts|encrypted|decrypt|decrypts|decrypted|hash|hashes|hashed|sign|signs?|signed|compress|compresses|compressed|decompress|decompresses|decompressed|parse|parses|parsed|format|formats?|formatted)(?=[A-Z]|[^a-zA-Z]|$)/i;
37251
+ var METHOD_DECL_REGEX = /(?:public|private|protected)\s+(?:static\s+)?[\w<>,\s\[\]]+?\s+(\w+)\s*\([^)]*\)\s*\{([^{}]*)\}/g;
37252
+ var EMPTY_BODY_REGEX = /^\s*$/;
37253
+ var RETURN_CONSTANT_REGEX = /^\s*return\s+(?:null|true|false|0|1|0L|0\.0|"")\s*;\s*$/;
37254
+ var RETURN_INPUT_REGEX = /^\s*return\s+(\w+)\s*;\s*$/;
37255
+ var JUST_THROW_REGEX = /^\s*throw\s+new\s+(?:UnsupportedOperationException|UnsupportedOperationException\([^)]*\)|RuntimeException\([^)]*\)|IllegalStateException\([^)]*\))\s*;\s*$/;
37256
+ var javaSuspiciousImplementationRule = createRule({
37257
+ id: "java/suspicious-implementation",
37258
+ category: "security",
37259
+ severity: "high",
37260
+ aiSpecific: false,
37261
+ description: "function name suggests validation/encryption/auth but body is empty or trivially wrong \u2014 content mismatch",
37262
+ create(_context) {
37263
+ return {};
37264
+ },
37265
+ analyze(_context, facts) {
37266
+ const issues = [];
37267
+ const source = facts.v2?._source;
37268
+ if (!source) return issues;
37269
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37270
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
37271
+ let m;
37272
+ METHOD_DECL_REGEX.lastIndex = 0;
37273
+ while ((m = METHOD_DECL_REGEX.exec(source)) !== null) {
37274
+ const methodName = m[1];
37275
+ const body = m[2];
37276
+ if (!STRONG_VERB_REGEX.test(methodName)) continue;
37277
+ let reason = null;
37278
+ if (EMPTY_BODY_REGEX.test(body)) {
37279
+ reason = "empty body";
37280
+ } else if (RETURN_CONSTANT_REGEX.test(body)) {
37281
+ reason = "returns a constant (null/true/false/0/1)";
37282
+ } else if (RETURN_INPUT_REGEX.test(body)) {
37283
+ reason = "returns the input unchanged (pass-through stub)";
37284
+ } else if (JUST_THROW_REGEX.test(body)) {
37285
+ reason = "throws UnsupportedOperationException \u2014 not implemented";
37286
+ }
37287
+ if (!reason) continue;
37288
+ const line = source.slice(0, m.index).split("\n").length;
37289
+ issues.push({
37290
+ ruleId: "java/suspicious-implementation",
37291
+ category: "security",
37292
+ severity: "high",
37293
+ aiSpecific: false,
37294
+ message: `function ${methodName} (${reason}) \u2014 content-based mismatch`,
37295
+ line,
37296
+ column: 1,
37297
+ advice: `The function name "${methodName}" suggests a real validation/encryption/auth/filter operation, but the body is ${reason}. This is a content mismatch \u2014 the code's claimed behavior doesn't match its actual behavior. Real production code that ships this is a silent security failure (OWASP A04:2021 \u2014 Insecure Design). The fix is to either implement the operation or rename the function to reflect what it actually does. Reference: java/suspicious-implementation v0.35.0 (CoCoNUTS-inspired content-based detection).`
37298
+ });
37299
+ }
37300
+ return issues;
37301
+ }
37302
+ });
37303
+
37188
37304
  // src/rules/java/system-out-println.ts
37189
37305
  var SYSTEM_OUT_REGEX = /\bSystem\.out\.println\s*\(/g;
37190
37306
  var REAL_LOGGING_IMPORT_REGEX = /\bimport\s+(?:org\.slf4j\.|org\.apache\.logging\.log4j|org\.apache\.log4j|java\.util\.logging|com\.google\.common\.logging)/;
@@ -43320,7 +43436,9 @@ var builtinRules = [
43320
43436
  goStructTagInconsistencyRule,
43321
43437
  javaCommandInjectionRule,
43322
43438
  javaHardcodedCredentialRule,
43439
+ javaLostStackTraceRule,
43323
43440
  javaSqlStringConcatRule,
43441
+ javaSuspiciousImplementationRule,
43324
43442
  javaSystemOutPrintlnRule,
43325
43443
  javaThreadSleepInLoopRule,
43326
43444
  kotlinCoroutineGlobalScopeRule,
@@ -45710,6 +45828,38 @@ var signal_strength_default = {
45710
45828
  _v9Precision: 0,
45711
45829
  defaultOff: true
45712
45830
  },
45831
+ "java/suspicious-implementation": {
45832
+ recall: 0,
45833
+ fpRate: 0,
45834
+ ratio: 0,
45835
+ precision: 0,
45836
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45837
+ verdict: "OK",
45838
+ _calibrationNote: "v0.35.0: NEW RULE \u2014 content-based detection (CoCoNUTS-inspired). Detects function names that claim a strong operation (validate, encrypt, hash, sanitize, check, verify, authenticate, etc.) but whose body is trivially empty, returns a constant, or returns the input unchanged. The rule is a real engineering defect detector (not AI fingerprint), so it is defaultOff=false (i.e. ON by default) and verdict=OK. The CoCoNUTS paper (2025) showed that style-based AI-detectors fail under paraphrasing; this rule looks at semantic content (function body behavior) rather than surface features. v9 calibration is pending \u2014 the rule was added at the end of the v0.34.X refinement series and has not been measured against the v9 corpus yet. The expected impact: low recall (most production code doesn't have these stubs) but high precision (when it fires, it's a real bug). The full v9 calibration is the next step for v0.35.0.x patches.",
45839
+ aiSpecific: false,
45840
+ _v9Verdict: "OK",
45841
+ _v9Lift: 0,
45842
+ _v9Recall: 0,
45843
+ _v9FpRate: 0,
45844
+ _v9Precision: 0,
45845
+ defaultOff: false
45846
+ },
45847
+ "java/lost-stack-trace": {
45848
+ recall: 0,
45849
+ fpRate: 0,
45850
+ ratio: 0,
45851
+ precision: 0,
45852
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45853
+ verdict: "OK",
45854
+ _calibrationNote: "v0.35.1: NEW RULE \u2014 Raidar-inspired content-based detection. The Raidar paper (ICLR 2024) showed that LLMs tend to 'polish' error handling by wrapping exceptions but losing the original cause; the inverse observation is that AI-generated code often has this pattern. This rule detects catch blocks that throw a new exception WITHOUT including the original exception as a cause \u2014 the original stack trace is lost. The rule is a real engineering defect detector (not AI fingerprint), so it is defaultOff=false (ON by default) and verdict=OK. v9 calibration is pending. The expected impact: low recall (most production code preserves the original exception) but very high precision (when it fires, it's a real bug \u2014 debugging without the stack trace is impossible).",
45855
+ aiSpecific: false,
45856
+ _v9Verdict: "OK",
45857
+ _v9Lift: 0,
45858
+ _v9Recall: 0,
45859
+ _v9FpRate: 0,
45860
+ _v9Precision: 0,
45861
+ defaultOff: false
45862
+ },
45713
45863
  "swift/force-unwrap": {
45714
45864
  recall: 0.10211,
45715
45865
  fpRate: 0.21462,
@@ -37110,6 +37110,67 @@ var javaHardcodedCredentialRule = createRule({
37110
37110
  }
37111
37111
  });
37112
37112
 
37113
+ // src/rules/java/lost-stack-trace.ts
37114
+ var THROW_NEW_REGEX = /throw\s+new\s+(\w+(?:Exception|Error|Throwable))\s*\(([^)]*)\)/g;
37115
+ var CATCH_REGEX = /catch\s*\(\s*(?:final\s+)?[\w<>,\s]+\s+(\w+)\s*\)\s*\{/g;
37116
+ var javaLostStackTraceRule = createRule({
37117
+ id: "java/lost-stack-trace",
37118
+ category: "logic",
37119
+ severity: "medium",
37120
+ aiSpecific: false,
37121
+ description: "catch block throws a new exception without the original cause \u2014 stack trace is lost",
37122
+ create(_context) {
37123
+ return {};
37124
+ },
37125
+ analyze(_context, facts) {
37126
+ const issues = [];
37127
+ const source = facts.v2?._source;
37128
+ if (!source) return issues;
37129
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37130
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
37131
+ const catchBlocks = [];
37132
+ let cm;
37133
+ CATCH_REGEX.lastIndex = 0;
37134
+ while ((cm = CATCH_REGEX.exec(source)) !== null) {
37135
+ const catchStart = cm.index;
37136
+ const exVar = cm[1];
37137
+ let depth = 1;
37138
+ let i = cm.index + cm[0].length;
37139
+ while (i < source.length && depth > 0) {
37140
+ const ch = source[i];
37141
+ if (ch === "{") depth++;
37142
+ else if (ch === "}") depth--;
37143
+ i++;
37144
+ }
37145
+ catchBlocks.push({ start: catchStart, end: i, exVar });
37146
+ }
37147
+ let m;
37148
+ THROW_NEW_REGEX.lastIndex = 0;
37149
+ while ((m = THROW_NEW_REGEX.exec(source)) !== null) {
37150
+ const throwPos = m.index;
37151
+ const args = m[2].trim();
37152
+ const enclosing = catchBlocks.find(
37153
+ (cb) => cb.start < throwPos && throwPos < cb.end
37154
+ );
37155
+ if (!enclosing) continue;
37156
+ const exVarPattern = new RegExp(`\\b${enclosing.exVar}\\b`);
37157
+ if (exVarPattern.test(args)) continue;
37158
+ const line = source.slice(0, throwPos).split("\n").length;
37159
+ issues.push({
37160
+ ruleId: "java/lost-stack-trace",
37161
+ category: "logic",
37162
+ severity: "medium",
37163
+ aiSpecific: false,
37164
+ message: `throw new ${m[1]}(${args}) \u2014 original exception \`${enclosing.exVar}\` is not included as cause`,
37165
+ line,
37166
+ column: 1,
37167
+ advice: `The catch block declares exception variable \`${enclosing.exVar}\` but the throw statement doesn't include it as a cause. The original stack trace is lost. Fix: \`throw new ${m[1]}("...", ${enclosing.exVar})\` \u2014 the second argument to the exception constructor is the cause, which Java's Throwable framework preserves in the stack trace chain. Reference: java/lost-stack-trace v0.35.1 (Raidar-inspired content-based detection of AI-polished error handling).`
37168
+ });
37169
+ }
37170
+ return issues;
37171
+ }
37172
+ });
37173
+
37113
37174
  // src/rules/java/sql-string-concat.ts
37114
37175
  var SQL_KEYWORD_REGEX = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|CREATE\s+TABLE|DROP\s+TABLE|ALTER\s+TABLE)\b/i;
37115
37176
  var UNSAFE_REGEX = /\+/;
@@ -37156,6 +37217,61 @@ var javaSqlStringConcatRule = createRule({
37156
37217
  }
37157
37218
  });
37158
37219
 
37220
+ // src/rules/java/suspicious-implementation.ts
37221
+ var STRONG_VERB_REGEX = /(?:validate|validates|validated|verifies?|verify|check|checks?|checked|ensures?|ensure|ensured|verifies|verified|sanitize|sanitizes|sanitized|escape|escapes|escaped|authenticate|authenticates|authenticated|authorize|authorizes|authorized|audit|audits|audited|inspect|inspects|inspected|filter|filters?|filtered|normalize|normalizes|normalized|encrypt|encrypts|encrypted|decrypt|decrypts|decrypted|hash|hashes|hashed|sign|signs?|signed|compress|compresses|compressed|decompress|decompresses|decompressed|parse|parses|parsed|format|formats?|formatted)(?=[A-Z]|[^a-zA-Z]|$)/i;
37222
+ var METHOD_DECL_REGEX = /(?:public|private|protected)\s+(?:static\s+)?[\w<>,\s\[\]]+?\s+(\w+)\s*\([^)]*\)\s*\{([^{}]*)\}/g;
37223
+ var EMPTY_BODY_REGEX = /^\s*$/;
37224
+ var RETURN_CONSTANT_REGEX = /^\s*return\s+(?:null|true|false|0|1|0L|0\.0|"")\s*;\s*$/;
37225
+ var RETURN_INPUT_REGEX = /^\s*return\s+(\w+)\s*;\s*$/;
37226
+ var JUST_THROW_REGEX = /^\s*throw\s+new\s+(?:UnsupportedOperationException|UnsupportedOperationException\([^)]*\)|RuntimeException\([^)]*\)|IllegalStateException\([^)]*\))\s*;\s*$/;
37227
+ var javaSuspiciousImplementationRule = createRule({
37228
+ id: "java/suspicious-implementation",
37229
+ category: "security",
37230
+ severity: "high",
37231
+ aiSpecific: false,
37232
+ description: "function name suggests validation/encryption/auth but body is empty or trivially wrong \u2014 content mismatch",
37233
+ create(_context) {
37234
+ return {};
37235
+ },
37236
+ analyze(_context, facts) {
37237
+ const issues = [];
37238
+ const source = facts.v2?._source;
37239
+ if (!source) return issues;
37240
+ if (!/\.java$/i.test(facts.filePath)) return issues;
37241
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
37242
+ let m;
37243
+ METHOD_DECL_REGEX.lastIndex = 0;
37244
+ while ((m = METHOD_DECL_REGEX.exec(source)) !== null) {
37245
+ const methodName = m[1];
37246
+ const body = m[2];
37247
+ if (!STRONG_VERB_REGEX.test(methodName)) continue;
37248
+ let reason = null;
37249
+ if (EMPTY_BODY_REGEX.test(body)) {
37250
+ reason = "empty body";
37251
+ } else if (RETURN_CONSTANT_REGEX.test(body)) {
37252
+ reason = "returns a constant (null/true/false/0/1)";
37253
+ } else if (RETURN_INPUT_REGEX.test(body)) {
37254
+ reason = "returns the input unchanged (pass-through stub)";
37255
+ } else if (JUST_THROW_REGEX.test(body)) {
37256
+ reason = "throws UnsupportedOperationException \u2014 not implemented";
37257
+ }
37258
+ if (!reason) continue;
37259
+ const line = source.slice(0, m.index).split("\n").length;
37260
+ issues.push({
37261
+ ruleId: "java/suspicious-implementation",
37262
+ category: "security",
37263
+ severity: "high",
37264
+ aiSpecific: false,
37265
+ message: `function ${methodName} (${reason}) \u2014 content-based mismatch`,
37266
+ line,
37267
+ column: 1,
37268
+ advice: `The function name "${methodName}" suggests a real validation/encryption/auth/filter operation, but the body is ${reason}. This is a content mismatch \u2014 the code's claimed behavior doesn't match its actual behavior. Real production code that ships this is a silent security failure (OWASP A04:2021 \u2014 Insecure Design). The fix is to either implement the operation or rename the function to reflect what it actually does. Reference: java/suspicious-implementation v0.35.0 (CoCoNUTS-inspired content-based detection).`
37269
+ });
37270
+ }
37271
+ return issues;
37272
+ }
37273
+ });
37274
+
37159
37275
  // src/rules/java/system-out-println.ts
37160
37276
  var SYSTEM_OUT_REGEX = /\bSystem\.out\.println\s*\(/g;
37161
37277
  var REAL_LOGGING_IMPORT_REGEX = /\bimport\s+(?:org\.slf4j\.|org\.apache\.logging\.log4j|org\.apache\.log4j|java\.util\.logging|com\.google\.common\.logging)/;
@@ -43291,7 +43407,9 @@ var builtinRules = [
43291
43407
  goStructTagInconsistencyRule,
43292
43408
  javaCommandInjectionRule,
43293
43409
  javaHardcodedCredentialRule,
43410
+ javaLostStackTraceRule,
43294
43411
  javaSqlStringConcatRule,
43412
+ javaSuspiciousImplementationRule,
43295
43413
  javaSystemOutPrintlnRule,
43296
43414
  javaThreadSleepInLoopRule,
43297
43415
  kotlinCoroutineGlobalScopeRule,
@@ -45681,6 +45799,38 @@ var signal_strength_default = {
45681
45799
  _v9Precision: 0,
45682
45800
  defaultOff: true
45683
45801
  },
45802
+ "java/suspicious-implementation": {
45803
+ recall: 0,
45804
+ fpRate: 0,
45805
+ ratio: 0,
45806
+ precision: 0,
45807
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45808
+ verdict: "OK",
45809
+ _calibrationNote: "v0.35.0: NEW RULE \u2014 content-based detection (CoCoNUTS-inspired). Detects function names that claim a strong operation (validate, encrypt, hash, sanitize, check, verify, authenticate, etc.) but whose body is trivially empty, returns a constant, or returns the input unchanged. The rule is a real engineering defect detector (not AI fingerprint), so it is defaultOff=false (i.e. ON by default) and verdict=OK. The CoCoNUTS paper (2025) showed that style-based AI-detectors fail under paraphrasing; this rule looks at semantic content (function body behavior) rather than surface features. v9 calibration is pending \u2014 the rule was added at the end of the v0.34.X refinement series and has not been measured against the v9 corpus yet. The expected impact: low recall (most production code doesn't have these stubs) but high precision (when it fires, it's a real bug). The full v9 calibration is the next step for v0.35.0.x patches.",
45810
+ aiSpecific: false,
45811
+ _v9Verdict: "OK",
45812
+ _v9Lift: 0,
45813
+ _v9Recall: 0,
45814
+ _v9FpRate: 0,
45815
+ _v9Precision: 0,
45816
+ defaultOff: false
45817
+ },
45818
+ "java/lost-stack-trace": {
45819
+ recall: 0,
45820
+ fpRate: 0,
45821
+ ratio: 0,
45822
+ precision: 0,
45823
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
45824
+ verdict: "OK",
45825
+ _calibrationNote: "v0.35.1: NEW RULE \u2014 Raidar-inspired content-based detection. The Raidar paper (ICLR 2024) showed that LLMs tend to 'polish' error handling by wrapping exceptions but losing the original cause; the inverse observation is that AI-generated code often has this pattern. This rule detects catch blocks that throw a new exception WITHOUT including the original exception as a cause \u2014 the original stack trace is lost. The rule is a real engineering defect detector (not AI fingerprint), so it is defaultOff=false (ON by default) and verdict=OK. v9 calibration is pending. The expected impact: low recall (most production code preserves the original exception) but very high precision (when it fires, it's a real bug \u2014 debugging without the stack trace is impossible).",
45826
+ aiSpecific: false,
45827
+ _v9Verdict: "OK",
45828
+ _v9Lift: 0,
45829
+ _v9Recall: 0,
45830
+ _v9FpRate: 0,
45831
+ _v9Precision: 0,
45832
+ defaultOff: false
45833
+ },
45684
45834
  "swift/force-unwrap": {
45685
45835
  recall: 0.10211,
45686
45836
  fpRate: 0.21462,
package/dist/index.cjs CHANGED
@@ -36,7 +36,7 @@ var VERSION;
36
36
  var init_header = __esm({
37
37
  "src/types/_header.ts"() {
38
38
  "use strict";
39
- VERSION = "0.34.10";
39
+ VERSION = "0.35.1";
40
40
  }
41
41
  });
42
42
 
@@ -30797,6 +30797,74 @@ var init_hardcoded_credential = __esm({
30797
30797
  }
30798
30798
  });
30799
30799
 
30800
+ // src/rules/java/lost-stack-trace.ts
30801
+ var THROW_NEW_REGEX, CATCH_REGEX, javaLostStackTraceRule;
30802
+ var init_lost_stack_trace = __esm({
30803
+ "src/rules/java/lost-stack-trace.ts"() {
30804
+ "use strict";
30805
+ init_rule();
30806
+ THROW_NEW_REGEX = /throw\s+new\s+(\w+(?:Exception|Error|Throwable))\s*\(([^)]*)\)/g;
30807
+ CATCH_REGEX = /catch\s*\(\s*(?:final\s+)?[\w<>,\s]+\s+(\w+)\s*\)\s*\{/g;
30808
+ javaLostStackTraceRule = createRule({
30809
+ id: "java/lost-stack-trace",
30810
+ category: "logic",
30811
+ severity: "medium",
30812
+ aiSpecific: false,
30813
+ description: "catch block throws a new exception without the original cause \u2014 stack trace is lost",
30814
+ create(_context) {
30815
+ return {};
30816
+ },
30817
+ analyze(_context, facts) {
30818
+ const issues = [];
30819
+ const source = facts.v2?._source;
30820
+ if (!source) return issues;
30821
+ if (!/\.java$/i.test(facts.filePath)) return issues;
30822
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
30823
+ const catchBlocks = [];
30824
+ let cm;
30825
+ CATCH_REGEX.lastIndex = 0;
30826
+ while ((cm = CATCH_REGEX.exec(source)) !== null) {
30827
+ const catchStart = cm.index;
30828
+ const exVar = cm[1];
30829
+ let depth = 1;
30830
+ let i = cm.index + cm[0].length;
30831
+ while (i < source.length && depth > 0) {
30832
+ const ch = source[i];
30833
+ if (ch === "{") depth++;
30834
+ else if (ch === "}") depth--;
30835
+ i++;
30836
+ }
30837
+ catchBlocks.push({ start: catchStart, end: i, exVar });
30838
+ }
30839
+ let m;
30840
+ THROW_NEW_REGEX.lastIndex = 0;
30841
+ while ((m = THROW_NEW_REGEX.exec(source)) !== null) {
30842
+ const throwPos = m.index;
30843
+ const args = m[2].trim();
30844
+ const enclosing = catchBlocks.find(
30845
+ (cb) => cb.start < throwPos && throwPos < cb.end
30846
+ );
30847
+ if (!enclosing) continue;
30848
+ const exVarPattern = new RegExp(`\\b${enclosing.exVar}\\b`);
30849
+ if (exVarPattern.test(args)) continue;
30850
+ const line = source.slice(0, throwPos).split("\n").length;
30851
+ issues.push({
30852
+ ruleId: "java/lost-stack-trace",
30853
+ category: "logic",
30854
+ severity: "medium",
30855
+ aiSpecific: false,
30856
+ message: `throw new ${m[1]}(${args}) \u2014 original exception \`${enclosing.exVar}\` is not included as cause`,
30857
+ line,
30858
+ column: 1,
30859
+ advice: `The catch block declares exception variable \`${enclosing.exVar}\` but the throw statement doesn't include it as a cause. The original stack trace is lost. Fix: \`throw new ${m[1]}("...", ${enclosing.exVar})\` \u2014 the second argument to the exception constructor is the cause, which Java's Throwable framework preserves in the stack trace chain. Reference: java/lost-stack-trace v0.35.1 (Raidar-inspired content-based detection of AI-polished error handling).`
30860
+ });
30861
+ }
30862
+ return issues;
30863
+ }
30864
+ });
30865
+ }
30866
+ });
30867
+
30800
30868
  // src/rules/java/sql-string-concat.ts
30801
30869
  var SQL_KEYWORD_REGEX, UNSAFE_REGEX, SAFE_REGEX, javaSqlStringConcatRule;
30802
30870
  var init_sql_string_concat = __esm({
@@ -30850,6 +30918,68 @@ var init_sql_string_concat = __esm({
30850
30918
  }
30851
30919
  });
30852
30920
 
30921
+ // src/rules/java/suspicious-implementation.ts
30922
+ var STRONG_VERB_REGEX, METHOD_DECL_REGEX, EMPTY_BODY_REGEX, RETURN_CONSTANT_REGEX, RETURN_INPUT_REGEX, JUST_THROW_REGEX, javaSuspiciousImplementationRule;
30923
+ var init_suspicious_implementation = __esm({
30924
+ "src/rules/java/suspicious-implementation.ts"() {
30925
+ "use strict";
30926
+ init_rule();
30927
+ STRONG_VERB_REGEX = /(?:validate|validates|validated|verifies?|verify|check|checks?|checked|ensures?|ensure|ensured|verifies|verified|sanitize|sanitizes|sanitized|escape|escapes|escaped|authenticate|authenticates|authenticated|authorize|authorizes|authorized|audit|audits|audited|inspect|inspects|inspected|filter|filters?|filtered|normalize|normalizes|normalized|encrypt|encrypts|encrypted|decrypt|decrypts|decrypted|hash|hashes|hashed|sign|signs?|signed|compress|compresses|compressed|decompress|decompresses|decompressed|parse|parses|parsed|format|formats?|formatted)(?=[A-Z]|[^a-zA-Z]|$)/i;
30928
+ METHOD_DECL_REGEX = /(?:public|private|protected)\s+(?:static\s+)?[\w<>,\s\[\]]+?\s+(\w+)\s*\([^)]*\)\s*\{([^{}]*)\}/g;
30929
+ EMPTY_BODY_REGEX = /^\s*$/;
30930
+ RETURN_CONSTANT_REGEX = /^\s*return\s+(?:null|true|false|0|1|0L|0\.0|"")\s*;\s*$/;
30931
+ RETURN_INPUT_REGEX = /^\s*return\s+(\w+)\s*;\s*$/;
30932
+ JUST_THROW_REGEX = /^\s*throw\s+new\s+(?:UnsupportedOperationException|UnsupportedOperationException\([^)]*\)|RuntimeException\([^)]*\)|IllegalStateException\([^)]*\))\s*;\s*$/;
30933
+ javaSuspiciousImplementationRule = createRule({
30934
+ id: "java/suspicious-implementation",
30935
+ category: "security",
30936
+ severity: "high",
30937
+ aiSpecific: false,
30938
+ description: "function name suggests validation/encryption/auth but body is empty or trivially wrong \u2014 content mismatch",
30939
+ create(_context) {
30940
+ return {};
30941
+ },
30942
+ analyze(_context, facts) {
30943
+ const issues = [];
30944
+ const source = facts.v2?._source;
30945
+ if (!source) return issues;
30946
+ if (!/\.java$/i.test(facts.filePath)) return issues;
30947
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
30948
+ let m;
30949
+ METHOD_DECL_REGEX.lastIndex = 0;
30950
+ while ((m = METHOD_DECL_REGEX.exec(source)) !== null) {
30951
+ const methodName = m[1];
30952
+ const body = m[2];
30953
+ if (!STRONG_VERB_REGEX.test(methodName)) continue;
30954
+ let reason = null;
30955
+ if (EMPTY_BODY_REGEX.test(body)) {
30956
+ reason = "empty body";
30957
+ } else if (RETURN_CONSTANT_REGEX.test(body)) {
30958
+ reason = "returns a constant (null/true/false/0/1)";
30959
+ } else if (RETURN_INPUT_REGEX.test(body)) {
30960
+ reason = "returns the input unchanged (pass-through stub)";
30961
+ } else if (JUST_THROW_REGEX.test(body)) {
30962
+ reason = "throws UnsupportedOperationException \u2014 not implemented";
30963
+ }
30964
+ if (!reason) continue;
30965
+ const line = source.slice(0, m.index).split("\n").length;
30966
+ issues.push({
30967
+ ruleId: "java/suspicious-implementation",
30968
+ category: "security",
30969
+ severity: "high",
30970
+ aiSpecific: false,
30971
+ message: `function ${methodName} (${reason}) \u2014 content-based mismatch`,
30972
+ line,
30973
+ column: 1,
30974
+ advice: `The function name "${methodName}" suggests a real validation/encryption/auth/filter operation, but the body is ${reason}. This is a content mismatch \u2014 the code's claimed behavior doesn't match its actual behavior. Real production code that ships this is a silent security failure (OWASP A04:2021 \u2014 Insecure Design). The fix is to either implement the operation or rename the function to reflect what it actually does. Reference: java/suspicious-implementation v0.35.0 (CoCoNUTS-inspired content-based detection).`
30975
+ });
30976
+ }
30977
+ return issues;
30978
+ }
30979
+ });
30980
+ }
30981
+ });
30982
+
30853
30983
  // src/rules/java/system-out-println.ts
30854
30984
  var SYSTEM_OUT_REGEX, REAL_LOGGING_IMPORT_REGEX, javaSystemOutPrintlnRule;
30855
30985
  var init_system_out_println = __esm({
@@ -46963,7 +47093,9 @@ var init_builtins = __esm({
46963
47093
  init_struct_tag_inconsistency();
46964
47094
  init_command_injection();
46965
47095
  init_hardcoded_credential();
47096
+ init_lost_stack_trace();
46966
47097
  init_sql_string_concat();
47098
+ init_suspicious_implementation();
46967
47099
  init_system_out_println();
46968
47100
  init_thread_sleep_in_loop();
46969
47101
  init_coroutine_global_scope();
@@ -47102,7 +47234,9 @@ var init_builtins = __esm({
47102
47234
  goStructTagInconsistencyRule,
47103
47235
  javaCommandInjectionRule,
47104
47236
  javaHardcodedCredentialRule,
47237
+ javaLostStackTraceRule,
47105
47238
  javaSqlStringConcatRule,
47239
+ javaSuspiciousImplementationRule,
47106
47240
  javaSystemOutPrintlnRule,
47107
47241
  javaThreadSleepInLoopRule,
47108
47242
  kotlinCoroutineGlobalScopeRule,
@@ -51797,6 +51931,38 @@ var init_signal_strength = __esm({
51797
51931
  _v9Precision: 0,
51798
51932
  defaultOff: true
51799
51933
  },
51934
+ "java/suspicious-implementation": {
51935
+ recall: 0,
51936
+ fpRate: 0,
51937
+ ratio: 0,
51938
+ precision: 0,
51939
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
51940
+ verdict: "OK",
51941
+ _calibrationNote: "v0.35.0: NEW RULE \u2014 content-based detection (CoCoNUTS-inspired). Detects function names that claim a strong operation (validate, encrypt, hash, sanitize, check, verify, authenticate, etc.) but whose body is trivially empty, returns a constant, or returns the input unchanged. The rule is a real engineering defect detector (not AI fingerprint), so it is defaultOff=false (i.e. ON by default) and verdict=OK. The CoCoNUTS paper (2025) showed that style-based AI-detectors fail under paraphrasing; this rule looks at semantic content (function body behavior) rather than surface features. v9 calibration is pending \u2014 the rule was added at the end of the v0.34.X refinement series and has not been measured against the v9 corpus yet. The expected impact: low recall (most production code doesn't have these stubs) but high precision (when it fires, it's a real bug). The full v9 calibration is the next step for v0.35.0.x patches.",
51942
+ aiSpecific: false,
51943
+ _v9Verdict: "OK",
51944
+ _v9Lift: 0,
51945
+ _v9Recall: 0,
51946
+ _v9FpRate: 0,
51947
+ _v9Precision: 0,
51948
+ defaultOff: false
51949
+ },
51950
+ "java/lost-stack-trace": {
51951
+ recall: 0,
51952
+ fpRate: 0,
51953
+ ratio: 0,
51954
+ precision: 0,
51955
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
51956
+ verdict: "OK",
51957
+ _calibrationNote: "v0.35.1: NEW RULE \u2014 Raidar-inspired content-based detection. The Raidar paper (ICLR 2024) showed that LLMs tend to 'polish' error handling by wrapping exceptions but losing the original cause; the inverse observation is that AI-generated code often has this pattern. This rule detects catch blocks that throw a new exception WITHOUT including the original exception as a cause \u2014 the original stack trace is lost. The rule is a real engineering defect detector (not AI fingerprint), so it is defaultOff=false (ON by default) and verdict=OK. v9 calibration is pending. The expected impact: low recall (most production code preserves the original exception) but very high precision (when it fires, it's a real bug \u2014 debugging without the stack trace is impossible).",
51958
+ aiSpecific: false,
51959
+ _v9Verdict: "OK",
51960
+ _v9Lift: 0,
51961
+ _v9Recall: 0,
51962
+ _v9FpRate: 0,
51963
+ _v9Precision: 0,
51964
+ defaultOff: false
51965
+ },
51800
51966
  "swift/force-unwrap": {
51801
51967
  recall: 0.10211,
51802
51968
  fpRate: 0.21462,
@@ -60063,6 +60229,10 @@ var RULE_HINTS = {
60063
60229
  "java/thread-sleep-in-loop": "Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the polling anti-pattern \u2014 ties up Tomcat/Jetty/Netty threads. (v0.30.0 \u2014 DORMANT, era-confounded ratio 0.97.)",
60064
60230
  "java/system-out-println": "The file imports a real logger (SLF4J/Log4j2/java.util.logging) but uses System.out.println. Use the declared log object instead. (v0.31.0 \u2014 OK; ratio 1.73, refined from v0.30.)",
60065
60231
  "java/command-injection": "Use ProcessBuilder with a List<String> of args (no shell parsing) and validate each arg. Runtime.exec() with concat is the canonical command-injection pattern. OWASP A03:2021. (v0.30.0 \u2014 DORMANT.)",
60232
+ // v0.35.0 — content-based detection (CoCoNUTS-inspired)
60233
+ "java/suspicious-implementation": "Function name claims validate/encrypt/hash/sanitize but body is empty, returns null/true, or returns the input. This is a content mismatch \u2014 the function's claimed behavior doesn't match its actual behavior. OWASP A04:2021.",
60234
+ // v0.35.1 — Raidar-inspired content-based detection
60235
+ "java/lost-stack-trace": 'catch block throws a new exception without the original cause \u2014 stack trace is lost. Pass the original exception as the second arg to the new exception\'s constructor: `throw new XxxException("msg", e)`.',
60066
60236
  // v0.24.0 — Swift rules (DORMANT until v9 Swift corpus calibration)
60067
60237
  "swift/force-unwrap": "Replace with the safe form: `as?` + guard/if let, `try?`, or `guard let x = optional else { return }`. `!` crashes unconditionally in release. (v0.24.0 \u2014 DORMANT.)",
60068
60238
  "swift/print-debug": "Replace with `Logger(subsystem:..., category:...).info(...)` (os.log). `print` writes to stdout with no level and no redaction. (v0.24.0 \u2014 DORMANT.)",
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ var VERSION;
19
19
  var init_header = __esm({
20
20
  "src/types/_header.ts"() {
21
21
  "use strict";
22
- VERSION = "0.34.10";
22
+ VERSION = "0.35.1";
23
23
  }
24
24
  });
25
25
 
@@ -30778,6 +30778,74 @@ var init_hardcoded_credential = __esm({
30778
30778
  }
30779
30779
  });
30780
30780
 
30781
+ // src/rules/java/lost-stack-trace.ts
30782
+ var THROW_NEW_REGEX, CATCH_REGEX, javaLostStackTraceRule;
30783
+ var init_lost_stack_trace = __esm({
30784
+ "src/rules/java/lost-stack-trace.ts"() {
30785
+ "use strict";
30786
+ init_rule();
30787
+ THROW_NEW_REGEX = /throw\s+new\s+(\w+(?:Exception|Error|Throwable))\s*\(([^)]*)\)/g;
30788
+ CATCH_REGEX = /catch\s*\(\s*(?:final\s+)?[\w<>,\s]+\s+(\w+)\s*\)\s*\{/g;
30789
+ javaLostStackTraceRule = createRule({
30790
+ id: "java/lost-stack-trace",
30791
+ category: "logic",
30792
+ severity: "medium",
30793
+ aiSpecific: false,
30794
+ description: "catch block throws a new exception without the original cause \u2014 stack trace is lost",
30795
+ create(_context) {
30796
+ return {};
30797
+ },
30798
+ analyze(_context, facts) {
30799
+ const issues = [];
30800
+ const source = facts.v2?._source;
30801
+ if (!source) return issues;
30802
+ if (!/\.java$/i.test(facts.filePath)) return issues;
30803
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
30804
+ const catchBlocks = [];
30805
+ let cm;
30806
+ CATCH_REGEX.lastIndex = 0;
30807
+ while ((cm = CATCH_REGEX.exec(source)) !== null) {
30808
+ const catchStart = cm.index;
30809
+ const exVar = cm[1];
30810
+ let depth = 1;
30811
+ let i = cm.index + cm[0].length;
30812
+ while (i < source.length && depth > 0) {
30813
+ const ch = source[i];
30814
+ if (ch === "{") depth++;
30815
+ else if (ch === "}") depth--;
30816
+ i++;
30817
+ }
30818
+ catchBlocks.push({ start: catchStart, end: i, exVar });
30819
+ }
30820
+ let m;
30821
+ THROW_NEW_REGEX.lastIndex = 0;
30822
+ while ((m = THROW_NEW_REGEX.exec(source)) !== null) {
30823
+ const throwPos = m.index;
30824
+ const args = m[2].trim();
30825
+ const enclosing = catchBlocks.find(
30826
+ (cb) => cb.start < throwPos && throwPos < cb.end
30827
+ );
30828
+ if (!enclosing) continue;
30829
+ const exVarPattern = new RegExp(`\\b${enclosing.exVar}\\b`);
30830
+ if (exVarPattern.test(args)) continue;
30831
+ const line = source.slice(0, throwPos).split("\n").length;
30832
+ issues.push({
30833
+ ruleId: "java/lost-stack-trace",
30834
+ category: "logic",
30835
+ severity: "medium",
30836
+ aiSpecific: false,
30837
+ message: `throw new ${m[1]}(${args}) \u2014 original exception \`${enclosing.exVar}\` is not included as cause`,
30838
+ line,
30839
+ column: 1,
30840
+ advice: `The catch block declares exception variable \`${enclosing.exVar}\` but the throw statement doesn't include it as a cause. The original stack trace is lost. Fix: \`throw new ${m[1]}("...", ${enclosing.exVar})\` \u2014 the second argument to the exception constructor is the cause, which Java's Throwable framework preserves in the stack trace chain. Reference: java/lost-stack-trace v0.35.1 (Raidar-inspired content-based detection of AI-polished error handling).`
30841
+ });
30842
+ }
30843
+ return issues;
30844
+ }
30845
+ });
30846
+ }
30847
+ });
30848
+
30781
30849
  // src/rules/java/sql-string-concat.ts
30782
30850
  var SQL_KEYWORD_REGEX, UNSAFE_REGEX, SAFE_REGEX, javaSqlStringConcatRule;
30783
30851
  var init_sql_string_concat = __esm({
@@ -30831,6 +30899,68 @@ var init_sql_string_concat = __esm({
30831
30899
  }
30832
30900
  });
30833
30901
 
30902
+ // src/rules/java/suspicious-implementation.ts
30903
+ var STRONG_VERB_REGEX, METHOD_DECL_REGEX, EMPTY_BODY_REGEX, RETURN_CONSTANT_REGEX, RETURN_INPUT_REGEX, JUST_THROW_REGEX, javaSuspiciousImplementationRule;
30904
+ var init_suspicious_implementation = __esm({
30905
+ "src/rules/java/suspicious-implementation.ts"() {
30906
+ "use strict";
30907
+ init_rule();
30908
+ STRONG_VERB_REGEX = /(?:validate|validates|validated|verifies?|verify|check|checks?|checked|ensures?|ensure|ensured|verifies|verified|sanitize|sanitizes|sanitized|escape|escapes|escaped|authenticate|authenticates|authenticated|authorize|authorizes|authorized|audit|audits|audited|inspect|inspects|inspected|filter|filters?|filtered|normalize|normalizes|normalized|encrypt|encrypts|encrypted|decrypt|decrypts|decrypted|hash|hashes|hashed|sign|signs?|signed|compress|compresses|compressed|decompress|decompresses|decompressed|parse|parses|parsed|format|formats?|formatted)(?=[A-Z]|[^a-zA-Z]|$)/i;
30909
+ METHOD_DECL_REGEX = /(?:public|private|protected)\s+(?:static\s+)?[\w<>,\s\[\]]+?\s+(\w+)\s*\([^)]*\)\s*\{([^{}]*)\}/g;
30910
+ EMPTY_BODY_REGEX = /^\s*$/;
30911
+ RETURN_CONSTANT_REGEX = /^\s*return\s+(?:null|true|false|0|1|0L|0\.0|"")\s*;\s*$/;
30912
+ RETURN_INPUT_REGEX = /^\s*return\s+(\w+)\s*;\s*$/;
30913
+ JUST_THROW_REGEX = /^\s*throw\s+new\s+(?:UnsupportedOperationException|UnsupportedOperationException\([^)]*\)|RuntimeException\([^)]*\)|IllegalStateException\([^)]*\))\s*;\s*$/;
30914
+ javaSuspiciousImplementationRule = createRule({
30915
+ id: "java/suspicious-implementation",
30916
+ category: "security",
30917
+ severity: "high",
30918
+ aiSpecific: false,
30919
+ description: "function name suggests validation/encryption/auth but body is empty or trivially wrong \u2014 content mismatch",
30920
+ create(_context) {
30921
+ return {};
30922
+ },
30923
+ analyze(_context, facts) {
30924
+ const issues = [];
30925
+ const source = facts.v2?._source;
30926
+ if (!source) return issues;
30927
+ if (!/\.java$/i.test(facts.filePath)) return issues;
30928
+ if (/\/test\//i.test(facts.filePath) || /\/src\/test\//i.test(facts.filePath)) return issues;
30929
+ let m;
30930
+ METHOD_DECL_REGEX.lastIndex = 0;
30931
+ while ((m = METHOD_DECL_REGEX.exec(source)) !== null) {
30932
+ const methodName = m[1];
30933
+ const body = m[2];
30934
+ if (!STRONG_VERB_REGEX.test(methodName)) continue;
30935
+ let reason = null;
30936
+ if (EMPTY_BODY_REGEX.test(body)) {
30937
+ reason = "empty body";
30938
+ } else if (RETURN_CONSTANT_REGEX.test(body)) {
30939
+ reason = "returns a constant (null/true/false/0/1)";
30940
+ } else if (RETURN_INPUT_REGEX.test(body)) {
30941
+ reason = "returns the input unchanged (pass-through stub)";
30942
+ } else if (JUST_THROW_REGEX.test(body)) {
30943
+ reason = "throws UnsupportedOperationException \u2014 not implemented";
30944
+ }
30945
+ if (!reason) continue;
30946
+ const line = source.slice(0, m.index).split("\n").length;
30947
+ issues.push({
30948
+ ruleId: "java/suspicious-implementation",
30949
+ category: "security",
30950
+ severity: "high",
30951
+ aiSpecific: false,
30952
+ message: `function ${methodName} (${reason}) \u2014 content-based mismatch`,
30953
+ line,
30954
+ column: 1,
30955
+ advice: `The function name "${methodName}" suggests a real validation/encryption/auth/filter operation, but the body is ${reason}. This is a content mismatch \u2014 the code's claimed behavior doesn't match its actual behavior. Real production code that ships this is a silent security failure (OWASP A04:2021 \u2014 Insecure Design). The fix is to either implement the operation or rename the function to reflect what it actually does. Reference: java/suspicious-implementation v0.35.0 (CoCoNUTS-inspired content-based detection).`
30956
+ });
30957
+ }
30958
+ return issues;
30959
+ }
30960
+ });
30961
+ }
30962
+ });
30963
+
30834
30964
  // src/rules/java/system-out-println.ts
30835
30965
  var SYSTEM_OUT_REGEX, REAL_LOGGING_IMPORT_REGEX, javaSystemOutPrintlnRule;
30836
30966
  var init_system_out_println = __esm({
@@ -46944,7 +47074,9 @@ var init_builtins = __esm({
46944
47074
  init_struct_tag_inconsistency();
46945
47075
  init_command_injection();
46946
47076
  init_hardcoded_credential();
47077
+ init_lost_stack_trace();
46947
47078
  init_sql_string_concat();
47079
+ init_suspicious_implementation();
46948
47080
  init_system_out_println();
46949
47081
  init_thread_sleep_in_loop();
46950
47082
  init_coroutine_global_scope();
@@ -47083,7 +47215,9 @@ var init_builtins = __esm({
47083
47215
  goStructTagInconsistencyRule,
47084
47216
  javaCommandInjectionRule,
47085
47217
  javaHardcodedCredentialRule,
47218
+ javaLostStackTraceRule,
47086
47219
  javaSqlStringConcatRule,
47220
+ javaSuspiciousImplementationRule,
47087
47221
  javaSystemOutPrintlnRule,
47088
47222
  javaThreadSleepInLoopRule,
47089
47223
  kotlinCoroutineGlobalScopeRule,
@@ -51775,6 +51909,38 @@ var init_signal_strength = __esm({
51775
51909
  _v9Precision: 0,
51776
51910
  defaultOff: true
51777
51911
  },
51912
+ "java/suspicious-implementation": {
51913
+ recall: 0,
51914
+ fpRate: 0,
51915
+ ratio: 0,
51916
+ precision: 0,
51917
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
51918
+ verdict: "OK",
51919
+ _calibrationNote: "v0.35.0: NEW RULE \u2014 content-based detection (CoCoNUTS-inspired). Detects function names that claim a strong operation (validate, encrypt, hash, sanitize, check, verify, authenticate, etc.) but whose body is trivially empty, returns a constant, or returns the input unchanged. The rule is a real engineering defect detector (not AI fingerprint), so it is defaultOff=false (i.e. ON by default) and verdict=OK. The CoCoNUTS paper (2025) showed that style-based AI-detectors fail under paraphrasing; this rule looks at semantic content (function body behavior) rather than surface features. v9 calibration is pending \u2014 the rule was added at the end of the v0.34.X refinement series and has not been measured against the v9 corpus yet. The expected impact: low recall (most production code doesn't have these stubs) but high precision (when it fires, it's a real bug). The full v9 calibration is the next step for v0.35.0.x patches.",
51920
+ aiSpecific: false,
51921
+ _v9Verdict: "OK",
51922
+ _v9Lift: 0,
51923
+ _v9Recall: 0,
51924
+ _v9FpRate: 0,
51925
+ _v9Precision: 0,
51926
+ defaultOff: false
51927
+ },
51928
+ "java/lost-stack-trace": {
51929
+ recall: 0,
51930
+ fpRate: 0,
51931
+ ratio: 0,
51932
+ precision: 0,
51933
+ lastCalibratedAt: "2026-07-03T00:00:00Z",
51934
+ verdict: "OK",
51935
+ _calibrationNote: "v0.35.1: NEW RULE \u2014 Raidar-inspired content-based detection. The Raidar paper (ICLR 2024) showed that LLMs tend to 'polish' error handling by wrapping exceptions but losing the original cause; the inverse observation is that AI-generated code often has this pattern. This rule detects catch blocks that throw a new exception WITHOUT including the original exception as a cause \u2014 the original stack trace is lost. The rule is a real engineering defect detector (not AI fingerprint), so it is defaultOff=false (ON by default) and verdict=OK. v9 calibration is pending. The expected impact: low recall (most production code preserves the original exception) but very high precision (when it fires, it's a real bug \u2014 debugging without the stack trace is impossible).",
51936
+ aiSpecific: false,
51937
+ _v9Verdict: "OK",
51938
+ _v9Lift: 0,
51939
+ _v9Recall: 0,
51940
+ _v9FpRate: 0,
51941
+ _v9Precision: 0,
51942
+ defaultOff: false
51943
+ },
51778
51944
  "swift/force-unwrap": {
51779
51945
  recall: 0.10211,
51780
51946
  fpRate: 0.21462,
@@ -59962,6 +60128,10 @@ var RULE_HINTS = {
59962
60128
  "java/thread-sleep-in-loop": "Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the polling anti-pattern \u2014 ties up Tomcat/Jetty/Netty threads. (v0.30.0 \u2014 DORMANT, era-confounded ratio 0.97.)",
59963
60129
  "java/system-out-println": "The file imports a real logger (SLF4J/Log4j2/java.util.logging) but uses System.out.println. Use the declared log object instead. (v0.31.0 \u2014 OK; ratio 1.73, refined from v0.30.)",
59964
60130
  "java/command-injection": "Use ProcessBuilder with a List<String> of args (no shell parsing) and validate each arg. Runtime.exec() with concat is the canonical command-injection pattern. OWASP A03:2021. (v0.30.0 \u2014 DORMANT.)",
60131
+ // v0.35.0 — content-based detection (CoCoNUTS-inspired)
60132
+ "java/suspicious-implementation": "Function name claims validate/encrypt/hash/sanitize but body is empty, returns null/true, or returns the input. This is a content mismatch \u2014 the function's claimed behavior doesn't match its actual behavior. OWASP A04:2021.",
60133
+ // v0.35.1 — Raidar-inspired content-based detection
60134
+ "java/lost-stack-trace": 'catch block throws a new exception without the original cause \u2014 stack trace is lost. Pass the original exception as the second arg to the new exception\'s constructor: `throw new XxxException("msg", e)`.',
59965
60135
  // v0.24.0 — Swift rules (DORMANT until v9 Swift corpus calibration)
59966
60136
  "swift/force-unwrap": "Replace with the safe form: `as?` + guard/if let, `try?`, or `guard let x = optional else { return }`. `!` crashes unconditionally in release. (v0.24.0 \u2014 DORMANT.)",
59967
60137
  "swift/print-debug": "Replace with `Logger(subsystem:..., category:...).info(...)` (os.log). `print` writes to stdout with no level and no redaction. (v0.24.0 \u2014 DORMANT.)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopbrick",
3
- "version": "0.34.10",
3
+ "version": "0.35.1",
4
4
  "description": "Discovered, modeled, and governed repository structure. SlopBrick scans source code, classifies it against 95+ rules in 15 categories, computes 4 scores (aiSlopScore: lower=cleaner, engineeringHygiene, security, repositoryHealth composite), and persists the structure for AI agents and CI.",
5
5
  "type": "module",
6
6
  "bin": {