@gulu9527/code-trust 0.2.0 → 0.2.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.
- package/README.md +9 -0
- package/dist/cli/index.js +305 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -105,6 +105,12 @@ declare class ScanEngine {
|
|
|
105
105
|
declare function loadConfig(searchFrom?: string): Promise<CodeTrustConfig>;
|
|
106
106
|
declare function generateDefaultConfig(): string;
|
|
107
107
|
|
|
108
|
+
interface Fix {
|
|
109
|
+
/** Byte range in the original file content [startOffset, endOffset) */
|
|
110
|
+
range: [number, number];
|
|
111
|
+
/** Replacement text (empty string = delete) */
|
|
112
|
+
text: string;
|
|
113
|
+
}
|
|
108
114
|
interface Rule {
|
|
109
115
|
id: string;
|
|
110
116
|
category: RuleCategory;
|
|
@@ -112,6 +118,10 @@ interface Rule {
|
|
|
112
118
|
title: string;
|
|
113
119
|
description: string;
|
|
114
120
|
check: (context: RuleContext) => Issue[];
|
|
121
|
+
/** Whether this rule supports auto-fix */
|
|
122
|
+
fixable?: boolean;
|
|
123
|
+
/** Generate a fix for a given issue. Returns null if unfixable. */
|
|
124
|
+
fix?: (context: RuleContext, issue: Issue) => Fix | null;
|
|
115
125
|
}
|
|
116
126
|
interface RuleContext {
|
|
117
127
|
filePath: string;
|
package/dist/index.js
CHANGED
|
@@ -524,6 +524,24 @@ function detectImmediateReassign(context, lines, issues) {
|
|
|
524
524
|
}
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
// src/rules/fix-utils.ts
|
|
528
|
+
function lineStartOffset(content, lineNumber) {
|
|
529
|
+
let offset = 0;
|
|
530
|
+
const lines = content.split("\n");
|
|
531
|
+
for (let i = 0; i < lineNumber - 1 && i < lines.length; i++) {
|
|
532
|
+
offset += lines[i].length + 1;
|
|
533
|
+
}
|
|
534
|
+
return offset;
|
|
535
|
+
}
|
|
536
|
+
function lineRange(content, lineNumber) {
|
|
537
|
+
const lines = content.split("\n");
|
|
538
|
+
const lineIndex = lineNumber - 1;
|
|
539
|
+
if (lineIndex < 0 || lineIndex >= lines.length) return [0, 0];
|
|
540
|
+
const start = lineStartOffset(content, lineNumber);
|
|
541
|
+
const end = start + lines[lineIndex].length + (lineIndex < lines.length - 1 ? 1 : 0);
|
|
542
|
+
return [start, end];
|
|
543
|
+
}
|
|
544
|
+
|
|
527
545
|
// src/parsers/ast.ts
|
|
528
546
|
import { parse, AST_NODE_TYPES } from "@typescript-eslint/typescript-estree";
|
|
529
547
|
|
|
@@ -706,6 +724,19 @@ var unusedVariablesRule = {
|
|
|
706
724
|
severity: "low",
|
|
707
725
|
title: "Unused variable detected",
|
|
708
726
|
description: "AI-generated code sometimes declares variables that are never used, indicating incomplete or hallucinated logic.",
|
|
727
|
+
fixable: true,
|
|
728
|
+
fix(context, issue) {
|
|
729
|
+
const lines = context.fileContent.split("\n");
|
|
730
|
+
const lineIndex = issue.startLine - 1;
|
|
731
|
+
if (lineIndex < 0 || lineIndex >= lines.length) return null;
|
|
732
|
+
const line = lines[lineIndex].trim();
|
|
733
|
+
if (/^(const|let|var)\s+\w+\s*[=:;]/.test(line) && !line.includes(",")) {
|
|
734
|
+
const [start, end] = lineRange(context.fileContent, issue.startLine);
|
|
735
|
+
if (start === end) return null;
|
|
736
|
+
return { range: [start, end], text: "" };
|
|
737
|
+
}
|
|
738
|
+
return null;
|
|
739
|
+
},
|
|
709
740
|
check(context) {
|
|
710
741
|
const issues = [];
|
|
711
742
|
let ast;
|
|
@@ -1429,6 +1460,23 @@ var unusedImportRule = {
|
|
|
1429
1460
|
severity: "low",
|
|
1430
1461
|
title: "Unused import",
|
|
1431
1462
|
description: "AI-generated code often imports modules or identifiers that are never used in the file.",
|
|
1463
|
+
fixable: true,
|
|
1464
|
+
fix(context, issue) {
|
|
1465
|
+
const lines = context.fileContent.split("\n");
|
|
1466
|
+
const lineIndex = issue.startLine - 1;
|
|
1467
|
+
if (lineIndex < 0 || lineIndex >= lines.length) return null;
|
|
1468
|
+
const line = lines[lineIndex].trim();
|
|
1469
|
+
const isSingleDefault = /^import\s+\w+\s+from\s+/.test(line);
|
|
1470
|
+
const isSingleNamed = /^import\s*\{\s*\w+\s*\}\s*from\s+/.test(line);
|
|
1471
|
+
const isSingleTypeNamed = /^import\s+type\s*\{\s*\w+\s*\}\s*from\s+/.test(line);
|
|
1472
|
+
const isSingleTypeDefault = /^import\s+type\s+\w+\s+from\s+/.test(line);
|
|
1473
|
+
if (!isSingleDefault && !isSingleNamed && !isSingleTypeNamed && !isSingleTypeDefault) {
|
|
1474
|
+
return null;
|
|
1475
|
+
}
|
|
1476
|
+
const [start, end] = lineRange(context.fileContent, issue.startLine);
|
|
1477
|
+
if (start === end) return null;
|
|
1478
|
+
return { range: [start, end], text: "" };
|
|
1479
|
+
},
|
|
1432
1480
|
check(context) {
|
|
1433
1481
|
const issues = [];
|
|
1434
1482
|
let ast;
|
|
@@ -1666,6 +1714,33 @@ var typeCoercionRule = {
|
|
|
1666
1714
|
severity: "medium",
|
|
1667
1715
|
title: "Loose equality with type coercion",
|
|
1668
1716
|
description: "AI-generated code often uses == instead of ===, leading to implicit type coercion bugs.",
|
|
1717
|
+
fixable: true,
|
|
1718
|
+
fix(context, issue) {
|
|
1719
|
+
const lines = context.fileContent.split("\n");
|
|
1720
|
+
const lineIndex = issue.startLine - 1;
|
|
1721
|
+
if (lineIndex < 0 || lineIndex >= lines.length) return null;
|
|
1722
|
+
const line = lines[lineIndex];
|
|
1723
|
+
const base = lineStartOffset(context.fileContent, issue.startLine);
|
|
1724
|
+
const isNotEqual = issue.message.includes("!=");
|
|
1725
|
+
const searchOp = isNotEqual ? "!=" : "==";
|
|
1726
|
+
const replaceOp = isNotEqual ? "!==" : "===";
|
|
1727
|
+
let pos = -1;
|
|
1728
|
+
for (let j = 0; j < line.length - 1; j++) {
|
|
1729
|
+
if (line[j] === searchOp[0] && line[j + 1] === "=") {
|
|
1730
|
+
if (line[j + 2] === "=") {
|
|
1731
|
+
j += 2;
|
|
1732
|
+
continue;
|
|
1733
|
+
}
|
|
1734
|
+
if (!isNotEqual && j > 0 && (line[j - 1] === "!" || line[j - 1] === "<" || line[j - 1] === ">")) {
|
|
1735
|
+
continue;
|
|
1736
|
+
}
|
|
1737
|
+
pos = j;
|
|
1738
|
+
break;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
if (pos === -1) return null;
|
|
1742
|
+
return { range: [base + pos, base + pos + searchOp.length], text: replaceOp };
|
|
1743
|
+
},
|
|
1669
1744
|
check(context) {
|
|
1670
1745
|
const issues = [];
|
|
1671
1746
|
const lines = context.fileContent.split("\n");
|
|
@@ -1920,6 +1995,12 @@ var noDebuggerRule = {
|
|
|
1920
1995
|
severity: "high",
|
|
1921
1996
|
title: "Debugger statement",
|
|
1922
1997
|
description: "Debugger statements should never be committed to production code.",
|
|
1998
|
+
fixable: true,
|
|
1999
|
+
fix(context, issue) {
|
|
2000
|
+
const [start, end] = lineRange(context.fileContent, issue.startLine);
|
|
2001
|
+
if (start === end) return null;
|
|
2002
|
+
return { range: [start, end], text: "" };
|
|
2003
|
+
},
|
|
1923
2004
|
check(context) {
|
|
1924
2005
|
const issues = [];
|
|
1925
2006
|
const lines = context.fileContent.split("\n");
|