circle-ir 3.8.3 → 3.9.7
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 +82 -5
- package/dist/analysis/config-loader.js +1 -1
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/dfg-verifier.d.ts +3 -14
- package/dist/analysis/dfg-verifier.js +43 -74
- package/dist/analysis/dfg-verifier.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts +5 -1
- package/dist/analysis/interprocedural.js +62 -60
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/metrics/index.d.ts +2 -0
- package/dist/analysis/metrics/index.js +2 -0
- package/dist/analysis/metrics/index.js.map +1 -0
- package/dist/analysis/metrics/metric-pass.d.ts +27 -0
- package/dist/analysis/metrics/metric-pass.js +2 -0
- package/dist/analysis/metrics/metric-pass.js.map +1 -0
- package/dist/analysis/metrics/metric-runner.d.ts +21 -0
- package/dist/analysis/metrics/metric-runner.js +47 -0
- package/dist/analysis/metrics/metric-runner.js.map +1 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.d.ts +21 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.js +100 -0
- package/dist/analysis/metrics/passes/cohesion-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.d.ts +15 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.js +76 -0
- package/dist/analysis/metrics/passes/complexity-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.d.ts +17 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.js +77 -0
- package/dist/analysis/metrics/passes/composite-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.d.ts +19 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.js +94 -0
- package/dist/analysis/metrics/passes/coupling-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.d.ts +14 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.js +25 -0
- package/dist/analysis/metrics/passes/data-flow-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.d.ts +15 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.js +64 -0
- package/dist/analysis/metrics/passes/documentation-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.d.ts +16 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.js +95 -0
- package/dist/analysis/metrics/passes/halstead-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.d.ts +18 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.js +73 -0
- package/dist/analysis/metrics/passes/inheritance-metrics-pass.js.map +1 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.d.ts +11 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.js +64 -0
- package/dist/analysis/metrics/passes/size-metrics-pass.js.map +1 -0
- package/dist/analysis/passes/circular-dependency-pass.d.ts +18 -0
- package/dist/analysis/passes/circular-dependency-pass.js +39 -0
- package/dist/analysis/passes/circular-dependency-pass.js.map +1 -0
- package/dist/analysis/passes/constant-propagation-pass.d.ts +22 -0
- package/dist/analysis/passes/constant-propagation-pass.js +44 -0
- package/dist/analysis/passes/constant-propagation-pass.js.map +1 -0
- package/dist/analysis/passes/cross-file-pass.d.ts +27 -0
- package/dist/analysis/passes/cross-file-pass.js +102 -0
- package/dist/analysis/passes/cross-file-pass.js.map +1 -0
- package/dist/analysis/passes/dead-code-pass.d.ts +25 -0
- package/dist/analysis/passes/dead-code-pass.js +117 -0
- package/dist/analysis/passes/dead-code-pass.js.map +1 -0
- package/dist/analysis/passes/dependency-fan-out-pass.d.ts +19 -0
- package/dist/analysis/passes/dependency-fan-out-pass.js +35 -0
- package/dist/analysis/passes/dependency-fan-out-pass.js.map +1 -0
- package/dist/analysis/passes/interprocedural-pass.d.ts +29 -0
- package/dist/analysis/passes/interprocedural-pass.js +169 -0
- package/dist/analysis/passes/interprocedural-pass.js.map +1 -0
- package/dist/analysis/passes/language-sources-pass.d.ts +76 -0
- package/dist/analysis/passes/language-sources-pass.js +491 -0
- package/dist/analysis/passes/language-sources-pass.js.map +1 -0
- package/dist/analysis/passes/leaked-global-pass.d.ts +34 -0
- package/dist/analysis/passes/leaked-global-pass.js +108 -0
- package/dist/analysis/passes/leaked-global-pass.js.map +1 -0
- package/dist/analysis/passes/missing-await-pass.d.ts +29 -0
- package/dist/analysis/passes/missing-await-pass.js +90 -0
- package/dist/analysis/passes/missing-await-pass.js.map +1 -0
- package/dist/analysis/passes/missing-public-doc-pass.d.ts +35 -0
- package/dist/analysis/passes/missing-public-doc-pass.js +148 -0
- package/dist/analysis/passes/missing-public-doc-pass.js.map +1 -0
- package/dist/analysis/passes/n-plus-one-pass.d.ts +29 -0
- package/dist/analysis/passes/n-plus-one-pass.js +100 -0
- package/dist/analysis/passes/n-plus-one-pass.js.map +1 -0
- package/dist/analysis/passes/null-deref-pass.d.ts +32 -0
- package/dist/analysis/passes/null-deref-pass.js +130 -0
- package/dist/analysis/passes/null-deref-pass.js.map +1 -0
- package/dist/analysis/passes/orphan-module-pass.d.ts +21 -0
- package/dist/analysis/passes/orphan-module-pass.js +38 -0
- package/dist/analysis/passes/orphan-module-pass.js.map +1 -0
- package/dist/analysis/passes/resource-leak-pass.d.ts +43 -0
- package/dist/analysis/passes/resource-leak-pass.js +156 -0
- package/dist/analysis/passes/resource-leak-pass.js.map +1 -0
- package/dist/analysis/passes/sink-filter-pass.d.ts +39 -0
- package/dist/analysis/passes/sink-filter-pass.js +231 -0
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -0
- package/dist/analysis/passes/stale-doc-ref-pass.d.ts +21 -0
- package/dist/analysis/passes/stale-doc-ref-pass.js +96 -0
- package/dist/analysis/passes/stale-doc-ref-pass.js.map +1 -0
- package/dist/analysis/passes/string-concat-loop-pass.d.ts +26 -0
- package/dist/analysis/passes/string-concat-loop-pass.js +87 -0
- package/dist/analysis/passes/string-concat-loop-pass.js.map +1 -0
- package/dist/analysis/passes/sync-io-async-pass.d.ts +28 -0
- package/dist/analysis/passes/sync-io-async-pass.js +80 -0
- package/dist/analysis/passes/sync-io-async-pass.js.map +1 -0
- package/dist/analysis/passes/taint-matcher-pass.d.ts +24 -0
- package/dist/analysis/passes/taint-matcher-pass.js +71 -0
- package/dist/analysis/passes/taint-matcher-pass.js.map +1 -0
- package/dist/analysis/passes/taint-propagation-pass.d.ts +22 -0
- package/dist/analysis/passes/taint-propagation-pass.js +266 -0
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -0
- package/dist/analysis/passes/todo-in-prod-pass.d.ts +28 -0
- package/dist/analysis/passes/todo-in-prod-pass.js +71 -0
- package/dist/analysis/passes/todo-in-prod-pass.js.map +1 -0
- package/dist/analysis/passes/unchecked-return-pass.d.ts +34 -0
- package/dist/analysis/passes/unchecked-return-pass.js +106 -0
- package/dist/analysis/passes/unchecked-return-pass.js.map +1 -0
- package/dist/analysis/passes/unused-variable-pass.d.ts +36 -0
- package/dist/analysis/passes/unused-variable-pass.js +150 -0
- package/dist/analysis/passes/unused-variable-pass.js.map +1 -0
- package/dist/analysis/passes/variable-shadowing-pass.d.ts +41 -0
- package/dist/analysis/passes/variable-shadowing-pass.js +211 -0
- package/dist/analysis/passes/variable-shadowing-pass.js.map +1 -0
- package/dist/analysis/path-finder.d.ts +3 -13
- package/dist/analysis/path-finder.js +48 -63
- package/dist/analysis/path-finder.js.map +1 -1
- package/dist/analysis/taint-matcher.js +8 -1
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analysis/taint-propagation.d.ts +5 -1
- package/dist/analysis/taint-propagation.js +44 -41
- package/dist/analysis/taint-propagation.js.map +1 -1
- package/dist/analyzer.d.ts +42 -1
- package/dist/analyzer.js +234 -1476
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +3414 -1272
- package/dist/core/circle-ir-core.cjs +361 -107
- package/dist/core/circle-ir-core.js +361 -107
- package/dist/core/extractors/imports.js +18 -0
- package/dist/core/extractors/imports.js.map +1 -1
- package/dist/graph/analysis-pass.d.ts +68 -0
- package/dist/graph/analysis-pass.js +51 -0
- package/dist/graph/analysis-pass.js.map +1 -0
- package/dist/graph/code-graph.d.ts +92 -0
- package/dist/graph/code-graph.js +262 -0
- package/dist/graph/code-graph.js.map +1 -0
- package/dist/graph/import-graph.d.ts +33 -0
- package/dist/graph/import-graph.js +170 -0
- package/dist/graph/import-graph.js.map +1 -0
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.js +5 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/project-graph.d.ts +43 -0
- package/dist/graph/project-graph.js +80 -0
- package/dist/graph/project-graph.js.map +1 -0
- package/dist/graph/scope-graph.d.ts +63 -0
- package/dist/graph/scope-graph.js +89 -0
- package/dist/graph/scope-graph.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resolution/cross-file.js +52 -19
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/types/index.d.ts +151 -0
- package/docs/SPEC.md +10 -6
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# circle-ir
|
|
2
2
|
|
|
3
|
-
A high-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis. Works in Node.js and browsers.
|
|
3
|
+
A high-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis, and code quality findings through an extensible analysis-pass pipeline. Works in Node.js and browsers.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Taint Analysis**: Track data flow from sources (user input) to sinks (dangerous operations)
|
|
8
8
|
- **Multi-language Support**: Java, JavaScript/TypeScript, Python, Rust, Bash/Shell
|
|
9
9
|
- **High Accuracy**: 100% on OWASP Benchmark, 100% on Juliet Test Suite, 97.7% TPR on SecuriBench Micro
|
|
10
|
+
- **11-Pass Pipeline**: Security taint passes + quality passes (dead code, missing await, N+1, doc coverage, TODO markers)
|
|
11
|
+
- **Cross-File Analysis**: `analyzeProject()` surfaces taint flows that span multiple files
|
|
10
12
|
- **Universal**: Works in Node.js and browsers with environment-agnostic core
|
|
11
13
|
- **Zero External Dependencies**: Core analysis runs without network calls or external services
|
|
12
14
|
- **Browser Compatible**: Tree-sitter WASM for universal parsing
|
|
@@ -31,12 +33,19 @@ await initAnalyzer();
|
|
|
31
33
|
// Analyze Java code
|
|
32
34
|
const result = await analyze(code, 'MyClass.java', 'java');
|
|
33
35
|
|
|
34
|
-
//
|
|
36
|
+
// Security taint flows
|
|
35
37
|
for (const flow of result.taint.flows || []) {
|
|
36
38
|
console.log(`Found ${flow.sink_type} vulnerability`);
|
|
37
39
|
console.log(` Source: line ${flow.source_line}`);
|
|
38
40
|
console.log(` Sink: line ${flow.sink_line}`);
|
|
39
41
|
}
|
|
42
|
+
|
|
43
|
+
// Quality findings from analysis passes (dead-code, missing-await, n-plus-one, etc.)
|
|
44
|
+
for (const finding of result.findings || []) {
|
|
45
|
+
console.log(`[${finding.severity}] ${finding.rule_id} at line ${finding.line}`);
|
|
46
|
+
console.log(` ${finding.message}`);
|
|
47
|
+
if (finding.fix) console.log(` Fix: ${finding.fix}`);
|
|
48
|
+
}
|
|
40
49
|
```
|
|
41
50
|
|
|
42
51
|
### Browser
|
|
@@ -78,7 +87,7 @@ interface AnalyzerOptions {
|
|
|
78
87
|
|
|
79
88
|
### `analyze(code, filePath, language, options?)`
|
|
80
89
|
|
|
81
|
-
Analyze
|
|
90
|
+
Analyze a single file and return Circle-IR output.
|
|
82
91
|
|
|
83
92
|
```typescript
|
|
84
93
|
const result = await analyze(code, 'File.java', 'java');
|
|
@@ -92,6 +101,38 @@ result.dfg // Data flow graph
|
|
|
92
101
|
result.taint // Taint sources, sinks, flows
|
|
93
102
|
result.imports // Import statements
|
|
94
103
|
result.exports // Exported symbols
|
|
104
|
+
result.findings // SastFinding[] from all 11 analysis passes
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### `analyzeProject(files, options?)`
|
|
108
|
+
|
|
109
|
+
Analyze multiple files together to detect cross-file taint flows.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { analyzeProject } from 'circle-ir';
|
|
113
|
+
|
|
114
|
+
const result = await analyzeProject([
|
|
115
|
+
{ code: controllerCode, filePath: 'UserController.java', language: 'java' },
|
|
116
|
+
{ code: serviceCode, filePath: 'UserService.java', language: 'java' },
|
|
117
|
+
{ code: daoCode, filePath: 'UserDao.java', language: 'java' },
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
// Per-file analysis (same as analyze() per file)
|
|
121
|
+
for (const { file, analysis } of result.files) {
|
|
122
|
+
console.log(`${file}: ${analysis.taint.flows?.length ?? 0} intra-file flows`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Cross-file taint paths (the key deliverable)
|
|
126
|
+
for (const path of result.taint_paths) {
|
|
127
|
+
console.log(`Cross-file ${path.sink.type}: ${path.source.file} → ${path.sink.file}`);
|
|
128
|
+
console.log(` Confidence: ${path.confidence.toFixed(2)}, CWE: ${path.sink.cwe}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Resolved inter-file method calls
|
|
132
|
+
console.log(`${result.cross_file_calls.length} cross-file calls resolved`);
|
|
133
|
+
|
|
134
|
+
// Project metadata
|
|
135
|
+
console.log(`${result.meta.total_files} files, ${result.meta.total_loc} LOC`);
|
|
95
136
|
```
|
|
96
137
|
|
|
97
138
|
### `analyzeForAPI(code, filePath, language, options?)`
|
|
@@ -169,6 +210,42 @@ sources:
|
|
|
169
210
|
tainted_args: [return]
|
|
170
211
|
```
|
|
171
212
|
|
|
213
|
+
## SAST Findings & Quality Passes
|
|
214
|
+
|
|
215
|
+
The 11-pass pipeline emits `SastFinding[]` via `result.findings`. Each finding is SARIF 2.1.0-aligned:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
interface SastFinding {
|
|
219
|
+
id: string; // e.g. "dead-code-42"
|
|
220
|
+
rule_id: string; // e.g. "dead-code"
|
|
221
|
+
category: PassCategory; // 'security' | 'reliability' | 'performance' | 'maintainability' | 'architecture'
|
|
222
|
+
severity: string; // 'critical' | 'high' | 'medium' | 'low'
|
|
223
|
+
level: SarifLevel; // 'error' | 'warning' | 'note' | 'none'
|
|
224
|
+
message: string;
|
|
225
|
+
file: string;
|
|
226
|
+
line: number;
|
|
227
|
+
cwe?: string; // e.g. "CWE-561"
|
|
228
|
+
fix?: string; // Instance-specific remediation hint
|
|
229
|
+
evidence?: Record<string, unknown>;
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Current passes** (see [docs/PASSES.md](docs/PASSES.md) for the full registry):
|
|
234
|
+
|
|
235
|
+
| Pass | rule_id | Category | CWE | Level |
|
|
236
|
+
|------|---------|----------|-----|-------|
|
|
237
|
+
| TaintMatcherPass | _(produces flows)_ | security | — | error |
|
|
238
|
+
| ConstantPropagationPass | _(reduces FP)_ | security | — | — |
|
|
239
|
+
| LanguageSourcesPass | _(enriches sources)_ | security | — | — |
|
|
240
|
+
| SinkFilterPass | _(filters sinks)_ | security | — | — |
|
|
241
|
+
| TaintPropagationPass | _(propagates taint)_ | security | — | error |
|
|
242
|
+
| InterproceduralPass | _(cross-method)_ | security | — | error |
|
|
243
|
+
| DeadCodePass | `dead-code` | reliability | CWE-561 | warning |
|
|
244
|
+
| MissingAwaitPass | `missing-await` | reliability | CWE-252 | warning |
|
|
245
|
+
| NPlusOnePass | `n-plus-one` | performance | CWE-1049 | warning |
|
|
246
|
+
| MissingPublicDocPass | `missing-public-doc` | maintainability | — | note |
|
|
247
|
+
| TodoInProdPass | `todo-in-prod` | maintainability | — | note |
|
|
248
|
+
|
|
172
249
|
## Key Analysis Features
|
|
173
250
|
|
|
174
251
|
- **Constant Propagation**: Eliminates false positives by tracking variable values and detecting dead code
|
|
@@ -191,11 +268,11 @@ All scores below are for **circle-ir static analysis only** (no LLM).
|
|
|
191
268
|
|
|
192
269
|
## Documentation
|
|
193
270
|
|
|
271
|
+
- [Pass & Metric Registry](docs/PASSES.md) - Canonical list of every pass and metric with rule_id, CWE, and status
|
|
194
272
|
- [Circle-IR Specification](docs/SPEC.md) - IR format specification
|
|
195
273
|
- [Architecture Guide](docs/ARCHITECTURE.md) - Detailed system architecture
|
|
196
|
-
- [Contributing Guide](CONTRIBUTING.md) - How to contribute
|
|
197
274
|
- [Changelog](CHANGELOG.md) - Version history
|
|
198
|
-
- [TODO](TODO.md) -
|
|
275
|
+
- [TODO](TODO.md) - Phase-based roadmap
|
|
199
276
|
|
|
200
277
|
## License
|
|
201
278
|
|
|
@@ -851,7 +851,7 @@ export const DEFAULT_SINKS = [
|
|
|
851
851
|
// Jenkins/CI Pipeline execution
|
|
852
852
|
{ method: 'executeScript', type: 'code_injection', cwe: 'CWE-94', severity: 'critical', arg_positions: [0] },
|
|
853
853
|
{ method: 'runScript', type: 'code_injection', cwe: 'CWE-94', severity: 'critical', arg_positions: [0] },
|
|
854
|
-
{ method: 'evaluate', type: 'code_injection', cwe: 'CWE-94', severity: 'critical', arg_positions: [0] },
|
|
854
|
+
{ method: 'evaluate', class: 'ScriptEngine', type: 'code_injection', cwe: 'CWE-94', severity: 'critical', arg_positions: [0] },
|
|
855
855
|
{ method: 'execute', class: 'Script', type: 'code_injection', cwe: 'CWE-94', severity: 'critical', arg_positions: [] },
|
|
856
856
|
{ method: 'run', class: 'Script', type: 'code_injection', cwe: 'CWE-94', severity: 'critical', arg_positions: [] },
|
|
857
857
|
{ method: 'checkout', class: 'SCM', type: 'code_injection', cwe: 'CWE-94', severity: 'high', arg_positions: [0] },
|