circle-ir 3.12.1 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/passes/blocking-main-thread-pass.d.ts +40 -0
- package/dist/analysis/passes/blocking-main-thread-pass.js +112 -0
- package/dist/analysis/passes/blocking-main-thread-pass.js.map +1 -0
- package/dist/analysis/passes/excessive-allocation-pass.d.ts +29 -0
- package/dist/analysis/passes/excessive-allocation-pass.js +85 -0
- package/dist/analysis/passes/excessive-allocation-pass.js.map +1 -0
- package/dist/analysis/passes/feature-envy-pass.d.ts +54 -0
- package/dist/analysis/passes/feature-envy-pass.js +132 -0
- package/dist/analysis/passes/feature-envy-pass.js.map +1 -0
- package/dist/analysis/passes/god-class-pass.d.ts +58 -0
- package/dist/analysis/passes/god-class-pass.js +197 -0
- package/dist/analysis/passes/god-class-pass.js.map +1 -0
- package/dist/analysis/passes/missing-guard-dom-pass.d.ts +18 -0
- package/dist/analysis/passes/missing-guard-dom-pass.js +18 -0
- package/dist/analysis/passes/missing-guard-dom-pass.js.map +1 -1
- package/dist/analysis/passes/missing-stream-pass.d.ts +28 -0
- package/dist/analysis/passes/missing-stream-pass.js +173 -0
- package/dist/analysis/passes/missing-stream-pass.js.map +1 -0
- package/dist/analysis/passes/n-plus-one-pass.js +18 -3
- package/dist/analysis/passes/n-plus-one-pass.js.map +1 -1
- package/dist/analysis/passes/naming-convention-pass.d.ts +62 -0
- package/dist/analysis/passes/naming-convention-pass.js +169 -0
- package/dist/analysis/passes/naming-convention-pass.js.map +1 -0
- package/dist/analysis/passes/null-deref-pass.js +17 -1
- package/dist/analysis/passes/null-deref-pass.js.map +1 -1
- package/dist/analysis/passes/serial-await-pass.js +3 -2
- package/dist/analysis/passes/serial-await-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +70 -8
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analyzer.d.ts +28 -12
- package/dist/analyzer.js +30 -14
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +690 -101
- package/dist/index.d.ts +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass #86: god-class (CWE-1060, category: architecture)
|
|
3
|
+
*
|
|
4
|
+
* Detects "God Class" anti-pattern: a class that does too much, is poorly
|
|
5
|
+
* cohesive, and is heavily coupled to external types. These classes are hard
|
|
6
|
+
* to test, maintain, and evolve.
|
|
7
|
+
*
|
|
8
|
+
* Three CK metrics are computed inline (the metrics pipeline runs separately
|
|
9
|
+
* and its results are not available here):
|
|
10
|
+
*
|
|
11
|
+
* WMC — Weighted Methods per Class: Σ v(G) per method, where v(G) is
|
|
12
|
+
* McCabe cyclomatic complexity = CFG edges − nodes + 2.
|
|
13
|
+
* Fallback = 1 per method when CFG data is absent.
|
|
14
|
+
*
|
|
15
|
+
* LCOM2 — Lack of Cohesion of Methods (0–1 scale):
|
|
16
|
+
* (P − Q) / max(1, m*(m−1)/2)
|
|
17
|
+
* P = method pairs sharing no fields, Q = pairs sharing ≥1 field.
|
|
18
|
+
* Field access is inferred from DFG defs/uses whose variable name
|
|
19
|
+
* matches a declared field name (name-match heuristic).
|
|
20
|
+
*
|
|
21
|
+
* CBO — Coupling Between Objects: count of distinct external type names
|
|
22
|
+
* referenced in parameter types, field types, or call receiver_type
|
|
23
|
+
* within the class, excluding primitives and same-class references.
|
|
24
|
+
*
|
|
25
|
+
* A finding is emitted when at least 2 of the 3 thresholds are exceeded:
|
|
26
|
+
* WMC > 47 (SonarQube default)
|
|
27
|
+
* LCOM2 > 0.8
|
|
28
|
+
* CBO > 14 (SATD threshold)
|
|
29
|
+
*
|
|
30
|
+
* Languages: Java, TypeScript, Python. Bash / Rust — skipped.
|
|
31
|
+
*/
|
|
32
|
+
const WMC_THRESHOLD = 47;
|
|
33
|
+
const LCOM2_THRESHOLD = 0.8;
|
|
34
|
+
const CBO_THRESHOLD = 14;
|
|
35
|
+
/** Java / TypeScript primitive type names to exclude from CBO. */
|
|
36
|
+
const PRIMITIVES = new Set([
|
|
37
|
+
'void', 'boolean', 'byte', 'short', 'int', 'long', 'float', 'double', 'char',
|
|
38
|
+
'string', 'number', 'boolean', 'object', 'any', 'never', 'unknown',
|
|
39
|
+
'String', 'Integer', 'Long', 'Double', 'Boolean', 'Object', 'Number',
|
|
40
|
+
'null', 'undefined',
|
|
41
|
+
]);
|
|
42
|
+
export class GodClassPass {
|
|
43
|
+
name = 'god-class';
|
|
44
|
+
category = 'architecture';
|
|
45
|
+
run(ctx) {
|
|
46
|
+
const { graph, language } = ctx;
|
|
47
|
+
if (language === 'bash' || language === 'rust') {
|
|
48
|
+
return { godClasses: [] };
|
|
49
|
+
}
|
|
50
|
+
const file = graph.ir.meta.file;
|
|
51
|
+
const godClasses = [];
|
|
52
|
+
for (const type of graph.ir.types) {
|
|
53
|
+
if (type.kind !== 'class')
|
|
54
|
+
continue;
|
|
55
|
+
if (type.methods.length < 2)
|
|
56
|
+
continue;
|
|
57
|
+
const wmc = this.computeWMC(graph.ir.cfg.blocks, graph.ir.cfg.edges, type);
|
|
58
|
+
const lcom2 = this.computeLCOM2(graph.ir.dfg, type);
|
|
59
|
+
const cbo = this.computeCBO(graph.ir.calls, type);
|
|
60
|
+
const violations = [
|
|
61
|
+
wmc > WMC_THRESHOLD,
|
|
62
|
+
lcom2 > LCOM2_THRESHOLD,
|
|
63
|
+
cbo > CBO_THRESHOLD,
|
|
64
|
+
].filter(Boolean).length;
|
|
65
|
+
if (violations < 2)
|
|
66
|
+
continue;
|
|
67
|
+
godClasses.push({ className: type.name, line: type.start_line, wmc, lcom2, cbo });
|
|
68
|
+
const lcom2Str = lcom2.toFixed(2);
|
|
69
|
+
ctx.addFinding({
|
|
70
|
+
id: `god-class-${file}-${type.start_line}`,
|
|
71
|
+
pass: this.name,
|
|
72
|
+
category: this.category,
|
|
73
|
+
rule_id: this.name,
|
|
74
|
+
cwe: 'CWE-1060',
|
|
75
|
+
severity: 'medium',
|
|
76
|
+
level: 'warning',
|
|
77
|
+
message: `God class detected: \`${type.name}\` exceeds ${violations}/3 thresholds ` +
|
|
78
|
+
`(WMC=${wmc}, LCOM2=${lcom2Str}, CBO=${cbo})`,
|
|
79
|
+
file,
|
|
80
|
+
line: type.start_line,
|
|
81
|
+
fix: 'Break into focused classes: extract cohesive method groups into separate types. ' +
|
|
82
|
+
'Apply Single Responsibility Principle.',
|
|
83
|
+
evidence: { wmc, lcom2: parseFloat(lcom2Str), cbo },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return { godClasses };
|
|
87
|
+
}
|
|
88
|
+
/** Compute WMC = Σ v(G) for all methods. v(G) = edges − nodes + 2. */
|
|
89
|
+
computeWMC(blocks, edges, type) {
|
|
90
|
+
let wmc = 0;
|
|
91
|
+
for (const method of type.methods) {
|
|
92
|
+
// Find CFG blocks within this method's line range
|
|
93
|
+
const methodBlockIds = new Set(blocks
|
|
94
|
+
.filter(b => b.start_line >= method.start_line && b.end_line <= method.end_line)
|
|
95
|
+
.map(b => b.id));
|
|
96
|
+
if (methodBlockIds.size === 0) {
|
|
97
|
+
// No CFG data — fallback complexity of 1
|
|
98
|
+
wmc += 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
// Count edges between blocks within this method
|
|
102
|
+
const methodEdges = edges.filter(e => methodBlockIds.has(e.from) && methodBlockIds.has(e.to));
|
|
103
|
+
const n = methodBlockIds.size;
|
|
104
|
+
const e = methodEdges.length;
|
|
105
|
+
const vG = Math.max(1, e - n + 2);
|
|
106
|
+
wmc += vG;
|
|
107
|
+
}
|
|
108
|
+
return wmc;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Compute LCOM2 = (P − Q) / max(1, m*(m−1)/2) clamped to [0, 1].
|
|
112
|
+
* Uses DFG variable names intersected with declared field names.
|
|
113
|
+
*/
|
|
114
|
+
computeLCOM2(dfg, type) {
|
|
115
|
+
const m = type.methods.length;
|
|
116
|
+
if (m < 2)
|
|
117
|
+
return 0;
|
|
118
|
+
const fieldNames = new Set(type.fields.map(f => f.name));
|
|
119
|
+
if (fieldNames.size === 0)
|
|
120
|
+
return 0;
|
|
121
|
+
// For each method, collect the set of field names it accesses (def or use)
|
|
122
|
+
const methodFields = type.methods.map(method => {
|
|
123
|
+
const accessed = new Set();
|
|
124
|
+
const start = method.start_line;
|
|
125
|
+
const end = method.end_line;
|
|
126
|
+
for (const def of dfg.defs) {
|
|
127
|
+
if (def.line >= start && def.line <= end && fieldNames.has(def.variable)) {
|
|
128
|
+
accessed.add(def.variable);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const use of dfg.uses) {
|
|
132
|
+
if (use.line >= start && use.line <= end && fieldNames.has(use.variable)) {
|
|
133
|
+
accessed.add(use.variable);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return accessed;
|
|
137
|
+
});
|
|
138
|
+
let P = 0; // pairs with no shared fields
|
|
139
|
+
let Q = 0; // pairs with >= 1 shared field
|
|
140
|
+
for (let i = 0; i < m; i++) {
|
|
141
|
+
for (let j = i + 1; j < m; j++) {
|
|
142
|
+
const shared = [...methodFields[i]].some(f => methodFields[j].has(f));
|
|
143
|
+
if (shared) {
|
|
144
|
+
Q++;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
P++;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const total = (m * (m - 1)) / 2;
|
|
152
|
+
const raw = (P - Q) / Math.max(1, total);
|
|
153
|
+
return Math.min(1, Math.max(0, raw));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Compute CBO = count of distinct external type names referenced in the class.
|
|
157
|
+
* Sources: call receiver_type, method parameter types, field types.
|
|
158
|
+
*/
|
|
159
|
+
computeCBO(calls, type) {
|
|
160
|
+
const externalTypes = new Set();
|
|
161
|
+
const ownName = type.name.toLowerCase();
|
|
162
|
+
const addType = (t) => {
|
|
163
|
+
if (!t)
|
|
164
|
+
return;
|
|
165
|
+
// Strip generic parameters: List<String> → List
|
|
166
|
+
const base = t.replace(/<.*>/, '').replace(/\[\]/g, '').replace(/\?/g, '').trim();
|
|
167
|
+
if (!base)
|
|
168
|
+
return;
|
|
169
|
+
if (PRIMITIVES.has(base))
|
|
170
|
+
return;
|
|
171
|
+
if (base.toLowerCase() === ownName)
|
|
172
|
+
return;
|
|
173
|
+
externalTypes.add(base);
|
|
174
|
+
};
|
|
175
|
+
// Field types
|
|
176
|
+
for (const field of type.fields) {
|
|
177
|
+
addType(field.type);
|
|
178
|
+
}
|
|
179
|
+
// Method parameter types
|
|
180
|
+
for (const method of type.methods) {
|
|
181
|
+
for (const param of method.parameters) {
|
|
182
|
+
addType(param.type);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Call receiver types within this class's line range
|
|
186
|
+
const classStart = type.methods.reduce((mn, m) => Math.min(mn, m.start_line), Infinity);
|
|
187
|
+
const classEnd = type.methods.reduce((mx, m) => Math.max(mx, m.end_line), 0);
|
|
188
|
+
for (const call of calls) {
|
|
189
|
+
const ln = call.location.line;
|
|
190
|
+
if (ln >= classStart && ln <= classEnd) {
|
|
191
|
+
addType(call.receiver_type ?? null);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return externalTypes.size;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=god-class-pass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"god-class-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/god-class-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pass: missing-guard-dom (#53, CWE-285)
|
|
3
3
|
*
|
|
4
|
+
* @deprecated NOT REGISTERED IN THE DEFAULT PIPELINE
|
|
5
|
+
*
|
|
6
|
+
* This pass was removed from the default AnalysisPipeline in v3.14.0 because
|
|
7
|
+
* its hardcoded auth-method list (12 names) produces high-severity false
|
|
8
|
+
* positives in any codebase that uses framework-level authorization (Spring
|
|
9
|
+
* Security annotations, filter chains, middleware) — those guards never appear
|
|
10
|
+
* as intra-method call nodes in the CFG, so every sensitive operation looks
|
|
11
|
+
* unguarded.
|
|
12
|
+
*
|
|
13
|
+
* The raw signals this pass relies on are already present in CircleIR:
|
|
14
|
+
* • ir.calls — all call sites with method_name; filter AUTH_METHODS /
|
|
15
|
+
* SENSITIVE_METHODS to identify candidates.
|
|
16
|
+
* • ir.cfg — full CFG (blocks + edges); build DominatorGraph from it.
|
|
17
|
+
*
|
|
18
|
+
* This file is retained so that circle-ir-ai can reconstruct the dominator
|
|
19
|
+
* analysis on top of LLM-identified auth guards (which correctly handle
|
|
20
|
+
* annotations, middleware, and framework-specific patterns).
|
|
21
|
+
*
|
|
4
22
|
* Detects sensitive operations that are not dominated by an authentication
|
|
5
23
|
* or authorization check on all control-flow paths within the same method.
|
|
6
24
|
*
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pass: missing-guard-dom (#53, CWE-285)
|
|
3
3
|
*
|
|
4
|
+
* @deprecated NOT REGISTERED IN THE DEFAULT PIPELINE
|
|
5
|
+
*
|
|
6
|
+
* This pass was removed from the default AnalysisPipeline in v3.14.0 because
|
|
7
|
+
* its hardcoded auth-method list (12 names) produces high-severity false
|
|
8
|
+
* positives in any codebase that uses framework-level authorization (Spring
|
|
9
|
+
* Security annotations, filter chains, middleware) — those guards never appear
|
|
10
|
+
* as intra-method call nodes in the CFG, so every sensitive operation looks
|
|
11
|
+
* unguarded.
|
|
12
|
+
*
|
|
13
|
+
* The raw signals this pass relies on are already present in CircleIR:
|
|
14
|
+
* • ir.calls — all call sites with method_name; filter AUTH_METHODS /
|
|
15
|
+
* SENSITIVE_METHODS to identify candidates.
|
|
16
|
+
* • ir.cfg — full CFG (blocks + edges); build DominatorGraph from it.
|
|
17
|
+
*
|
|
18
|
+
* This file is retained so that circle-ir-ai can reconstruct the dominator
|
|
19
|
+
* analysis on top of LLM-identified auth guards (which correctly handle
|
|
20
|
+
* annotations, middleware, and framework-specific patterns).
|
|
21
|
+
*
|
|
4
22
|
* Detects sensitive operations that are not dominated by an authentication
|
|
5
23
|
* or authorization check on all control-flow paths within the same method.
|
|
6
24
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"missing-guard-dom-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/missing-guard-dom-pass.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"missing-guard-dom-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/missing-guard-dom-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass #85: missing-stream (category: performance)
|
|
3
|
+
*
|
|
4
|
+
* Detects whole-file / whole-response reads that load the entire payload into
|
|
5
|
+
* memory when a streaming approach would be more memory-efficient.
|
|
6
|
+
*
|
|
7
|
+
* Detection strategy (source-text heuristics):
|
|
8
|
+
* JS/TS — fs.readFile / fs.readFileSync / response.text() / response.json()
|
|
9
|
+
* in a method body that has no adjacent streaming indicator
|
|
10
|
+
* (.pipe / for-await-of / createReadStream).
|
|
11
|
+
* Java — Files.readAllBytes / Files.readAllLines / Files.readString or a
|
|
12
|
+
* new BufferedReader constructor.
|
|
13
|
+
* Python — file_handle.read() (whole-file read).
|
|
14
|
+
*
|
|
15
|
+
* Languages: JavaScript, TypeScript, Java, Python. Bash / Rust — skipped.
|
|
16
|
+
*/
|
|
17
|
+
import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
|
|
18
|
+
export interface MissingStreamResult {
|
|
19
|
+
wholeFileReads: Array<{
|
|
20
|
+
line: number;
|
|
21
|
+
method: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export declare class MissingStreamPass implements AnalysisPass<MissingStreamResult> {
|
|
25
|
+
readonly name = "missing-stream";
|
|
26
|
+
readonly category: "performance";
|
|
27
|
+
run(ctx: PassContext): MissingStreamResult;
|
|
28
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass #85: missing-stream (category: performance)
|
|
3
|
+
*
|
|
4
|
+
* Detects whole-file / whole-response reads that load the entire payload into
|
|
5
|
+
* memory when a streaming approach would be more memory-efficient.
|
|
6
|
+
*
|
|
7
|
+
* Detection strategy (source-text heuristics):
|
|
8
|
+
* JS/TS — fs.readFile / fs.readFileSync / response.text() / response.json()
|
|
9
|
+
* in a method body that has no adjacent streaming indicator
|
|
10
|
+
* (.pipe / for-await-of / createReadStream).
|
|
11
|
+
* Java — Files.readAllBytes / Files.readAllLines / Files.readString or a
|
|
12
|
+
* new BufferedReader constructor.
|
|
13
|
+
* Python — file_handle.read() (whole-file read).
|
|
14
|
+
*
|
|
15
|
+
* Languages: JavaScript, TypeScript, Java, Python. Bash / Rust — skipped.
|
|
16
|
+
*/
|
|
17
|
+
/** JS/TS calls that read an entire file or HTTP response into memory. */
|
|
18
|
+
const JS_WHOLE_LOAD_RE = /\b(?:readFileSync|fs\.readFile\b|response\.text\b|response\.json\b|res\.text\b|res\.json\b|body\.text\b|body\.json\b)\s*\(/;
|
|
19
|
+
/** Indicators that the method already uses streaming in JS/TS. */
|
|
20
|
+
const JS_STREAM_RE = /\.pipe\s*\(|\.on\s*\(\s*['"]data['"]|for\s+await\s*\(|\bcreateReadStream\b|\bstream\b/i;
|
|
21
|
+
/** Java patterns that load an entire file eagerly. */
|
|
22
|
+
const JAVA_WHOLE_READ_RE = /\bFiles\.readAllBytes\s*\(|\bFiles\.readAllLines\s*\(|\bFiles\.readString\s*\(|\bnew\s+BufferedReader\s*\(|\bFileInputStream\b/;
|
|
23
|
+
/** Python: calling .read() with no arguments loads the whole file. */
|
|
24
|
+
const PYTHON_WHOLE_READ_RE = /\.\s*read\s*\(\s*\)/;
|
|
25
|
+
export class MissingStreamPass {
|
|
26
|
+
name = 'missing-stream';
|
|
27
|
+
category = 'performance';
|
|
28
|
+
run(ctx) {
|
|
29
|
+
const { graph, code, language } = ctx;
|
|
30
|
+
if (language === 'bash' || language === 'rust') {
|
|
31
|
+
return { wholeFileReads: [] };
|
|
32
|
+
}
|
|
33
|
+
const file = graph.ir.meta.file;
|
|
34
|
+
const codeLines = code.split('\n');
|
|
35
|
+
const wholeFileReads = [];
|
|
36
|
+
const reported = new Set();
|
|
37
|
+
if (language === 'javascript' || language === 'typescript') {
|
|
38
|
+
// Check per method: only flag if the method body has no streaming indicator
|
|
39
|
+
for (const type of graph.ir.types) {
|
|
40
|
+
for (const method of type.methods) {
|
|
41
|
+
const start = method.start_line;
|
|
42
|
+
const end = method.end_line;
|
|
43
|
+
const methodSrc = codeLines.slice(start - 1, end).join('\n');
|
|
44
|
+
// Skip methods that already use streaming
|
|
45
|
+
if (JS_STREAM_RE.test(methodSrc))
|
|
46
|
+
continue;
|
|
47
|
+
for (let i = start - 1; i < end && i < codeLines.length; i++) {
|
|
48
|
+
const ln = i + 1;
|
|
49
|
+
if (reported.has(ln))
|
|
50
|
+
continue;
|
|
51
|
+
const src = codeLines[i];
|
|
52
|
+
const match = JS_WHOLE_LOAD_RE.exec(src);
|
|
53
|
+
if (!match)
|
|
54
|
+
continue;
|
|
55
|
+
const methodName = match[0].replace(/\s*\(.*/, '').trim();
|
|
56
|
+
wholeFileReads.push({ line: ln, method: methodName });
|
|
57
|
+
reported.add(ln);
|
|
58
|
+
ctx.addFinding({
|
|
59
|
+
id: `missing-stream-${file}-${ln}`,
|
|
60
|
+
pass: this.name,
|
|
61
|
+
category: this.category,
|
|
62
|
+
rule_id: this.name,
|
|
63
|
+
severity: 'low',
|
|
64
|
+
level: 'note',
|
|
65
|
+
message: `\`${methodName}()\` loads the entire file/response into memory. ` +
|
|
66
|
+
`Use a streaming API for large payloads.`,
|
|
67
|
+
file,
|
|
68
|
+
line: ln,
|
|
69
|
+
snippet: src.trim(),
|
|
70
|
+
fix: 'Replace with fs.createReadStream / response.body (async iterator) ' +
|
|
71
|
+
'to process data in chunks',
|
|
72
|
+
evidence: { method: methodName },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Also check top-level code (outside any class/method) in loose JS files
|
|
78
|
+
if (graph.ir.types.length === 0) {
|
|
79
|
+
if (!JS_STREAM_RE.test(code)) {
|
|
80
|
+
for (let i = 0; i < codeLines.length; i++) {
|
|
81
|
+
const ln = i + 1;
|
|
82
|
+
if (reported.has(ln))
|
|
83
|
+
continue;
|
|
84
|
+
const src = codeLines[i];
|
|
85
|
+
const match = JS_WHOLE_LOAD_RE.exec(src);
|
|
86
|
+
if (!match)
|
|
87
|
+
continue;
|
|
88
|
+
const methodName = match[0].replace(/\s*\(.*/, '').trim();
|
|
89
|
+
wholeFileReads.push({ line: ln, method: methodName });
|
|
90
|
+
reported.add(ln);
|
|
91
|
+
ctx.addFinding({
|
|
92
|
+
id: `missing-stream-${file}-${ln}`,
|
|
93
|
+
pass: this.name,
|
|
94
|
+
category: this.category,
|
|
95
|
+
rule_id: this.name,
|
|
96
|
+
severity: 'low',
|
|
97
|
+
level: 'note',
|
|
98
|
+
message: `\`${methodName}()\` loads the entire file/response into memory. ` +
|
|
99
|
+
`Use a streaming API for large payloads.`,
|
|
100
|
+
file,
|
|
101
|
+
line: ln,
|
|
102
|
+
snippet: src.trim(),
|
|
103
|
+
fix: 'Replace with fs.createReadStream / response.body (async iterator) ' +
|
|
104
|
+
'to process data in chunks',
|
|
105
|
+
evidence: { method: methodName },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (language === 'java') {
|
|
112
|
+
for (let i = 0; i < codeLines.length; i++) {
|
|
113
|
+
const ln = i + 1;
|
|
114
|
+
if (reported.has(ln))
|
|
115
|
+
continue;
|
|
116
|
+
const src = codeLines[i];
|
|
117
|
+
const match = JAVA_WHOLE_READ_RE.exec(src);
|
|
118
|
+
if (!match)
|
|
119
|
+
continue;
|
|
120
|
+
const matchText = match[0].replace(/\s*\(.*/, '').trim();
|
|
121
|
+
wholeFileReads.push({ line: ln, method: matchText });
|
|
122
|
+
reported.add(ln);
|
|
123
|
+
ctx.addFinding({
|
|
124
|
+
id: `missing-stream-${file}-${ln}`,
|
|
125
|
+
pass: this.name,
|
|
126
|
+
category: this.category,
|
|
127
|
+
rule_id: this.name,
|
|
128
|
+
severity: 'low',
|
|
129
|
+
level: 'note',
|
|
130
|
+
message: `Whole-file read at line ${ln}: \`${matchText}\` loads the entire file into memory. ` +
|
|
131
|
+
`Consider NIO Channels or InputStream for large files.`,
|
|
132
|
+
file,
|
|
133
|
+
line: ln,
|
|
134
|
+
snippet: src.trim(),
|
|
135
|
+
fix: 'Use Files.lines() for line streaming, or InputStream / NIO channels for byte streaming',
|
|
136
|
+
evidence: { method: matchText },
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (language === 'python') {
|
|
141
|
+
for (let i = 0; i < codeLines.length; i++) {
|
|
142
|
+
const ln = i + 1;
|
|
143
|
+
if (reported.has(ln))
|
|
144
|
+
continue;
|
|
145
|
+
const src = codeLines[i];
|
|
146
|
+
if (!PYTHON_WHOLE_READ_RE.test(src))
|
|
147
|
+
continue;
|
|
148
|
+
// Skip comment lines
|
|
149
|
+
if (/^\s*#/.test(src))
|
|
150
|
+
continue;
|
|
151
|
+
wholeFileReads.push({ line: ln, method: 'read' });
|
|
152
|
+
reported.add(ln);
|
|
153
|
+
ctx.addFinding({
|
|
154
|
+
id: `missing-stream-${file}-${ln}`,
|
|
155
|
+
pass: this.name,
|
|
156
|
+
category: this.category,
|
|
157
|
+
rule_id: this.name,
|
|
158
|
+
severity: 'low',
|
|
159
|
+
level: 'note',
|
|
160
|
+
message: `\`.read()\` loads the entire file into memory. ` +
|
|
161
|
+
`Iterate over the file object instead for line-by-line streaming.`,
|
|
162
|
+
file,
|
|
163
|
+
line: ln,
|
|
164
|
+
snippet: src.trim(),
|
|
165
|
+
fix: "Iterate the file object: `for line in f:` instead of `data = f.read()`",
|
|
166
|
+
evidence: { method: 'read' },
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { wholeFileReads };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=missing-stream-pass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-stream-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/missing-stream-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -46,13 +46,28 @@ const MEDIUM_CONFIDENCE_DB_METHODS = new Set([
|
|
|
46
46
|
'get', 'post', 'put', 'patch', 'request',
|
|
47
47
|
'load', 'lookup',
|
|
48
48
|
]);
|
|
49
|
-
/**
|
|
50
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Receiver patterns that indicate a DB or HTTP client.
|
|
51
|
+
*
|
|
52
|
+
* Two-tier matching:
|
|
53
|
+
* 1. Prefix match: names starting with db, conn, pool, repo, etc.
|
|
54
|
+
* 2. Suffix match: names ending with Repository, Repo, Dao, Service, Client, etc.
|
|
55
|
+
*
|
|
56
|
+
* This catches both `dbConnection.query()` and `userRepository.find()`.
|
|
57
|
+
*/
|
|
58
|
+
const DB_OR_HTTP_RECEIVER_PREFIX = /^(db|conn|connection|pool|client|repo|repository|orm|em|entityManager|sequelize|mongoose|prisma|axios|http|https|api|svc|service|dao|store|cache|gql|graphql|mongo|redis|sql|pg|mysql|sqlite|dynamo|cosmos|elastic|es|solr|neo4j|cassandra|couchbase|firestore|supabase|drizzle|knex|typeorm|mikro)/i;
|
|
59
|
+
const DB_OR_HTTP_RECEIVER_SUFFIX = /(?:Repository|Repo|Dao|DataSource|DbContext|Client|Service|Store|Cache|Gateway|Adapter|Provider|Manager|Handler|Proxy|Facade|Connection|Pool|Session|Template|Mapper|Access|Query|Command|Storage|Bucket|Table|Collection|Index)$/;
|
|
60
|
+
/**
|
|
61
|
+
* Check if a receiver name indicates a DB or HTTP client.
|
|
62
|
+
*/
|
|
63
|
+
function isDbOrHttpReceiver(receiver) {
|
|
64
|
+
return DB_OR_HTTP_RECEIVER_PREFIX.test(receiver) || DB_OR_HTTP_RECEIVER_SUFFIX.test(receiver);
|
|
65
|
+
}
|
|
51
66
|
function isDbOrApiCall(call) {
|
|
52
67
|
if (HIGH_CONFIDENCE_DB_METHODS.has(call.method_name))
|
|
53
68
|
return true;
|
|
54
69
|
if (MEDIUM_CONFIDENCE_DB_METHODS.has(call.method_name)) {
|
|
55
|
-
return call.receiver != null &&
|
|
70
|
+
return call.receiver != null && isDbOrHttpReceiver(call.receiver);
|
|
56
71
|
}
|
|
57
72
|
return false;
|
|
58
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"n-plus-one-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/n-plus-one-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH;;;GAGG;AACH,MAAM,0BAA0B,GAAwB,IAAI,GAAG,CAAC;IAC9D,iBAAiB;IACjB,cAAc,EAAE,eAAe,EAAE,kBAAkB,EAAE,aAAa;IAClE,oBAAoB;IACpB,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY;IAC5D,WAAW;IACX,mBAAmB,EAAE,mBAAmB;IACxC,kBAAkB,EAAE,kBAAkB;IACtC,gBAAgB,EAAE,WAAW,EAAE,UAAU;IACzC,YAAY;IACZ,UAAU,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY;IACzD,SAAS;IACT,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY;IAC/E,UAAU;IACV,OAAO;CACR,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,4BAA4B,GAAwB,IAAI,GAAG,CAAC;IAChE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS;IACrC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ;IAClE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS;IACxC,MAAM,EAAE,QAAQ;CACjB,CAAC,CAAC;AAEH,
|
|
1
|
+
{"version":3,"file":"n-plus-one-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/n-plus-one-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pass #88: naming-convention (category: maintainability)
|
|
3
|
+
*
|
|
4
|
+
* Checks that class, interface, method, and field names follow the
|
|
5
|
+
* established conventions for each language. Violations are purely
|
|
6
|
+
* structural — no corpus statistics or LLM required.
|
|
7
|
+
*
|
|
8
|
+
* Conventions enforced:
|
|
9
|
+
* Java / TypeScript
|
|
10
|
+
* • Class name → PascalCase (`^[A-Z][A-Za-z0-9]*$`)
|
|
11
|
+
* • Interface name → PascalCase; the `I`-prefix anti-pattern is
|
|
12
|
+
* configurable (opt-in, off by default — many codebases use it
|
|
13
|
+
* intentionally; enable via `passOptions.namingConvention.enforceIPrefix`)
|
|
14
|
+
* • Method name → camelCase (`^[a-z][a-zA-Z0-9]*$`)
|
|
15
|
+
* • Constant field → UPPER_SNAKE_CASE (field with final/static/const modifier)
|
|
16
|
+
*
|
|
17
|
+
* Python
|
|
18
|
+
* • Class name → PascalCase (`^[A-Z][A-Za-z0-9]*$`)
|
|
19
|
+
* • Method name → snake_case (`^[a-z_][a-z0-9_]*$`)
|
|
20
|
+
*
|
|
21
|
+
* Bash / Rust
|
|
22
|
+
* • Function name → snake_case (`^[a-z_][a-z0-9_]*$`)
|
|
23
|
+
*
|
|
24
|
+
* Skip rules (to reduce false positives):
|
|
25
|
+
* • Names ≤ 2 characters
|
|
26
|
+
* • Names starting with `_` (private convention)
|
|
27
|
+
* • Names starting with `$` (JS framework injections)
|
|
28
|
+
* • Dunder methods: `__init__`, `__str__`, etc.
|
|
29
|
+
* • Common single-letter generics: T, K, V, E
|
|
30
|
+
* • Java/TS main entry: `main`
|
|
31
|
+
*
|
|
32
|
+
* Capped at 20 findings per file to avoid noise.
|
|
33
|
+
*/
|
|
34
|
+
import type { AnalysisPass, PassContext } from '../../graph/analysis-pass.js';
|
|
35
|
+
/**
|
|
36
|
+
* Per-pass options for NamingConventionPass.
|
|
37
|
+
* Pass via `AnalyzerOptions.passOptions.namingConvention`.
|
|
38
|
+
*/
|
|
39
|
+
export interface NamingConventionOptions {
|
|
40
|
+
/**
|
|
41
|
+
* When true, flag TypeScript/Java interfaces whose names begin with `I`
|
|
42
|
+
* followed by an uppercase letter (e.g. `IUserRepository`).
|
|
43
|
+
* Default: false — the I-prefix is used intentionally in many codebases.
|
|
44
|
+
*/
|
|
45
|
+
enforceIPrefix?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface NamingConventionResult {
|
|
48
|
+
violations: Array<{
|
|
49
|
+
entity: 'class' | 'interface' | 'method' | 'field';
|
|
50
|
+
name: string;
|
|
51
|
+
line: number;
|
|
52
|
+
expected: string;
|
|
53
|
+
actual: string;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
56
|
+
export declare class NamingConventionPass implements AnalysisPass<NamingConventionResult> {
|
|
57
|
+
readonly name = "naming-convention";
|
|
58
|
+
readonly category: "maintainability";
|
|
59
|
+
private readonly enforceIPrefix;
|
|
60
|
+
constructor(options?: NamingConventionOptions);
|
|
61
|
+
run(ctx: PassContext): NamingConventionResult;
|
|
62
|
+
}
|