codeslick-cli 1.2.0 → 1.2.2
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/README.md +18 -19
- package/dist/packages/cli/src/reporters/cli-reporter.js +7 -7
- package/dist/packages/cli/src/reporters/cli-reporter.js.map +1 -1
- package/dist/src/lib/analyzers/go/security-checks/ai-generated-code.d.ts +5 -2
- package/dist/src/lib/analyzers/go/security-checks/ai-generated-code.d.ts.map +1 -1
- package/dist/src/lib/analyzers/go/security-checks/ai-generated-code.js +61 -5
- package/dist/src/lib/analyzers/go/security-checks/ai-generated-code.js.map +1 -1
- package/dist/src/lib/analyzers/go/security-checks/credentials-crypto.d.ts +6 -4
- package/dist/src/lib/analyzers/go/security-checks/credentials-crypto.d.ts.map +1 -1
- package/dist/src/lib/analyzers/go/security-checks/credentials-crypto.js +97 -4
- package/dist/src/lib/analyzers/go/security-checks/credentials-crypto.js.map +1 -1
- package/dist/src/lib/analyzers/go/security-checks/enhanced-supply-chain.d.ts +21 -0
- package/dist/src/lib/analyzers/go/security-checks/enhanced-supply-chain.d.ts.map +1 -0
- package/dist/src/lib/analyzers/go/security-checks/enhanced-supply-chain.js +114 -0
- package/dist/src/lib/analyzers/go/security-checks/enhanced-supply-chain.js.map +1 -0
- package/dist/src/lib/analyzers/go/security-checks/injection-attacks.d.ts +1 -0
- package/dist/src/lib/analyzers/go/security-checks/injection-attacks.d.ts.map +1 -1
- package/dist/src/lib/analyzers/go/security-checks/injection-attacks.js +48 -0
- package/dist/src/lib/analyzers/go/security-checks/injection-attacks.js.map +1 -1
- package/dist/src/lib/analyzers/go-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/go-analyzer.js +3 -0
- package/dist/src/lib/analyzers/go-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/helpers/ai-code-detection-utils.d.ts +226 -2
- package/dist/src/lib/analyzers/helpers/ai-code-detection-utils.d.ts.map +1 -1
- package/dist/src/lib/analyzers/helpers/ai-code-detection-utils.js +1108 -23
- package/dist/src/lib/analyzers/helpers/ai-code-detection-utils.js.map +1 -1
- package/dist/src/lib/analyzers/helpers/variable-tracker.d.ts.map +1 -1
- package/dist/src/lib/analyzers/helpers/variable-tracker.js +6 -4
- package/dist/src/lib/analyzers/helpers/variable-tracker.js.map +1 -1
- package/dist/src/lib/analyzers/java/security-checks/ai-generated-code.d.ts +2 -0
- package/dist/src/lib/analyzers/java/security-checks/ai-generated-code.d.ts.map +1 -1
- package/dist/src/lib/analyzers/java/security-checks/ai-generated-code.js +76 -12
- package/dist/src/lib/analyzers/java/security-checks/ai-generated-code.js.map +1 -1
- package/dist/src/lib/analyzers/java/security-checks/enhanced-supply-chain.d.ts +2 -0
- package/dist/src/lib/analyzers/java/security-checks/enhanced-supply-chain.d.ts.map +1 -1
- package/dist/src/lib/analyzers/java/security-checks/enhanced-supply-chain.js +99 -6
- package/dist/src/lib/analyzers/java/security-checks/enhanced-supply-chain.js.map +1 -1
- package/dist/src/lib/analyzers/java/security-checks/injection-attacks.d.ts +1 -0
- package/dist/src/lib/analyzers/java/security-checks/injection-attacks.d.ts.map +1 -1
- package/dist/src/lib/analyzers/java/security-checks/injection-attacks.js +41 -3
- package/dist/src/lib/analyzers/java/security-checks/injection-attacks.js.map +1 -1
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.d.ts +3 -2
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.js +82 -11
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.js.map +1 -1
- package/dist/src/lib/analyzers/javascript/security-checks/enhanced-supply-chain.d.ts +3 -0
- package/dist/src/lib/analyzers/javascript/security-checks/enhanced-supply-chain.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript/security-checks/enhanced-supply-chain.js +75 -0
- package/dist/src/lib/analyzers/javascript/security-checks/enhanced-supply-chain.js.map +1 -1
- package/dist/src/lib/analyzers/javascript-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/javascript-analyzer.js +9 -2
- package/dist/src/lib/analyzers/javascript-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/ai-generated-code.d.ts +3 -2
- package/dist/src/lib/analyzers/python/security-checks/ai-generated-code.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/ai-generated-code.js +113 -10
- package/dist/src/lib/analyzers/python/security-checks/ai-generated-code.js.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.d.ts +2 -0
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.js +48 -0
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.js.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/enhanced-supply-chain.d.ts +3 -0
- package/dist/src/lib/analyzers/python/security-checks/enhanced-supply-chain.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/enhanced-supply-chain.js +84 -0
- package/dist/src/lib/analyzers/python/security-checks/enhanced-supply-chain.js.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/injection-attacks.d.ts +4 -2
- package/dist/src/lib/analyzers/python/security-checks/injection-attacks.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python/security-checks/injection-attacks.js +43 -3
- package/dist/src/lib/analyzers/python/security-checks/injection-attacks.js.map +1 -1
- package/dist/src/lib/analyzers/python-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/python-analyzer.js +19 -3
- package/dist/src/lib/analyzers/python-analyzer.js.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/aws.js +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/aws.js.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/communication.js +2 -2
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/communication.js.map +1 -1
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/github.js +3 -3
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/github.js.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.js +8 -1
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.js.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/enhanced-supply-chain.d.ts +2 -0
- package/dist/src/lib/analyzers/typescript/security-checks/enhanced-supply-chain.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/enhanced-supply-chain.js +49 -0
- package/dist/src/lib/analyzers/typescript/security-checks/enhanced-supply-chain.js.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/injection-attacks.d.ts +13 -11
- package/dist/src/lib/analyzers/typescript/security-checks/injection-attacks.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/injection-attacks.js +79 -22
- package/dist/src/lib/analyzers/typescript/security-checks/injection-attacks.js.map +1 -1
- package/dist/src/lib/analyzers/typescript/security-checks/type-safety.d.ts +24 -0
- package/dist/src/lib/analyzers/typescript/security-checks/type-safety.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/type-safety.js +181 -0
- package/dist/src/lib/analyzers/typescript/security-checks/type-safety.js.map +1 -0
- package/dist/src/lib/analyzers/typescript-analyzer.d.ts.map +1 -1
- package/dist/src/lib/analyzers/typescript-analyzer.js +3 -0
- package/dist/src/lib/analyzers/typescript-analyzer.js.map +1 -1
- package/dist/src/lib/security/compliance-mapping.d.ts.map +1 -1
- package/dist/src/lib/security/compliance-mapping.js +19 -0
- package/dist/src/lib/security/compliance-mapping.js.map +1 -1
- package/dist/src/lib/security/severity-scoring.d.ts.map +1 -1
- package/dist/src/lib/security/severity-scoring.js +7 -0
- package/dist/src/lib/security/severity-scoring.js.map +1 -1
- package/package.json +1 -1
- package/src/reporters/cli-reporter.ts +7 -7
|
@@ -21,21 +21,87 @@ exports.detectRedundantNullChecks = detectRedundantNullChecks;
|
|
|
21
21
|
exports.detectUnnecessaryAsync = detectUnnecessaryAsync;
|
|
22
22
|
exports.detectGenericVariableOveruse = detectGenericVariableOveruse;
|
|
23
23
|
exports.detectInconsistentStringConcatenation = detectInconsistentStringConcatenation;
|
|
24
|
+
exports.detectVerboseDocstrings = detectVerboseDocstrings;
|
|
25
|
+
exports.detectDefensiveNullChecks = detectDefensiveNullChecks;
|
|
26
|
+
exports.detectExcessiveTryCatch = detectExcessiveTryCatch;
|
|
27
|
+
exports.detectHelperFunctionProliferation = detectHelperFunctionProliferation;
|
|
28
|
+
exports.detectOverlyDescriptiveNames = detectOverlyDescriptiveNames;
|
|
29
|
+
exports.detectPrematureOptimizationComments = detectPrematureOptimizationComments;
|
|
30
|
+
exports.detectDetailedTodoMarkers = detectDetailedTodoMarkers;
|
|
31
|
+
exports.detectTypeAnnotationOveruse = detectTypeAnnotationOveruse;
|
|
32
|
+
exports.detectBoilerplateComments = detectBoilerplateComments;
|
|
33
|
+
exports.detectPlaceholderTodos = detectPlaceholderTodos;
|
|
34
|
+
exports.detectGenericFunctionNames = detectGenericFunctionNames;
|
|
35
|
+
exports.detectPlaceholderConstants = detectPlaceholderConstants;
|
|
36
|
+
exports.detectUnusualImportOrdering = detectUnusualImportOrdering;
|
|
37
|
+
exports.detectCopilotMarkers = detectCopilotMarkers;
|
|
38
|
+
exports.detectExcessiveTypeAssertions = detectExcessiveTypeAssertions;
|
|
39
|
+
exports.detectDetailedExplanatoryComments = detectDetailedExplanatoryComments;
|
|
40
|
+
exports.detectCustomErrorClasses = detectCustomErrorClasses;
|
|
41
|
+
exports.detectExtensiveInputValidation = detectExtensiveInputValidation;
|
|
42
|
+
exports.detectDescriptiveHelperFunctions = detectDescriptiveHelperFunctions;
|
|
43
|
+
exports.detectStructuredReturnObjects = detectStructuredReturnObjects;
|
|
44
|
+
exports.detectAICommandMarkers = detectAICommandMarkers;
|
|
45
|
+
exports.detectDiffStyleComments = detectDiffStyleComments;
|
|
46
|
+
exports.detectTabCompletionArtifacts = detectTabCompletionArtifacts;
|
|
47
|
+
exports.detectContextWindowLeakage = detectContextWindowLeakage;
|
|
48
|
+
exports.detectOverGenericExports = detectOverGenericExports;
|
|
49
|
+
exports.detectUnusedImportCleanup = detectUnusedImportCleanup;
|
|
50
|
+
exports.detectPlaceholderErrorMessages = detectPlaceholderErrorMessages;
|
|
51
|
+
exports.detectInlineDocumentationOverload = detectInlineDocumentationOverload;
|
|
52
|
+
exports.detectZeroEdgeCases = detectZeroEdgeCases;
|
|
53
|
+
exports.detectUniformIndentation = detectUniformIndentation;
|
|
54
|
+
exports.detectTextbookVariableNames = detectTextbookVariableNames;
|
|
55
|
+
exports.detectNoCommentsWithPerfectStructure = detectNoCommentsWithPerfectStructure;
|
|
56
|
+
exports.detectExcessiveParameterValidation = detectExcessiveParameterValidation;
|
|
24
57
|
exports.calculateAICodeConfidence = calculateAICodeConfidence;
|
|
25
58
|
exports.isTestFile = isTestFile;
|
|
26
59
|
exports.removeCommentsAndStrings = removeCommentsAndStrings;
|
|
27
60
|
/**
|
|
28
|
-
* Heuristic weights (sum = 1.0)
|
|
61
|
+
* Heuristic weights (sum = 1.0) - Now 13 heuristics
|
|
29
62
|
*/
|
|
30
63
|
const HEURISTIC_WEIGHTS = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
// Original 8 heuristics (slightly reduced weights)
|
|
65
|
+
overEngineeredErrors: 0.10,
|
|
66
|
+
unnecessaryWrappers: 0.08,
|
|
67
|
+
verboseComments: 0.07,
|
|
68
|
+
mixedNaming: 0.09,
|
|
69
|
+
redundantNullChecks: 0.10,
|
|
70
|
+
unnecessaryAsync: 0.08,
|
|
71
|
+
genericVariables: 0.07,
|
|
72
|
+
inconsistentStrings: 0.09,
|
|
73
|
+
// "Perfect code" heuristics (5 new patterns - 32% total)
|
|
74
|
+
zeroEdgeCases: 0.08,
|
|
75
|
+
uniformIndentation: 0.07,
|
|
76
|
+
textbookVariableNames: 0.07,
|
|
77
|
+
noCommentsWithPerfectStructure: 0.05,
|
|
78
|
+
excessiveParameterValidation: 0.05,
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* LLM fingerprint weights for GPT-4 (sum = 1.0)
|
|
82
|
+
*/
|
|
83
|
+
const GPT4_FINGERPRINT_WEIGHTS = {
|
|
84
|
+
verboseDocstrings: 0.15,
|
|
85
|
+
defensiveNullChecks: 0.12,
|
|
86
|
+
excessiveTryCatch: 0.15,
|
|
87
|
+
helperFunctionProliferation: 0.13,
|
|
88
|
+
overlyDescriptiveNames: 0.10,
|
|
89
|
+
prematureOptimizationComments: 0.10,
|
|
90
|
+
detailedTodoMarkers: 0.10,
|
|
91
|
+
typeAnnotationOveruse: 0.15,
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* LLM fingerprint weights for Cursor (sum = 1.0)
|
|
95
|
+
*/
|
|
96
|
+
const CURSOR_FINGERPRINT_WEIGHTS = {
|
|
97
|
+
aiCommandMarkers: 0.15,
|
|
98
|
+
diffStyleComments: 0.12,
|
|
99
|
+
tabCompletionArtifacts: 0.15,
|
|
100
|
+
contextWindowLeakage: 0.10,
|
|
101
|
+
overGenericExports: 0.10,
|
|
102
|
+
unusedImportCleanup: 0.13,
|
|
103
|
+
placeholderErrorMessages: 0.13,
|
|
104
|
+
inlineDocumentationOverload: 0.12,
|
|
39
105
|
};
|
|
40
106
|
/**
|
|
41
107
|
* 1. Detect over-engineered error handling
|
|
@@ -290,41 +356,1060 @@ function detectInconsistentStringConcatenation(lines) {
|
|
|
290
356
|
return totalFunctions > 0 ? Math.min(inconsistentFunctions / totalFunctions, 1.0) : 0;
|
|
291
357
|
}
|
|
292
358
|
/**
|
|
293
|
-
*
|
|
359
|
+
* ====================
|
|
360
|
+
* LLM FINGERPRINT DETECTORS (Week 2)
|
|
361
|
+
* ====================
|
|
362
|
+
*/
|
|
363
|
+
/**
|
|
364
|
+
* GPT-4 Fingerprint #1: Verbose docstrings
|
|
365
|
+
* Pattern: JSDoc/docstring with >5 lines and @param/@returns for trivial functions
|
|
366
|
+
*/
|
|
367
|
+
function detectVerboseDocstrings(lines) {
|
|
368
|
+
let verboseDocCount = 0;
|
|
369
|
+
let totalDocs = 0;
|
|
370
|
+
let inDocstring = false;
|
|
371
|
+
let docstringLines = 0;
|
|
372
|
+
let docstringStart = -1;
|
|
373
|
+
let docstringType = ''; // '/**' or '"""'
|
|
374
|
+
for (let i = 0; i < lines.length; i++) {
|
|
375
|
+
const trimmed = lines[i].trim();
|
|
376
|
+
// If already in docstring, check for end first
|
|
377
|
+
if (inDocstring) {
|
|
378
|
+
docstringLines++;
|
|
379
|
+
// Detect docstring end
|
|
380
|
+
const isJSDocEnd = docstringType === '/**' && trimmed.endsWith('*/');
|
|
381
|
+
const isPythonDocEnd = docstringType === '"""' && trimmed.endsWith('"""') && i > docstringStart;
|
|
382
|
+
if (isJSDocEnd || isPythonDocEnd) {
|
|
383
|
+
inDocstring = false;
|
|
384
|
+
// Check if next line is a trivial function
|
|
385
|
+
let nextFunctionLine = i + 1;
|
|
386
|
+
while (nextFunctionLine < lines.length && lines[nextFunctionLine].trim() === '') {
|
|
387
|
+
nextFunctionLine++;
|
|
388
|
+
}
|
|
389
|
+
if (nextFunctionLine < lines.length) {
|
|
390
|
+
const functionDecl = lines[nextFunctionLine].trim();
|
|
391
|
+
// Check if trivial function (one-liner or simple return on next line)
|
|
392
|
+
const isJSOneLiner = functionDecl.match(/^function\s+\w+\s*\([^)]*\)\s*{\s*return/);
|
|
393
|
+
const isPythonOneLiner = functionDecl.match(/^def\s+\w+\([^)]*\):\s*return/);
|
|
394
|
+
// Check if it's a function declaration followed by a simple return statement
|
|
395
|
+
let isSimpleReturn = false;
|
|
396
|
+
if (functionDecl.match(/^function\s+\w+\s*\([^)]*\)\s*{/) && nextFunctionLine + 1 < lines.length) {
|
|
397
|
+
const nextLine = lines[nextFunctionLine + 1].trim();
|
|
398
|
+
isSimpleReturn = nextLine.startsWith('return ');
|
|
399
|
+
}
|
|
400
|
+
const isTrivialFunction = isJSOneLiner || isPythonOneLiner || isSimpleReturn;
|
|
401
|
+
// Verbose = >5 lines of docs for trivial function
|
|
402
|
+
if (docstringLines > 5 && isTrivialFunction) {
|
|
403
|
+
verboseDocCount++;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
// Not in docstring, check for start (/** or """)
|
|
410
|
+
if (trimmed.startsWith('/**')) {
|
|
411
|
+
inDocstring = true;
|
|
412
|
+
docstringStart = i;
|
|
413
|
+
docstringLines = 0;
|
|
414
|
+
docstringType = '/**';
|
|
415
|
+
totalDocs++;
|
|
416
|
+
}
|
|
417
|
+
else if (trimmed.startsWith('"""') || trimmed === '"""') {
|
|
418
|
+
inDocstring = true;
|
|
419
|
+
docstringStart = i;
|
|
420
|
+
docstringLines = 0;
|
|
421
|
+
docstringType = '"""';
|
|
422
|
+
totalDocs++;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return totalDocs > 0 ? Math.min(verboseDocCount / totalDocs, 1.0) : 0;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* GPT-4 Fingerprint #2: Defensive null checks
|
|
430
|
+
* Pattern: 3+ consecutive null checks on different variables
|
|
431
|
+
*/
|
|
432
|
+
function detectDefensiveNullChecks(lines) {
|
|
433
|
+
let defensiveBlocks = 0;
|
|
434
|
+
for (let i = 0; i < lines.length - 2; i++) {
|
|
435
|
+
const window = lines.slice(i, i + 3).join('\n');
|
|
436
|
+
// Count null checks (JavaScript/TypeScript/Java and Python)
|
|
437
|
+
const jsNullChecks = (window.match(/if\s*\([^)]*\s*(===|!==|==|!=)\s*(null|undefined)\s*\)/g) || []).length;
|
|
438
|
+
// Count typeof checks (typeof x === 'string', typeof x !== 'number', etc.)
|
|
439
|
+
const typeofChecks = (window.match(/if\s*\([^)]*typeof\s+[\w.]+\s*(===|!==|==|!=)\s*['"][a-z]+['"]\s*\)/g) || []).length;
|
|
440
|
+
// Python: if user is None, if user.name is None, etc.
|
|
441
|
+
const pythonNullChecks = (window.match(/if\s+[\w.]+\s+is\s+(None|null)/gi) || []).length;
|
|
442
|
+
const totalChecks = jsNullChecks + typeofChecks + pythonNullChecks;
|
|
443
|
+
if (totalChecks >= 3) {
|
|
444
|
+
defensiveBlocks++;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return Math.min(defensiveBlocks / 5, 1.0); // Normalize (5 = very defensive)
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* GPT-4 Fingerprint #3: Excessive try-catch
|
|
451
|
+
* Pattern: >50% of functions wrapped in try-catch, even for simple operations
|
|
452
|
+
*/
|
|
453
|
+
function detectExcessiveTryCatch(lines) {
|
|
454
|
+
let functionsWithTryCatch = 0;
|
|
455
|
+
let totalFunctions = 0;
|
|
456
|
+
let inFunction = false;
|
|
457
|
+
let functionStart = -1;
|
|
458
|
+
for (let i = 0; i < lines.length; i++) {
|
|
459
|
+
const line = lines[i].trim();
|
|
460
|
+
// Detect function start
|
|
461
|
+
const isFunctionStart = line.match(/^function\s+\w+\s*\(/) ||
|
|
462
|
+
line.match(/^def\s+\w+\s*\(/) ||
|
|
463
|
+
line.match(/^(public|private|protected)?\s*(static)?\s+\w+\s+\w+\s*\(/);
|
|
464
|
+
if (isFunctionStart) {
|
|
465
|
+
inFunction = true;
|
|
466
|
+
functionStart = i;
|
|
467
|
+
totalFunctions++;
|
|
468
|
+
}
|
|
469
|
+
// Detect function end
|
|
470
|
+
if (inFunction && line.match(/^}/)) {
|
|
471
|
+
const functionBody = lines.slice(functionStart, i + 1).join('\n');
|
|
472
|
+
// Check if function contains try-catch
|
|
473
|
+
const hasTryCatch = functionBody.match(/\btry\s*{/) !== null;
|
|
474
|
+
if (hasTryCatch) {
|
|
475
|
+
functionsWithTryCatch++;
|
|
476
|
+
}
|
|
477
|
+
inFunction = false;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const tryCatchRatio = totalFunctions > 0 ? functionsWithTryCatch / totalFunctions : 0;
|
|
481
|
+
// GPT-4 fingerprint: ≥50% of functions have try-catch (excessive)
|
|
482
|
+
// Flag if 50% or more functions have try-catch
|
|
483
|
+
if (tryCatchRatio < 0.5)
|
|
484
|
+
return 0;
|
|
485
|
+
return Math.min(tryCatchRatio * 2 - 0.9, 1.0); // Maps 50% → 0.1, 75% → 0.6, 100% → 1.0
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* GPT-4 Fingerprint #4: Helper function proliferation
|
|
489
|
+
* Pattern: >30% of functions are single-line helpers used only once
|
|
490
|
+
*/
|
|
491
|
+
function detectHelperFunctionProliferation(lines) {
|
|
492
|
+
const functionNames = new Map(); // name -> line count
|
|
493
|
+
const functionUsage = new Map(); // name -> usage count
|
|
494
|
+
let inFunction = false;
|
|
495
|
+
let currentFunctionName = '';
|
|
496
|
+
let functionStart = -1;
|
|
497
|
+
// Phase 1: Identify functions and count their body lines
|
|
498
|
+
for (let i = 0; i < lines.length; i++) {
|
|
499
|
+
const trimmed = lines[i].trim();
|
|
500
|
+
// Detect function declaration
|
|
501
|
+
const funcMatch = trimmed.match(/^function\s+(\w+)\s*\(/) ||
|
|
502
|
+
trimmed.match(/^def\s+(\w+)\s*\(/) ||
|
|
503
|
+
trimmed.match(/^(?:public|private|protected)?\s*(?:static)?\s+\w+\s+(\w+)\s*\(/);
|
|
504
|
+
if (funcMatch) {
|
|
505
|
+
inFunction = true;
|
|
506
|
+
currentFunctionName = funcMatch[1];
|
|
507
|
+
functionStart = i;
|
|
508
|
+
// Check if it's a one-liner (declaration and closing brace on same line)
|
|
509
|
+
if (trimmed.includes('}') && trimmed.endsWith('}')) {
|
|
510
|
+
const lineCount = 1; // One-liner function
|
|
511
|
+
functionNames.set(currentFunctionName, lineCount);
|
|
512
|
+
functionUsage.set(currentFunctionName, 0); // Initialize usage count
|
|
513
|
+
inFunction = false;
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
// Check if it's a Python one-liner (def foo(): return bar)
|
|
517
|
+
if (trimmed.match(/^def\s+\w+\([^)]*\):\s*return/)) {
|
|
518
|
+
const lineCount = 1; // Python one-liner
|
|
519
|
+
functionNames.set(currentFunctionName, lineCount);
|
|
520
|
+
functionUsage.set(currentFunctionName, 0); // Initialize usage count
|
|
521
|
+
inFunction = false;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// Detect function end (closing brace)
|
|
526
|
+
if (inFunction && (trimmed === '}' || trimmed.startsWith('}'))) {
|
|
527
|
+
const lineCount = i - functionStart;
|
|
528
|
+
functionNames.set(currentFunctionName, lineCount);
|
|
529
|
+
functionUsage.set(currentFunctionName, 0); // Initialize usage count
|
|
530
|
+
inFunction = false;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// Phase 2: Count function usage
|
|
534
|
+
const code = lines.join('\n');
|
|
535
|
+
functionNames.forEach((_, name) => {
|
|
536
|
+
const usagePattern = new RegExp(`\\b${name}\\s*\\(`, 'g');
|
|
537
|
+
const matches = code.match(usagePattern) || [];
|
|
538
|
+
functionUsage.set(name, matches.length - 1); // -1 to exclude declaration
|
|
539
|
+
});
|
|
540
|
+
// Phase 3: Count single-line helpers used ≤1 time
|
|
541
|
+
let helperCount = 0;
|
|
542
|
+
functionNames.forEach((lineCount, name) => {
|
|
543
|
+
const usageCount = functionUsage.get(name) || 0;
|
|
544
|
+
// Only flag truly trivial one-liners (lineCount == 1) with low usage
|
|
545
|
+
if (lineCount == 1 && usageCount <= 1) {
|
|
546
|
+
helperCount++;
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
const helperRatio = functionNames.size > 0 ? helperCount / functionNames.size : 0;
|
|
550
|
+
// GPT-4 fingerprint: >30% are single-use helpers
|
|
551
|
+
return helperRatio > 0.3 ? 1.0 : helperRatio / 0.3;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* GPT-4 Fingerprint #5: Overly descriptive variable names
|
|
555
|
+
* Pattern: >3 variables with names >25 characters
|
|
556
|
+
*/
|
|
557
|
+
function detectOverlyDescriptiveNames(lines) {
|
|
558
|
+
const longVariableNames = new Set();
|
|
559
|
+
lines.forEach(line => {
|
|
560
|
+
// Match variable declarations
|
|
561
|
+
const varPattern = /(?:const|let|var|val)\s+(\w{26,})\s*=/g;
|
|
562
|
+
const matches = Array.from(line.matchAll(varPattern));
|
|
563
|
+
matches.forEach(match => {
|
|
564
|
+
longVariableNames.add(match[1]);
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
return Math.min(longVariableNames.size / 3, 1.0); // Normalize (3+ = verbose)
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* GPT-4 Fingerprint #6: Premature optimization comments
|
|
571
|
+
* Pattern: Comments mentioning "performance", "optimization", "O(n)" before implementation
|
|
572
|
+
*/
|
|
573
|
+
function detectPrematureOptimizationComments(lines) {
|
|
574
|
+
let optimizationComments = 0;
|
|
575
|
+
lines.forEach(line => {
|
|
576
|
+
const trimmed = line.trim();
|
|
577
|
+
const isComment = trimmed.startsWith('//') || trimmed.startsWith('#');
|
|
578
|
+
if (isComment) {
|
|
579
|
+
const hasOptimizationKeywords = /\b(performance|optimization|optimize|O\(\w+\)|time complexity|space complexity)\b/i.test(trimmed);
|
|
580
|
+
if (hasOptimizationKeywords) {
|
|
581
|
+
optimizationComments++;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
return Math.min(optimizationComments / 3, 1.0); // Normalize (3+ = premature)
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* GPT-4 Fingerprint #7: Detailed TODO markers
|
|
589
|
+
* Pattern: TODO comments with >10 words (GPT-4 over-explains)
|
|
590
|
+
*/
|
|
591
|
+
function detectDetailedTodoMarkers(lines) {
|
|
592
|
+
let detailedTodoCount = 0;
|
|
593
|
+
let totalTodos = 0;
|
|
594
|
+
lines.forEach(line => {
|
|
595
|
+
const trimmed = line.trim();
|
|
596
|
+
const isTodo = /TODO|FIXME|XXX/i.test(trimmed);
|
|
597
|
+
if (isTodo) {
|
|
598
|
+
totalTodos++;
|
|
599
|
+
// Extract TODO text
|
|
600
|
+
const todoText = trimmed.replace(/^(\/\/|#)\s*/, '');
|
|
601
|
+
const wordCount = todoText.split(/\s+/).length;
|
|
602
|
+
if (wordCount > 10) {
|
|
603
|
+
detailedTodoCount++;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
return totalTodos > 0 ? Math.min(detailedTodoCount / totalTodos, 1.0) : 0;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* GPT-4 Fingerprint #8: Type annotation overuse
|
|
611
|
+
* Pattern: Type hints on >70% of variables in Python/TypeScript (unnecessary)
|
|
612
|
+
*/
|
|
613
|
+
function detectTypeAnnotationOveruse(lines) {
|
|
614
|
+
let annotatedVariables = 0;
|
|
615
|
+
let totalVariables = 0;
|
|
616
|
+
lines.forEach(line => {
|
|
617
|
+
const trimmed = line.trim();
|
|
618
|
+
// Python type hints: var: str = "value"
|
|
619
|
+
const pythonTypeHint = trimmed.match(/^(\w+)\s*:\s*\w+\s*=/);
|
|
620
|
+
if (pythonTypeHint) {
|
|
621
|
+
annotatedVariables++;
|
|
622
|
+
totalVariables++;
|
|
623
|
+
}
|
|
624
|
+
// Python variable without type hint: var = "value"
|
|
625
|
+
const pythonVar = trimmed.match(/^(\w+)\s*=\s*[^=]/);
|
|
626
|
+
if (pythonVar && !pythonTypeHint) {
|
|
627
|
+
totalVariables++;
|
|
628
|
+
}
|
|
629
|
+
// TypeScript/JavaScript type annotations
|
|
630
|
+
const tsAnnotated = line.match(/(?:const|let|var)\s+\w+\s*:\s*\w+/g) || [];
|
|
631
|
+
annotatedVariables += tsAnnotated.length;
|
|
632
|
+
const tsVars = line.match(/(?:const|let|var)\s+\w+/g) || [];
|
|
633
|
+
totalVariables += tsVars.length;
|
|
634
|
+
});
|
|
635
|
+
const annotationRatio = totalVariables > 0 ? annotatedVariables / totalVariables : 0;
|
|
636
|
+
// GPT-4 fingerprint: >70% annotated (excessive for dynamic languages)
|
|
637
|
+
return annotationRatio > 0.7 ? 1.0 : annotationRatio / 0.7;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* ====================
|
|
641
|
+
* GITHUB COPILOT FINGERPRINT DETECTORS (Week 2)
|
|
642
|
+
* ====================
|
|
643
|
+
*/
|
|
644
|
+
/**
|
|
645
|
+
* GitHub Copilot Fingerprint #1: Boilerplate comments
|
|
646
|
+
* Pattern: Comments like "// your code here", "// TODO: implement this"
|
|
647
|
+
*/
|
|
648
|
+
function detectBoilerplateComments(lines) {
|
|
649
|
+
let boilerplateCount = 0;
|
|
650
|
+
const boilerplatePatterns = [
|
|
651
|
+
/\/\/\s*(your code here|add your code|implementation here|code goes here)/i,
|
|
652
|
+
/#\s*(your code here|add your code|implementation here|code goes here)/i,
|
|
653
|
+
];
|
|
654
|
+
lines.forEach(line => {
|
|
655
|
+
const trimmed = line.trim();
|
|
656
|
+
if (boilerplatePatterns.some(pattern => pattern.test(trimmed))) {
|
|
657
|
+
boilerplateCount++;
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
return Math.min(boilerplateCount / 3, 1.0); // 3+ = high score
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* GitHub Copilot Fingerprint #2: Placeholder TODOs
|
|
664
|
+
* Pattern: Generic TODOs like "TODO: implement", "TODO: add error handling"
|
|
665
|
+
*/
|
|
666
|
+
function detectPlaceholderTodos(lines) {
|
|
667
|
+
let placeholderTodoCount = 0;
|
|
668
|
+
const placeholderPatterns = [
|
|
669
|
+
/TODO:\s*(implement|add|fix|update|handle|check)/i,
|
|
670
|
+
/FIXME:\s*(implement|add|fix|update|handle|check)/i,
|
|
671
|
+
];
|
|
672
|
+
lines.forEach(line => {
|
|
673
|
+
const trimmed = line.trim();
|
|
674
|
+
if (placeholderPatterns.some(pattern => pattern.test(trimmed))) {
|
|
675
|
+
placeholderTodoCount++;
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
return Math.min(placeholderTodoCount / 5, 1.0); // 5+ = high score
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* GitHub Copilot Fingerprint #3: Generic function names
|
|
682
|
+
* Pattern: handleClick, doSomething, processData, etc.
|
|
683
|
+
*/
|
|
684
|
+
function detectGenericFunctionNames(lines) {
|
|
685
|
+
const genericNames = [
|
|
686
|
+
'handleClick', 'handleSubmit', 'handleChange', 'handleEvent',
|
|
687
|
+
'doSomething', 'doStuff', 'processData', 'processInput',
|
|
688
|
+
'getData', 'setData', 'updateData', 'fetchData',
|
|
689
|
+
'myFunction', 'testFunction', 'exampleFunction',
|
|
690
|
+
];
|
|
691
|
+
let genericFunctionCount = 0;
|
|
692
|
+
let totalFunctions = 0;
|
|
693
|
+
lines.forEach(line => {
|
|
694
|
+
const trimmed = line.trim();
|
|
695
|
+
// Detect function declarations
|
|
696
|
+
const funcMatch = trimmed.match(/function\s+(\w+)\s*\(/) ||
|
|
697
|
+
trimmed.match(/def\s+(\w+)\s*\(/) ||
|
|
698
|
+
trimmed.match(/const\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/);
|
|
699
|
+
if (funcMatch) {
|
|
700
|
+
totalFunctions++;
|
|
701
|
+
const functionName = funcMatch[1];
|
|
702
|
+
if (genericNames.includes(functionName)) {
|
|
703
|
+
genericFunctionCount++;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
return totalFunctions > 0 ? Math.min(genericFunctionCount / totalFunctions, 1.0) : 0;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* GitHub Copilot Fingerprint #4: Placeholder constants
|
|
711
|
+
* Pattern: PLACEHOLDER, YOUR_API_KEY, CHANGE_ME, etc.
|
|
712
|
+
*/
|
|
713
|
+
function detectPlaceholderConstants(lines) {
|
|
714
|
+
const placeholderPatterns = [
|
|
715
|
+
/\b(PLACEHOLDER|YOUR_API_KEY|YOUR_SECRET|CHANGE_ME|REPLACE_ME|API_KEY_HERE|TOKEN_HERE)\b/,
|
|
716
|
+
/\b(TODO_REPLACE|FIX_ME|UPDATE_THIS|CHANGEME)\b/,
|
|
717
|
+
];
|
|
718
|
+
let placeholderCount = 0;
|
|
719
|
+
lines.forEach(line => {
|
|
720
|
+
const trimmed = line.trim();
|
|
721
|
+
if (placeholderPatterns.some(pattern => pattern.test(trimmed))) {
|
|
722
|
+
placeholderCount++;
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
return Math.min(placeholderCount / 2, 1.0); // 2+ = high score
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* GitHub Copilot Fingerprint #5: Unusual import ordering
|
|
729
|
+
* Pattern: Copilot often puts imports in alphabetical order, which is uncommon
|
|
730
|
+
*/
|
|
731
|
+
function detectUnusualImportOrdering(lines) {
|
|
732
|
+
const imports = [];
|
|
733
|
+
lines.forEach(line => {
|
|
734
|
+
const trimmed = line.trim();
|
|
735
|
+
if (trimmed.startsWith('import ') || trimmed.startsWith('from ')) {
|
|
736
|
+
imports.push(trimmed);
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
if (imports.length < 3)
|
|
740
|
+
return 0;
|
|
741
|
+
// Check if imports are in strict alphabetical order
|
|
742
|
+
const sortedImports = [...imports].sort();
|
|
743
|
+
const isAlphabetical = imports.every((imp, i) => imp === sortedImports[i]);
|
|
744
|
+
return isAlphabetical ? 0.7 : 0; // Alphabetical imports = 70% score
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* GitHub Copilot Fingerprint #6: Copilot suggestion markers
|
|
748
|
+
* Pattern: Comments mentioning "Copilot", "AI-generated", "auto-generated"
|
|
749
|
+
*/
|
|
750
|
+
function detectCopilotMarkers(lines) {
|
|
751
|
+
const markerPatterns = [
|
|
752
|
+
/\b(copilot|github copilot|ai.generated|auto.generated|generated by ai)\b/i,
|
|
753
|
+
/\b(ai suggestion|suggested by copilot)\b/i,
|
|
754
|
+
];
|
|
755
|
+
let markerCount = 0;
|
|
756
|
+
lines.forEach(line => {
|
|
757
|
+
const trimmed = line.trim();
|
|
758
|
+
if (markerPatterns.some(pattern => pattern.test(trimmed))) {
|
|
759
|
+
markerCount++;
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
return markerCount > 0 ? 1.0 : 0; // Any marker = 100% score
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* GitHub Copilot Fingerprint #7: Excessive type assertions
|
|
766
|
+
* Pattern: Overuse of `as any`, `!` (non-null assertion) in TypeScript
|
|
767
|
+
*/
|
|
768
|
+
function detectExcessiveTypeAssertions(lines) {
|
|
769
|
+
let typeAssertionCount = 0;
|
|
770
|
+
let totalLines = 0;
|
|
771
|
+
lines.forEach(line => {
|
|
772
|
+
const trimmed = line.trim();
|
|
773
|
+
if (trimmed.length > 0 && !trimmed.startsWith('//') && !trimmed.startsWith('#')) {
|
|
774
|
+
totalLines++;
|
|
775
|
+
// Count `as any`, `as unknown`, `!.` (non-null assertion)
|
|
776
|
+
const asAnyCount = (trimmed.match(/\s+as\s+any\b/g) || []).length;
|
|
777
|
+
const asUnknownCount = (trimmed.match(/\s+as\s+unknown\b/g) || []).length;
|
|
778
|
+
const nonNullCount = (trimmed.match(/!\./g) || []).length;
|
|
779
|
+
typeAssertionCount += asAnyCount + asUnknownCount + nonNullCount;
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
const assertionRatio = totalLines > 0 ? typeAssertionCount / totalLines : 0;
|
|
783
|
+
// >10% of lines have type assertions = excessive
|
|
784
|
+
return assertionRatio > 0.1 ? 1.0 : assertionRatio * 10;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* ====================
|
|
788
|
+
* CLAUDE CODE FINGERPRINT DETECTORS (Week 2)
|
|
789
|
+
* ====================
|
|
790
|
+
*/
|
|
791
|
+
/**
|
|
792
|
+
* Claude Code Fingerprint #1: Detailed explanatory comments
|
|
793
|
+
* Pattern: Multi-line comments explaining "why" not just "what" (Claude's style)
|
|
794
|
+
*/
|
|
795
|
+
function detectDetailedExplanatoryComments(lines) {
|
|
796
|
+
let detailedCommentBlocks = 0;
|
|
797
|
+
let inCommentBlock = false;
|
|
798
|
+
let commentLines = 0;
|
|
799
|
+
lines.forEach(line => {
|
|
800
|
+
const trimmed = line.trim();
|
|
801
|
+
// Multi-line comment start
|
|
802
|
+
if (trimmed.startsWith('/*') || trimmed.startsWith('/**')) {
|
|
803
|
+
inCommentBlock = true;
|
|
804
|
+
commentLines = 0;
|
|
805
|
+
}
|
|
806
|
+
if (inCommentBlock) {
|
|
807
|
+
commentLines++;
|
|
808
|
+
// Check for explanatory keywords (why, because, note, important)
|
|
809
|
+
if (/\b(why|because|note that|important|rationale|reason)\b/i.test(trimmed)) {
|
|
810
|
+
detailedCommentBlocks++;
|
|
811
|
+
inCommentBlock = false; // Count once per block
|
|
812
|
+
}
|
|
813
|
+
if (trimmed.endsWith('*/')) {
|
|
814
|
+
inCommentBlock = false;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
// Single-line explanatory comments
|
|
818
|
+
if (trimmed.startsWith('//') && /\b(why|because|note that|important|rationale)\b/i.test(trimmed)) {
|
|
819
|
+
detailedCommentBlocks++;
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
return Math.min(detailedCommentBlocks / 5, 1.0); // 5+ explanatory comments = Claude style
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Claude Code Fingerprint #2: Custom error classes
|
|
826
|
+
* Pattern: Creating custom error classes instead of using built-in errors
|
|
827
|
+
*/
|
|
828
|
+
function detectCustomErrorClasses(lines) {
|
|
829
|
+
let customErrorCount = 0;
|
|
830
|
+
const customErrorPatterns = [
|
|
831
|
+
/class\s+\w+Error\s+extends\s+Error/, // JavaScript/TypeScript
|
|
832
|
+
/class\s+\w+Exception\s*\([^)]*\):/, // Python custom exception
|
|
833
|
+
/public\s+class\s+\w+Exception\s+extends\s+Exception/, // Java
|
|
834
|
+
];
|
|
835
|
+
lines.forEach(line => {
|
|
836
|
+
const trimmed = line.trim();
|
|
837
|
+
if (customErrorPatterns.some(pattern => pattern.test(trimmed))) {
|
|
838
|
+
customErrorCount++;
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
return Math.min(customErrorCount / 2, 1.0); // 2+ custom error classes = Claude style
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Claude Code Fingerprint #3: Extensive input validation
|
|
845
|
+
* Pattern: Multiple validation checks at function entry (Claude is thorough)
|
|
846
|
+
*/
|
|
847
|
+
function detectExtensiveInputValidation(lines) {
|
|
848
|
+
let functionsWithExtensiveValidation = 0;
|
|
849
|
+
let totalFunctions = 0;
|
|
850
|
+
let inFunction = false;
|
|
851
|
+
let validationChecks = 0;
|
|
852
|
+
let linesIntoFunction = 0;
|
|
853
|
+
lines.forEach(line => {
|
|
854
|
+
const trimmed = line.trim();
|
|
855
|
+
// Detect function start
|
|
856
|
+
const isFunctionStart = trimmed.match(/^function\s+\w+\s*\(/) ||
|
|
857
|
+
trimmed.match(/^def\s+\w+\s*\(/) ||
|
|
858
|
+
trimmed.match(/^(public|private|protected)?\s*(static)?\s+\w+\s+\w+\s*\(/);
|
|
859
|
+
if (isFunctionStart) {
|
|
860
|
+
inFunction = true;
|
|
861
|
+
validationChecks = 0;
|
|
862
|
+
linesIntoFunction = 0;
|
|
863
|
+
totalFunctions++;
|
|
864
|
+
}
|
|
865
|
+
if (inFunction) {
|
|
866
|
+
linesIntoFunction++;
|
|
867
|
+
// Count validation patterns in first 10 lines of function
|
|
868
|
+
if (linesIntoFunction <= 10) {
|
|
869
|
+
if (/if\s*\([^)]*(!|===|!==|<|>|typeof|instanceof)\s*\)/.test(trimmed)) {
|
|
870
|
+
validationChecks++;
|
|
871
|
+
}
|
|
872
|
+
if (/throw new \w+Error/.test(trimmed)) {
|
|
873
|
+
validationChecks++;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
// Function end
|
|
877
|
+
if (trimmed === '}' || trimmed.startsWith('}')) {
|
|
878
|
+
if (validationChecks >= 3) {
|
|
879
|
+
functionsWithExtensiveValidation++;
|
|
880
|
+
}
|
|
881
|
+
inFunction = false;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
return totalFunctions > 0 ? Math.min(functionsWithExtensiveValidation / totalFunctions, 1.0) : 0;
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Claude Code Fingerprint #4: Descriptive helper functions
|
|
889
|
+
* Pattern: Helper functions with very clear, descriptive names (Claude's clarity)
|
|
890
|
+
*/
|
|
891
|
+
function detectDescriptiveHelperFunctions(lines) {
|
|
892
|
+
let descriptiveHelperCount = 0;
|
|
893
|
+
let totalFunctions = 0;
|
|
894
|
+
// Claude-style descriptive prefixes
|
|
895
|
+
const descriptivePrefixes = [
|
|
896
|
+
'validate', 'ensure', 'verify', 'check', 'sanitize',
|
|
897
|
+
'normalize', 'transform', 'convert', 'extract', 'parse',
|
|
898
|
+
'calculate', 'compute', 'determine', 'resolve',
|
|
899
|
+
];
|
|
900
|
+
lines.forEach(line => {
|
|
901
|
+
const trimmed = line.trim();
|
|
902
|
+
// Detect function declarations
|
|
903
|
+
const funcMatch = trimmed.match(/^function\s+(\w+)\s*\(/) ||
|
|
904
|
+
trimmed.match(/^def\s+(\w+)\s*\(/) ||
|
|
905
|
+
trimmed.match(/^const\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/);
|
|
906
|
+
if (funcMatch) {
|
|
907
|
+
totalFunctions++;
|
|
908
|
+
const functionName = funcMatch[1];
|
|
909
|
+
// Check if name starts with descriptive prefix
|
|
910
|
+
if (descriptivePrefixes.some(prefix => functionName.toLowerCase().startsWith(prefix))) {
|
|
911
|
+
descriptiveHelperCount++;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
return totalFunctions > 0 ? Math.min(descriptiveHelperCount / totalFunctions, 1.0) : 0;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Claude Code Fingerprint #5: Structured return objects
|
|
919
|
+
* Pattern: Returning objects with { success, data, error } structure
|
|
920
|
+
*/
|
|
921
|
+
function detectStructuredReturnObjects(lines) {
|
|
922
|
+
let structuredReturnCount = 0;
|
|
923
|
+
const structuredReturnPatterns = [
|
|
924
|
+
/return\s*{\s*success\s*:/,
|
|
925
|
+
/return\s*{\s*data\s*:/,
|
|
926
|
+
/return\s*{\s*error\s*:/,
|
|
927
|
+
/return\s*{\s*ok\s*:/,
|
|
928
|
+
/return\s*{\s*result\s*:/,
|
|
929
|
+
/\{\s*success\s*,\s*data\s*,\s*error\s*\}/,
|
|
930
|
+
];
|
|
931
|
+
lines.forEach(line => {
|
|
932
|
+
const trimmed = line.trim();
|
|
933
|
+
if (structuredReturnPatterns.some(pattern => pattern.test(trimmed))) {
|
|
934
|
+
structuredReturnCount++;
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
return Math.min(structuredReturnCount / 3, 1.0); // 3+ structured returns = Claude style
|
|
938
|
+
}
|
|
939
|
+
// =======================================================================================
|
|
940
|
+
// CURSOR BEHAVIORAL FINGERPRINTS (8 patterns)
|
|
941
|
+
// =======================================================================================
|
|
942
|
+
/**
|
|
943
|
+
* Cursor Fingerprint #1: AI command markers
|
|
944
|
+
* Pattern: Comments with AI/Cursor directives
|
|
945
|
+
*/
|
|
946
|
+
function detectAICommandMarkers(lines) {
|
|
947
|
+
let markerCount = 0;
|
|
948
|
+
const aiCommandPatterns = [
|
|
949
|
+
/\/\/\s*AI:/i,
|
|
950
|
+
/\/\/\s*Cursor:/i,
|
|
951
|
+
/#\s*AI:/i,
|
|
952
|
+
/#\s*Cursor:/i,
|
|
953
|
+
/@cursor/i,
|
|
954
|
+
/\/\*\s*@cursor/i,
|
|
955
|
+
/\/\/\s*@ai\s/i,
|
|
956
|
+
];
|
|
957
|
+
lines.forEach(line => {
|
|
958
|
+
const trimmed = line.trim();
|
|
959
|
+
if (aiCommandPatterns.some(pattern => pattern.test(trimmed))) {
|
|
960
|
+
markerCount++;
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
return Math.min(markerCount / 2, 1.0); // 2+ AI command markers = Cursor usage
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Cursor Fingerprint #2: Diff-style comments
|
|
967
|
+
* Pattern: Comments indicating code generation/addition
|
|
968
|
+
*/
|
|
969
|
+
function detectDiffStyleComments(lines) {
|
|
970
|
+
let diffCommentCount = 0;
|
|
971
|
+
const diffStylePatterns = [
|
|
972
|
+
/\/\/\s*Added by Cursor/i,
|
|
973
|
+
/\/\/\s*Generated block/i,
|
|
974
|
+
/\/\/\s*Cursor suggestion/i,
|
|
975
|
+
/#\s*Added by Cursor/i,
|
|
976
|
+
/#\s*Generated by AI/i,
|
|
977
|
+
/\/\/\s*AI-assisted code/i,
|
|
978
|
+
/\/\*\s*Generated code\s*\*\//i,
|
|
979
|
+
];
|
|
980
|
+
lines.forEach(line => {
|
|
981
|
+
const trimmed = line.trim();
|
|
982
|
+
if (diffStylePatterns.some(pattern => pattern.test(trimmed))) {
|
|
983
|
+
diffCommentCount++;
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
return Math.min(diffCommentCount / 2, 1.0); // 2+ diff-style comments
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Cursor Fingerprint #3: Tab completion artifacts
|
|
990
|
+
* Pattern: Incomplete suggestions, placeholder text left in code
|
|
294
991
|
*/
|
|
295
|
-
function
|
|
296
|
-
|
|
297
|
-
const
|
|
992
|
+
function detectTabCompletionArtifacts(lines) {
|
|
993
|
+
let artifactCount = 0;
|
|
994
|
+
const artifactPatterns = [
|
|
995
|
+
/\.\.\.\s*$/, // Trailing ellipsis
|
|
996
|
+
/\/\/\s*\.\.\./, // Comment with ellipsis
|
|
997
|
+
/#\s*\.\.\./, // Python comment with ellipsis
|
|
998
|
+
/\bTODO:\s*$/i, // Empty TODO
|
|
999
|
+
/\/\/\s*TODO\s*$/i, // Empty TODO comment
|
|
1000
|
+
/\?\?\?/, // Question marks placeholder
|
|
1001
|
+
/<<<<<<</, // Merge conflict markers (Cursor artifacts)
|
|
1002
|
+
/>>>>>>>/,
|
|
1003
|
+
];
|
|
1004
|
+
lines.forEach(line => {
|
|
1005
|
+
const trimmed = line.trim();
|
|
1006
|
+
if (artifactPatterns.some(pattern => pattern.test(trimmed))) {
|
|
1007
|
+
artifactCount++;
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
return Math.min(artifactCount / 3, 1.0); // 3+ artifacts
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Cursor Fingerprint #4: Context window leakage
|
|
1014
|
+
* Pattern: Comments referencing files/context not in codebase
|
|
1015
|
+
*/
|
|
1016
|
+
function detectContextWindowLeakage(lines) {
|
|
1017
|
+
let leakageCount = 0;
|
|
1018
|
+
const leakagePatterns = [
|
|
1019
|
+
/\/\/\s*From:\s*[a-zA-Z0-9_\-]+\.(js|ts|py|java|go)/i, // "From: otherfile.js"
|
|
1020
|
+
/\/\/\s*See:\s*[a-zA-Z0-9_\-]+\.(js|ts|py|java|go)/i, // "See: utils.ts"
|
|
1021
|
+
/\/\/\s*Similar to\s+[a-zA-Z0-9_\-]+/i, // "Similar to OtherClass"
|
|
1022
|
+
/#\s*Based on\s+[a-zA-Z0-9_\-]+/i, // "Based on module_name"
|
|
1023
|
+
/\/\/\s*Imported from\s+/i,
|
|
1024
|
+
];
|
|
1025
|
+
lines.forEach(line => {
|
|
1026
|
+
const trimmed = line.trim();
|
|
1027
|
+
if (leakagePatterns.some(pattern => pattern.test(trimmed))) {
|
|
1028
|
+
leakageCount++;
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
return Math.min(leakageCount / 2, 1.0); // 2+ context leakage comments
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Cursor Fingerprint #5: Over-generic exports
|
|
1035
|
+
* Pattern: Export statements with placeholder/generic patterns
|
|
1036
|
+
*/
|
|
1037
|
+
function detectOverGenericExports(lines) {
|
|
1038
|
+
let genericExportCount = 0;
|
|
1039
|
+
const genericExportPatterns = [
|
|
1040
|
+
/export\s*\{\s*everything\s*\}/i,
|
|
1041
|
+
/export\s+\*\s+as\s+utils\s+from/i,
|
|
1042
|
+
/export\s+\*\s+as\s+helpers\s+from/i,
|
|
1043
|
+
/export\s+\*\s+as\s+components\s+from/i,
|
|
1044
|
+
/export\s+default\s+\{\}/, // Empty default export
|
|
1045
|
+
];
|
|
1046
|
+
lines.forEach(line => {
|
|
1047
|
+
const trimmed = line.trim();
|
|
1048
|
+
if (genericExportPatterns.some(pattern => pattern.test(trimmed))) {
|
|
1049
|
+
genericExportCount++;
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
return Math.min(genericExportCount / 2, 1.0); // 2+ generic exports
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Cursor Fingerprint #6: Unused import cleanup
|
|
1056
|
+
* Pattern: Perfect alphabetical imports with no actual usage
|
|
1057
|
+
*/
|
|
1058
|
+
function detectUnusedImportCleanup(lines) {
|
|
1059
|
+
const imports = [];
|
|
1060
|
+
let alphabeticalCount = 0;
|
|
1061
|
+
lines.forEach(line => {
|
|
1062
|
+
const trimmed = line.trim();
|
|
1063
|
+
// Detect import statements
|
|
1064
|
+
const importMatch = trimmed.match(/^import\s+.*\s+from\s+['"]([^'"]+)['"]/) ||
|
|
1065
|
+
trimmed.match(/^from\s+([^\s]+)\s+import/);
|
|
1066
|
+
if (importMatch) {
|
|
1067
|
+
imports.push(trimmed);
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
// Check if imports are perfectly alphabetical (Cursor cleanup signature)
|
|
1071
|
+
if (imports.length >= 5) {
|
|
1072
|
+
const sorted = [...imports].sort();
|
|
1073
|
+
alphabeticalCount = imports.every((imp, i) => imp === sorted[i]) ? 1 : 0;
|
|
1074
|
+
}
|
|
1075
|
+
return alphabeticalCount;
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Cursor Fingerprint #7: Placeholder error messages
|
|
1079
|
+
* Pattern: Generic error messages from AI suggestions
|
|
1080
|
+
*/
|
|
1081
|
+
function detectPlaceholderErrorMessages(lines) {
|
|
1082
|
+
let placeholderErrorCount = 0;
|
|
1083
|
+
const placeholderErrorPatterns = [
|
|
1084
|
+
/(throw new Error|raise Exception)\s*\(\s*['"]An error occurred['"]\s*\)/i,
|
|
1085
|
+
/(throw new Error|raise Exception)\s*\(\s*['"]Something went wrong['"]\s*\)/i,
|
|
1086
|
+
/(throw new Error|raise Exception)\s*\(\s*['"]Error['"]\s*\)/i,
|
|
1087
|
+
/(throw new Error|raise Exception)\s*\(\s*['"]Invalid input['"]\s*\)/i,
|
|
1088
|
+
/(throw new Error|raise Exception)\s*\(\s*['"]Failed['"]\s*\)/i,
|
|
1089
|
+
/console\.error\s*\(\s*['"]Error:['"]/i,
|
|
1090
|
+
/logger\.error\s*\(\s*['"]An error/i,
|
|
1091
|
+
];
|
|
1092
|
+
lines.forEach(line => {
|
|
1093
|
+
const trimmed = line.trim();
|
|
1094
|
+
if (placeholderErrorPatterns.some(pattern => pattern.test(trimmed))) {
|
|
1095
|
+
placeholderErrorCount++;
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
return Math.min(placeholderErrorCount / 3, 1.0); // 3+ placeholder errors
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Cursor Fingerprint #8: Inline documentation overload
|
|
1102
|
+
* Pattern: JSDoc/docstrings on every single line (Cursor over-generates docs)
|
|
1103
|
+
*/
|
|
1104
|
+
function detectInlineDocumentationOverload(lines) {
|
|
1105
|
+
let docLineCount = 0;
|
|
1106
|
+
let codeLineCount = 0;
|
|
1107
|
+
lines.forEach(line => {
|
|
1108
|
+
const trimmed = line.trim();
|
|
1109
|
+
// Skip empty lines
|
|
1110
|
+
if (trimmed.length === 0)
|
|
1111
|
+
return;
|
|
1112
|
+
// Count documentation lines
|
|
1113
|
+
if (trimmed.startsWith('/**') ||
|
|
1114
|
+
trimmed.startsWith('*') ||
|
|
1115
|
+
trimmed.startsWith('*/') ||
|
|
1116
|
+
trimmed.startsWith('///') ||
|
|
1117
|
+
trimmed.startsWith('///')) {
|
|
1118
|
+
docLineCount++;
|
|
1119
|
+
}
|
|
1120
|
+
else if (trimmed.startsWith('//') || trimmed.startsWith('#')) {
|
|
1121
|
+
// Count inline comments
|
|
1122
|
+
docLineCount++;
|
|
1123
|
+
}
|
|
1124
|
+
else {
|
|
1125
|
+
codeLineCount++;
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
// Cursor signature: >60% of lines are documentation/comments
|
|
1129
|
+
const docRatio = codeLineCount > 0 ? docLineCount / (docLineCount + codeLineCount) : 0;
|
|
1130
|
+
return docRatio > 0.6 ? 1.0 : docRatio / 0.6;
|
|
1131
|
+
}
|
|
1132
|
+
// =======================================================================================
|
|
1133
|
+
// "PERFECT CODE" HEURISTICS (5 patterns) - AI code is "too perfect"
|
|
1134
|
+
// =======================================================================================
|
|
1135
|
+
/**
|
|
1136
|
+
* Perfect Code #1: Zero edge cases
|
|
1137
|
+
* Pattern: Functions with no error handling or boundary checks (AI assumes happy path)
|
|
1138
|
+
*/
|
|
1139
|
+
function detectZeroEdgeCases(lines) {
|
|
1140
|
+
let functionCount = 0;
|
|
1141
|
+
let functionsWithErrorHandling = 0;
|
|
1142
|
+
let inFunction = false;
|
|
1143
|
+
let currentFunctionHasErrorHandling = false;
|
|
1144
|
+
lines.forEach(line => {
|
|
1145
|
+
const trimmed = line.trim();
|
|
1146
|
+
// Detect function start
|
|
1147
|
+
if (trimmed.match(/^function\s+\w+\s*\(/) ||
|
|
1148
|
+
trimmed.match(/^(async\s+)?function\s*\(/) ||
|
|
1149
|
+
trimmed.match(/^def\s+\w+\s*\(/) ||
|
|
1150
|
+
trimmed.match(/^(public|private|protected)?\s*(static\s+)?\w+\s+\w+\s*\(/)) {
|
|
1151
|
+
if (inFunction && !currentFunctionHasErrorHandling) {
|
|
1152
|
+
// Previous function had no error handling
|
|
1153
|
+
}
|
|
1154
|
+
else if (inFunction) {
|
|
1155
|
+
functionsWithErrorHandling++;
|
|
1156
|
+
}
|
|
1157
|
+
inFunction = true;
|
|
1158
|
+
currentFunctionHasErrorHandling = false;
|
|
1159
|
+
functionCount++;
|
|
1160
|
+
}
|
|
1161
|
+
// Detect error handling patterns
|
|
1162
|
+
if (inFunction) {
|
|
1163
|
+
if (trimmed.includes('try') ||
|
|
1164
|
+
trimmed.includes('catch') ||
|
|
1165
|
+
trimmed.includes('except') ||
|
|
1166
|
+
trimmed.includes('if (!') ||
|
|
1167
|
+
trimmed.includes('if (!') ||
|
|
1168
|
+
trimmed.includes('if (') && (trimmed.includes('null') || trimmed.includes('undefined')) ||
|
|
1169
|
+
trimmed.includes('throw') ||
|
|
1170
|
+
trimmed.includes('raise') ||
|
|
1171
|
+
trimmed.includes('assert')) {
|
|
1172
|
+
currentFunctionHasErrorHandling = true;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
// Detect function end
|
|
1176
|
+
if (trimmed === '}' && inFunction) {
|
|
1177
|
+
if (currentFunctionHasErrorHandling) {
|
|
1178
|
+
functionsWithErrorHandling++;
|
|
1179
|
+
}
|
|
1180
|
+
inFunction = false;
|
|
1181
|
+
currentFunctionHasErrorHandling = false;
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
// AI code: >60% of functions have NO error handling (assumes happy path)
|
|
1185
|
+
const noErrorHandlingRatio = functionCount > 0 ? 1 - (functionsWithErrorHandling / functionCount) : 0;
|
|
1186
|
+
return noErrorHandlingRatio > 0.6 ? 1.0 : noErrorHandlingRatio / 0.6;
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Perfect Code #2: Uniform indentation
|
|
1190
|
+
* Pattern: Perfectly aligned code (humans are messy, AI is perfect)
|
|
1191
|
+
*/
|
|
1192
|
+
function detectUniformIndentation(lines) {
|
|
1193
|
+
const indentationLevels = [];
|
|
1194
|
+
lines.forEach(line => {
|
|
1195
|
+
if (line.trim().length === 0)
|
|
1196
|
+
return; // Skip empty lines
|
|
1197
|
+
// Count leading spaces
|
|
1198
|
+
const leadingSpaces = line.match(/^(\s*)/)?.[1].length || 0;
|
|
1199
|
+
if (leadingSpaces > 0) {
|
|
1200
|
+
indentationLevels.push(leadingSpaces);
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
if (indentationLevels.length < 10)
|
|
1204
|
+
return 0; // Not enough data
|
|
1205
|
+
// Check if all indentation is perfectly consistent (multiples of 2 or 4)
|
|
1206
|
+
const allMultiplesOf2 = indentationLevels.every(level => level % 2 === 0);
|
|
1207
|
+
const allMultiplesOf4 = indentationLevels.every(level => level % 4 === 0);
|
|
1208
|
+
// Check for perfect uniformity (no variation)
|
|
1209
|
+
const uniqueLevels = new Set(indentationLevels);
|
|
1210
|
+
const uniformityRatio = 1 - (uniqueLevels.size / indentationLevels.length);
|
|
1211
|
+
// AI signature: Perfect multiples + high uniformity
|
|
1212
|
+
if ((allMultiplesOf2 || allMultiplesOf4) && uniformityRatio > 0.8) {
|
|
1213
|
+
return 1.0;
|
|
1214
|
+
}
|
|
1215
|
+
else if ((allMultiplesOf2 || allMultiplesOf4) && uniformityRatio > 0.6) {
|
|
1216
|
+
return 0.5;
|
|
1217
|
+
}
|
|
1218
|
+
return 0;
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Perfect Code #3: Textbook variable names
|
|
1222
|
+
* Pattern: Generic tutorial-style names (firstName, lastName, emailAddress)
|
|
1223
|
+
*/
|
|
1224
|
+
function detectTextbookVariableNames(lines) {
|
|
1225
|
+
let textbookNameCount = 0;
|
|
1226
|
+
const textbookNames = [
|
|
1227
|
+
'firstName', 'first_name', 'lastname', 'last_name',
|
|
1228
|
+
'emailAddress', 'email_address', 'phoneNumber', 'phone_number',
|
|
1229
|
+
'userName', 'user_name', 'password', 'confirmPassword',
|
|
1230
|
+
'startDate', 'endDate', 'createdAt', 'updatedAt',
|
|
1231
|
+
'userId', 'user_id', 'customerId', 'customer_id',
|
|
1232
|
+
];
|
|
1233
|
+
lines.forEach(line => {
|
|
1234
|
+
textbookNames.forEach(name => {
|
|
1235
|
+
if (line.includes(name)) {
|
|
1236
|
+
textbookNameCount++;
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
});
|
|
1240
|
+
// AI uses textbook names from tutorials
|
|
1241
|
+
return Math.min(textbookNameCount / 5, 1.0); // 5+ textbook names = AI
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Perfect Code #4: No comments + perfect structure
|
|
1245
|
+
* Pattern: Production code is messy; AI generates clean, structured code with zero comments
|
|
1246
|
+
*/
|
|
1247
|
+
function detectNoCommentsWithPerfectStructure(lines) {
|
|
1248
|
+
let commentCount = 0;
|
|
1249
|
+
let structuralComplexity = 0;
|
|
1250
|
+
lines.forEach(line => {
|
|
1251
|
+
const trimmed = line.trim();
|
|
1252
|
+
// Count comments
|
|
1253
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('/*')) {
|
|
1254
|
+
commentCount++;
|
|
1255
|
+
}
|
|
1256
|
+
// Count structural elements
|
|
1257
|
+
if (trimmed.includes('function') ||
|
|
1258
|
+
trimmed.includes('def ') ||
|
|
1259
|
+
trimmed.includes('class ') ||
|
|
1260
|
+
trimmed.includes('if (') ||
|
|
1261
|
+
trimmed.includes('for (') ||
|
|
1262
|
+
trimmed.includes('while (')) {
|
|
1263
|
+
structuralComplexity++;
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
// AI signature: >10 structural elements with <2 comments
|
|
1267
|
+
if (structuralComplexity > 10 && commentCount < 2) {
|
|
1268
|
+
return 1.0;
|
|
1269
|
+
}
|
|
1270
|
+
else if (structuralComplexity > 5 && commentCount === 0) {
|
|
1271
|
+
return 0.5;
|
|
1272
|
+
}
|
|
1273
|
+
return 0;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Perfect Code #5: Excessive parameter validation
|
|
1277
|
+
* Pattern: AI validates every parameter, even in private functions
|
|
1278
|
+
*/
|
|
1279
|
+
function detectExcessiveParameterValidation(lines) {
|
|
1280
|
+
let functionCount = 0;
|
|
1281
|
+
let functionsWithParamValidation = 0;
|
|
1282
|
+
let inFunction = false;
|
|
1283
|
+
let currentFunctionHasValidation = false;
|
|
1284
|
+
let linesIntoFunction = 0;
|
|
1285
|
+
lines.forEach(line => {
|
|
1286
|
+
const trimmed = line.trim();
|
|
1287
|
+
// Detect function start
|
|
1288
|
+
if (trimmed.match(/^function\s+\w+\s*\(/) ||
|
|
1289
|
+
trimmed.match(/^def\s+\w+\s*\(/) ||
|
|
1290
|
+
trimmed.match(/^(public|private|protected)?\s*\w+\s+\w+\s*\(/)) {
|
|
1291
|
+
if (inFunction && currentFunctionHasValidation) {
|
|
1292
|
+
functionsWithParamValidation++;
|
|
1293
|
+
}
|
|
1294
|
+
inFunction = true;
|
|
1295
|
+
currentFunctionHasValidation = false;
|
|
1296
|
+
linesIntoFunction = 0;
|
|
1297
|
+
functionCount++;
|
|
1298
|
+
}
|
|
1299
|
+
// Check for parameter validation in first 5 lines of function
|
|
1300
|
+
if (inFunction && linesIntoFunction < 5) {
|
|
1301
|
+
if (trimmed.includes('if (!') ||
|
|
1302
|
+
trimmed.includes('if (typeof') ||
|
|
1303
|
+
trimmed.includes('if (') && (trimmed.includes('=== null') || trimmed.includes('=== undefined')) ||
|
|
1304
|
+
trimmed.includes('throw') && trimmed.includes('Invalid') ||
|
|
1305
|
+
trimmed.includes('raise') && (trimmed.includes('ValueError') || trimmed.includes('TypeError'))) {
|
|
1306
|
+
currentFunctionHasValidation = true;
|
|
1307
|
+
}
|
|
1308
|
+
linesIntoFunction++;
|
|
1309
|
+
}
|
|
1310
|
+
// Detect function end
|
|
1311
|
+
if (trimmed === '}' && inFunction) {
|
|
1312
|
+
if (currentFunctionHasValidation) {
|
|
1313
|
+
functionsWithParamValidation++;
|
|
1314
|
+
}
|
|
1315
|
+
inFunction = false;
|
|
1316
|
+
currentFunctionHasValidation = false;
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
// AI validates >70% of functions (even private ones)
|
|
1320
|
+
const validationRatio = functionCount > 0 ? functionsWithParamValidation / functionCount : 0;
|
|
1321
|
+
return validationRatio > 0.7 ? 1.0 : validationRatio / 0.7;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Calculate confidence score from hallucination count, heuristics, and LLM fingerprints
|
|
1325
|
+
*
|
|
1326
|
+
* Scoring weights:
|
|
1327
|
+
* - Hallucination patterns: 60% (strongest signal - causes runtime errors)
|
|
1328
|
+
* - Code smell heuristics: 25% (structural patterns)
|
|
1329
|
+
* - LLM fingerprints: 15% (behavioral patterns)
|
|
1330
|
+
*/
|
|
1331
|
+
function calculateAICodeConfidence(hallucinationCount, heuristicScores, llmFingerprintScores) {
|
|
1332
|
+
// Calculate weighted heuristic score (13 heuristics total)
|
|
1333
|
+
const heuristicScore =
|
|
1334
|
+
// Original 8 heuristics
|
|
1335
|
+
heuristicScores.overEngineeredErrors * HEURISTIC_WEIGHTS.overEngineeredErrors +
|
|
298
1336
|
heuristicScores.unnecessaryWrappers * HEURISTIC_WEIGHTS.unnecessaryWrappers +
|
|
299
1337
|
heuristicScores.verboseComments * HEURISTIC_WEIGHTS.verboseComments +
|
|
300
1338
|
heuristicScores.mixedNaming * HEURISTIC_WEIGHTS.mixedNaming +
|
|
301
1339
|
heuristicScores.redundantNullChecks * HEURISTIC_WEIGHTS.redundantNullChecks +
|
|
302
1340
|
heuristicScores.unnecessaryAsync * HEURISTIC_WEIGHTS.unnecessaryAsync +
|
|
303
1341
|
heuristicScores.genericVariables * HEURISTIC_WEIGHTS.genericVariables +
|
|
304
|
-
heuristicScores.inconsistentStrings * HEURISTIC_WEIGHTS.inconsistentStrings
|
|
305
|
-
|
|
306
|
-
|
|
1342
|
+
heuristicScores.inconsistentStrings * HEURISTIC_WEIGHTS.inconsistentStrings +
|
|
1343
|
+
// Perfect code heuristics (5 new)
|
|
1344
|
+
heuristicScores.zeroEdgeCases * HEURISTIC_WEIGHTS.zeroEdgeCases +
|
|
1345
|
+
heuristicScores.uniformIndentation * HEURISTIC_WEIGHTS.uniformIndentation +
|
|
1346
|
+
heuristicScores.textbookVariableNames * HEURISTIC_WEIGHTS.textbookVariableNames +
|
|
1347
|
+
heuristicScores.noCommentsWithPerfectStructure * HEURISTIC_WEIGHTS.noCommentsWithPerfectStructure +
|
|
1348
|
+
heuristicScores.excessiveParameterValidation * HEURISTIC_WEIGHTS.excessiveParameterValidation;
|
|
1349
|
+
// Calculate weighted LLM fingerprint scores (all 4 LLM tools)
|
|
1350
|
+
let llmFingerprintScore = 0;
|
|
1351
|
+
if (llmFingerprintScores) {
|
|
1352
|
+
// GPT-4 fingerprints
|
|
1353
|
+
const gpt4Score = (llmFingerprintScores.verboseDocstrings || 0) * GPT4_FINGERPRINT_WEIGHTS.verboseDocstrings +
|
|
1354
|
+
(llmFingerprintScores.defensiveNullChecks || 0) * GPT4_FINGERPRINT_WEIGHTS.defensiveNullChecks +
|
|
1355
|
+
(llmFingerprintScores.excessiveTryCatch || 0) * GPT4_FINGERPRINT_WEIGHTS.excessiveTryCatch +
|
|
1356
|
+
(llmFingerprintScores.helperFunctionProliferation || 0) * GPT4_FINGERPRINT_WEIGHTS.helperFunctionProliferation +
|
|
1357
|
+
(llmFingerprintScores.overlyDescriptiveNames || 0) * GPT4_FINGERPRINT_WEIGHTS.overlyDescriptiveNames +
|
|
1358
|
+
(llmFingerprintScores.prematureOptimizationComments || 0) * GPT4_FINGERPRINT_WEIGHTS.prematureOptimizationComments +
|
|
1359
|
+
(llmFingerprintScores.detailedTodoMarkers || 0) * GPT4_FINGERPRINT_WEIGHTS.detailedTodoMarkers +
|
|
1360
|
+
(llmFingerprintScores.typeAnnotationOveruse || 0) * GPT4_FINGERPRINT_WEIGHTS.typeAnnotationOveruse;
|
|
1361
|
+
// GitHub Copilot fingerprints (equal weight to GPT-4)
|
|
1362
|
+
const copilotScore = (llmFingerprintScores.boilerplateComments || 0) * 0.15 +
|
|
1363
|
+
(llmFingerprintScores.placeholderTodos || 0) * 0.14 +
|
|
1364
|
+
(llmFingerprintScores.genericFunctionNames || 0) * 0.15 +
|
|
1365
|
+
(llmFingerprintScores.placeholderConstants || 0) * 0.14 +
|
|
1366
|
+
(llmFingerprintScores.unusualImportOrdering || 0) * 0.14 +
|
|
1367
|
+
(llmFingerprintScores.copilotMarkers || 0) * 0.14 +
|
|
1368
|
+
(llmFingerprintScores.excessiveTypeAssertions || 0) * 0.14;
|
|
1369
|
+
// Claude Code fingerprints (equal weight to GPT-4)
|
|
1370
|
+
const claudeScore = (llmFingerprintScores.detailedExplanatoryComments || 0) * 0.20 +
|
|
1371
|
+
(llmFingerprintScores.customErrorClasses || 0) * 0.20 +
|
|
1372
|
+
(llmFingerprintScores.extensiveInputValidation || 0) * 0.20 +
|
|
1373
|
+
(llmFingerprintScores.descriptiveHelperFunctions || 0) * 0.20 +
|
|
1374
|
+
(llmFingerprintScores.structuredReturnObjects || 0) * 0.20;
|
|
1375
|
+
// Cursor fingerprints
|
|
1376
|
+
const cursorScore = (llmFingerprintScores.aiCommandMarkers || 0) * CURSOR_FINGERPRINT_WEIGHTS.aiCommandMarkers +
|
|
1377
|
+
(llmFingerprintScores.diffStyleComments || 0) * CURSOR_FINGERPRINT_WEIGHTS.diffStyleComments +
|
|
1378
|
+
(llmFingerprintScores.tabCompletionArtifacts || 0) * CURSOR_FINGERPRINT_WEIGHTS.tabCompletionArtifacts +
|
|
1379
|
+
(llmFingerprintScores.contextWindowLeakage || 0) * CURSOR_FINGERPRINT_WEIGHTS.contextWindowLeakage +
|
|
1380
|
+
(llmFingerprintScores.overGenericExports || 0) * CURSOR_FINGERPRINT_WEIGHTS.overGenericExports +
|
|
1381
|
+
(llmFingerprintScores.unusedImportCleanup || 0) * CURSOR_FINGERPRINT_WEIGHTS.unusedImportCleanup +
|
|
1382
|
+
(llmFingerprintScores.placeholderErrorMessages || 0) * CURSOR_FINGERPRINT_WEIGHTS.placeholderErrorMessages +
|
|
1383
|
+
(llmFingerprintScores.inlineDocumentationOverload || 0) * CURSOR_FINGERPRINT_WEIGHTS.inlineDocumentationOverload;
|
|
1384
|
+
// Average all LLM tool scores (equal weight per tool)
|
|
1385
|
+
llmFingerprintScore = (gpt4Score + copilotScore + claudeScore + cursorScore) / 4;
|
|
1386
|
+
}
|
|
1387
|
+
// Combined weighted score (hallucinations 60%, heuristics 25%, LLM fingerprints 15%)
|
|
1388
|
+
const hallucinationWeight = hallucinationCount > 0 ? Math.min(hallucinationCount / 3, 1.0) : 0;
|
|
1389
|
+
const combinedScore = (hallucinationWeight * 0.6) + (heuristicScore * 0.25) + (llmFingerprintScore * 0.15);
|
|
1390
|
+
// HIGH confidence: 2+ hallucinations OR combined score >= 0.6
|
|
1391
|
+
if (hallucinationCount >= 2 || combinedScore >= 0.6) {
|
|
307
1392
|
return {
|
|
308
1393
|
hallucinationPatterns: hallucinationCount,
|
|
309
|
-
heuristicScore,
|
|
1394
|
+
heuristicScore: combinedScore,
|
|
310
1395
|
confidence: 'HIGH',
|
|
311
1396
|
severity: 'CRITICAL',
|
|
312
1397
|
};
|
|
313
1398
|
}
|
|
314
|
-
// MEDIUM confidence: 1 hallucination OR
|
|
315
|
-
if (hallucinationCount === 1 ||
|
|
1399
|
+
// MEDIUM confidence: 1 hallucination OR combined score >= 0.4
|
|
1400
|
+
if (hallucinationCount === 1 || combinedScore >= 0.4) {
|
|
316
1401
|
return {
|
|
317
1402
|
hallucinationPatterns: hallucinationCount,
|
|
318
|
-
heuristicScore,
|
|
1403
|
+
heuristicScore: combinedScore,
|
|
319
1404
|
confidence: 'MEDIUM',
|
|
320
1405
|
severity: 'HIGH',
|
|
321
1406
|
};
|
|
322
1407
|
}
|
|
323
|
-
// LOW confidence: Weak
|
|
324
|
-
if (
|
|
1408
|
+
// LOW confidence: Weak signals (combined score >= 0.25)
|
|
1409
|
+
if (combinedScore >= 0.25) {
|
|
325
1410
|
return {
|
|
326
1411
|
hallucinationPatterns: 0,
|
|
327
|
-
heuristicScore,
|
|
1412
|
+
heuristicScore: combinedScore,
|
|
328
1413
|
confidence: 'LOW',
|
|
329
1414
|
severity: 'MEDIUM',
|
|
330
1415
|
};
|