circle-ir 3.27.1 → 3.28.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/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +270 -253
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +239 -214
- package/dist/core/circle-ir-core.cjs +17 -1
- package/dist/core/circle-ir-core.js +17 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/parser.d.ts +19 -1
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +53 -2
- package/dist/core/parser.js.map +1 -1
- package/package.json +1 -1
|
@@ -3997,6 +3997,7 @@ var parserInitialized = false;
|
|
|
3997
3997
|
var parserInitializing = null;
|
|
3998
3998
|
var loadedLanguages = /* @__PURE__ */ new Map();
|
|
3999
3999
|
var loadingLanguages = /* @__PURE__ */ new Map();
|
|
4000
|
+
var cachedParsers = /* @__PURE__ */ new Map();
|
|
4000
4001
|
var configuredLanguagePaths = {};
|
|
4001
4002
|
var configuredLanguageModules = {};
|
|
4002
4003
|
async function initParser(options = {}) {
|
|
@@ -4066,9 +4067,14 @@ async function loadLanguage(language, wasmPath) {
|
|
|
4066
4067
|
return loadPromise;
|
|
4067
4068
|
}
|
|
4068
4069
|
async function createParser(language) {
|
|
4070
|
+
const cached = cachedParsers.get(language);
|
|
4071
|
+
if (cached) {
|
|
4072
|
+
return cached;
|
|
4073
|
+
}
|
|
4069
4074
|
const lang = await loadLanguage(language);
|
|
4070
4075
|
const parser = new Parser();
|
|
4071
4076
|
parser.setLanguage(lang);
|
|
4077
|
+
cachedParsers.set(language, parser);
|
|
4072
4078
|
return parser;
|
|
4073
4079
|
}
|
|
4074
4080
|
async function parse(code, language) {
|
|
@@ -4079,6 +4085,13 @@ async function parse(code, language) {
|
|
|
4079
4085
|
}
|
|
4080
4086
|
return tree;
|
|
4081
4087
|
}
|
|
4088
|
+
function disposeTree(tree) {
|
|
4089
|
+
if (!tree) return;
|
|
4090
|
+
try {
|
|
4091
|
+
tree.delete();
|
|
4092
|
+
} catch {
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4082
4095
|
function walkTree(node, visitor) {
|
|
4083
4096
|
visitor(node);
|
|
4084
4097
|
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
@@ -26094,161 +26107,169 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
26094
26107
|
}
|
|
26095
26108
|
logger.debug("Analyzing file", { filePath, language, codeLength: code.length });
|
|
26096
26109
|
const tree = await parse(code, language);
|
|
26097
|
-
|
|
26098
|
-
|
|
26099
|
-
|
|
26100
|
-
|
|
26101
|
-
|
|
26102
|
-
|
|
26103
|
-
|
|
26104
|
-
|
|
26105
|
-
|
|
26106
|
-
|
|
26107
|
-
|
|
26108
|
-
|
|
26109
|
-
|
|
26110
|
-
|
|
26111
|
-
|
|
26112
|
-
|
|
26113
|
-
|
|
26114
|
-
|
|
26115
|
-
|
|
26116
|
-
|
|
26117
|
-
|
|
26118
|
-
|
|
26119
|
-
|
|
26120
|
-
|
|
26121
|
-
|
|
26122
|
-
|
|
26123
|
-
|
|
26124
|
-
|
|
26125
|
-
|
|
26126
|
-
|
|
26127
|
-
|
|
26128
|
-
|
|
26129
|
-
|
|
26130
|
-
|
|
26131
|
-
|
|
26132
|
-
|
|
26133
|
-
|
|
26134
|
-
|
|
26135
|
-
|
|
26136
|
-
|
|
26137
|
-
|
|
26138
|
-
|
|
26139
|
-
|
|
26140
|
-
|
|
26141
|
-
|
|
26142
|
-
|
|
26143
|
-
|
|
26144
|
-
|
|
26145
|
-
|
|
26146
|
-
|
|
26147
|
-
|
|
26148
|
-
|
|
26149
|
-
|
|
26150
|
-
|
|
26151
|
-
|
|
26152
|
-
|
|
26153
|
-
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
26158
|
-
|
|
26159
|
-
|
|
26160
|
-
|
|
26161
|
-
|
|
26162
|
-
|
|
26163
|
-
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26175
|
-
|
|
26176
|
-
|
|
26177
|
-
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
26187
|
-
|
|
26188
|
-
|
|
26189
|
-
|
|
26190
|
-
|
|
26191
|
-
|
|
26192
|
-
|
|
26193
|
-
|
|
26194
|
-
|
|
26195
|
-
|
|
26196
|
-
|
|
26197
|
-
|
|
26198
|
-
|
|
26199
|
-
|
|
26200
|
-
|
|
26201
|
-
|
|
26110
|
+
try {
|
|
26111
|
+
logger.trace("Parsed AST", { rootNodeType: tree.rootNode.type });
|
|
26112
|
+
const nodeCache = collectAllNodes(tree.rootNode, getNodeTypesForLanguage(language));
|
|
26113
|
+
const meta = extractMeta(code, tree, filePath, language);
|
|
26114
|
+
const types = extractTypes(tree, nodeCache, language);
|
|
26115
|
+
const calls = extractCalls(tree, nodeCache, language);
|
|
26116
|
+
const imports = extractImports(tree, language);
|
|
26117
|
+
const exports = extractExports(types);
|
|
26118
|
+
const cfg = buildCFG(tree, language);
|
|
26119
|
+
const dfg = buildDFG(tree, nodeCache, language);
|
|
26120
|
+
const graph = new CodeGraph({
|
|
26121
|
+
meta,
|
|
26122
|
+
types,
|
|
26123
|
+
calls,
|
|
26124
|
+
cfg,
|
|
26125
|
+
dfg,
|
|
26126
|
+
taint: { sources: [], sinks: [], sanitizers: [] },
|
|
26127
|
+
imports,
|
|
26128
|
+
exports,
|
|
26129
|
+
unresolved: [],
|
|
26130
|
+
enriched: {}
|
|
26131
|
+
});
|
|
26132
|
+
const config = options.taintConfig ?? getDefaultConfig();
|
|
26133
|
+
const disabledPasses = new Set(options.disabledPasses ?? []);
|
|
26134
|
+
const passOpts = options.passOptions ?? {};
|
|
26135
|
+
const pipeline = new AnalysisPipeline();
|
|
26136
|
+
pipeline.add(new TaintMatcherPass());
|
|
26137
|
+
pipeline.add(new ConstantPropagationPass(tree));
|
|
26138
|
+
pipeline.add(new LanguageSourcesPass());
|
|
26139
|
+
pipeline.add(new SinkFilterPass());
|
|
26140
|
+
pipeline.add(new TaintPropagationPass());
|
|
26141
|
+
pipeline.add(new InterproceduralPass());
|
|
26142
|
+
if (!disabledPasses.has("scan-secrets")) pipeline.add(new ScanSecretsPass());
|
|
26143
|
+
if (!disabledPasses.has("dead-code")) pipeline.add(new DeadCodePass());
|
|
26144
|
+
if (!disabledPasses.has("missing-await")) pipeline.add(new MissingAwaitPass());
|
|
26145
|
+
if (!disabledPasses.has("n-plus-one")) pipeline.add(new NPlusOnePass());
|
|
26146
|
+
if (!disabledPasses.has("missing-public-doc")) pipeline.add(new MissingPublicDocPass());
|
|
26147
|
+
if (!disabledPasses.has("todo-in-prod")) pipeline.add(new TodoInProdPass());
|
|
26148
|
+
if (!disabledPasses.has("string-concat-loop")) pipeline.add(new StringConcatLoopPass());
|
|
26149
|
+
if (!disabledPasses.has("sync-io-async")) pipeline.add(new SyncIoAsyncPass());
|
|
26150
|
+
if (!disabledPasses.has("unchecked-return")) pipeline.add(new UncheckedReturnPass());
|
|
26151
|
+
if (!disabledPasses.has("null-deref")) pipeline.add(new NullDerefPass());
|
|
26152
|
+
if (!disabledPasses.has("resource-leak")) pipeline.add(new ResourceLeakPass());
|
|
26153
|
+
if (!disabledPasses.has("variable-shadowing")) pipeline.add(new VariableShadowingPass());
|
|
26154
|
+
if (!disabledPasses.has("leaked-global")) pipeline.add(new LeakedGlobalPass());
|
|
26155
|
+
if (!disabledPasses.has("unused-variable")) pipeline.add(new UnusedVariablePass());
|
|
26156
|
+
if (!disabledPasses.has("dependency-fan-out")) pipeline.add(new DependencyFanOutPass(passOpts.dependencyFanOut));
|
|
26157
|
+
if (!disabledPasses.has("stale-doc-ref")) pipeline.add(new StaleDocRefPass());
|
|
26158
|
+
if (!disabledPasses.has("infinite-loop")) pipeline.add(new InfiniteLoopPass());
|
|
26159
|
+
if (!disabledPasses.has("deep-inheritance")) pipeline.add(new DeepInheritancePass());
|
|
26160
|
+
if (!disabledPasses.has("redundant-loop-computation")) pipeline.add(new RedundantLoopPass());
|
|
26161
|
+
if (!disabledPasses.has("unbounded-collection")) pipeline.add(new UnboundedCollectionPass(passOpts.unboundedCollection));
|
|
26162
|
+
if (!disabledPasses.has("serial-await")) pipeline.add(new SerialAwaitPass());
|
|
26163
|
+
if (!disabledPasses.has("react-inline-jsx")) pipeline.add(new ReactInlineJsxPass());
|
|
26164
|
+
if (!disabledPasses.has("swallowed-exception")) pipeline.add(new SwallowedExceptionPass());
|
|
26165
|
+
if (!disabledPasses.has("broad-catch")) pipeline.add(new BroadCatchPass());
|
|
26166
|
+
if (!disabledPasses.has("unhandled-exception")) pipeline.add(new UnhandledExceptionPass());
|
|
26167
|
+
if (!disabledPasses.has("double-close")) pipeline.add(new DoubleClosePass());
|
|
26168
|
+
if (!disabledPasses.has("use-after-close")) pipeline.add(new UseAfterClosePass());
|
|
26169
|
+
if (!disabledPasses.has("cleanup-verify")) pipeline.add(new CleanupVerifyPass());
|
|
26170
|
+
if (!disabledPasses.has("missing-override")) pipeline.add(new MissingOverridePass());
|
|
26171
|
+
if (!disabledPasses.has("unused-interface-method")) pipeline.add(new UnusedInterfaceMethodPass());
|
|
26172
|
+
if (!disabledPasses.has("blocking-main-thread")) pipeline.add(new BlockingMainThreadPass());
|
|
26173
|
+
if (!disabledPasses.has("excessive-allocation")) pipeline.add(new ExcessiveAllocationPass());
|
|
26174
|
+
if (!disabledPasses.has("missing-stream")) pipeline.add(new MissingStreamPass());
|
|
26175
|
+
if (!disabledPasses.has("god-class")) pipeline.add(new GodClassPass());
|
|
26176
|
+
if (!disabledPasses.has("naming-convention")) pipeline.add(new NamingConventionPass(passOpts.namingConvention));
|
|
26177
|
+
if (!disabledPasses.has("security-headers")) pipeline.add(new SecurityHeadersPass(passOpts.securityHeaders));
|
|
26178
|
+
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
26179
|
+
const sinkFilter = results.get("sink-filter");
|
|
26180
|
+
const interProc = results.get("interprocedural");
|
|
26181
|
+
const taint = {
|
|
26182
|
+
sources: sinkFilter.sources,
|
|
26183
|
+
sinks: [...sinkFilter.sinks, ...interProc.additionalSinks],
|
|
26184
|
+
sanitizers: sinkFilter.sanitizers,
|
|
26185
|
+
flows: interProc.additionalFlows,
|
|
26186
|
+
interprocedural: interProc.interprocedural
|
|
26187
|
+
};
|
|
26188
|
+
const unresolved = detectUnresolved(calls, types, dfg);
|
|
26189
|
+
const enriched = buildEnriched(types, calls, taint.sources, taint.sinks);
|
|
26190
|
+
const metricValues = new MetricRunner().run(
|
|
26191
|
+
{ meta, types, calls, cfg, dfg, taint, imports, exports, unresolved, enriched },
|
|
26192
|
+
code,
|
|
26193
|
+
language
|
|
26194
|
+
);
|
|
26195
|
+
logger.debug("Analysis complete", {
|
|
26196
|
+
filePath,
|
|
26197
|
+
finalSources: taint.sources.length,
|
|
26198
|
+
finalSinks: taint.sinks.length,
|
|
26199
|
+
flows: taint.flows?.length ?? 0,
|
|
26200
|
+
unresolvedItems: unresolved.length
|
|
26201
|
+
});
|
|
26202
|
+
return {
|
|
26203
|
+
meta,
|
|
26204
|
+
types,
|
|
26205
|
+
calls,
|
|
26206
|
+
cfg,
|
|
26207
|
+
dfg,
|
|
26208
|
+
taint,
|
|
26209
|
+
imports,
|
|
26210
|
+
exports,
|
|
26211
|
+
unresolved,
|
|
26212
|
+
enriched,
|
|
26213
|
+
findings: findings.length > 0 ? findings : void 0,
|
|
26214
|
+
metrics: { file: filePath, metrics: metricValues }
|
|
26215
|
+
};
|
|
26216
|
+
} finally {
|
|
26217
|
+
disposeTree(tree);
|
|
26218
|
+
}
|
|
26202
26219
|
}
|
|
26203
26220
|
async function analyzeHtmlFile(code, filePath, options) {
|
|
26204
26221
|
logger.debug("Analyzing HTML file", { filePath, codeLength: code.length });
|
|
26205
26222
|
const tree = await parse(code, "html");
|
|
26206
|
-
|
|
26207
|
-
|
|
26208
|
-
|
|
26209
|
-
|
|
26210
|
-
|
|
26211
|
-
|
|
26212
|
-
|
|
26213
|
-
|
|
26214
|
-
|
|
26215
|
-
|
|
26216
|
-
|
|
26217
|
-
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
|
|
26221
|
-
|
|
26222
|
-
|
|
26223
|
-
|
|
26224
|
-
|
|
26225
|
-
|
|
26226
|
-
|
|
26223
|
+
try {
|
|
26224
|
+
const meta = extractMeta(code, tree, filePath, "html");
|
|
26225
|
+
const { scriptBlocks, eventHandlers } = extractHtmlContent(tree.rootNode);
|
|
26226
|
+
logger.debug("HTML extraction", {
|
|
26227
|
+
filePath,
|
|
26228
|
+
inlineScripts: scriptBlocks.filter((b) => b.kind === "inline").length,
|
|
26229
|
+
externalScripts: scriptBlocks.filter((b) => b.kind === "external-src").length,
|
|
26230
|
+
eventHandlers: eventHandlers.length
|
|
26231
|
+
});
|
|
26232
|
+
const scriptResults = [];
|
|
26233
|
+
for (const block of scriptBlocks) {
|
|
26234
|
+
if (block.kind !== "inline" || !block.code.trim()) continue;
|
|
26235
|
+
const scriptLang = block.scriptType === "ts" || block.scriptType === "typescript" || block.scriptType === "text/typescript" ? "typescript" : "javascript";
|
|
26236
|
+
try {
|
|
26237
|
+
const ir = await analyze(block.code, filePath, scriptLang, options);
|
|
26238
|
+
scriptResults.push({ ir, lineOffset: block.lineOffset });
|
|
26239
|
+
} catch (e) {
|
|
26240
|
+
logger.warn("Failed to analyze script block", {
|
|
26241
|
+
filePath,
|
|
26242
|
+
lineOffset: block.lineOffset,
|
|
26243
|
+
error: e instanceof Error ? e.message : String(e)
|
|
26244
|
+
});
|
|
26245
|
+
}
|
|
26227
26246
|
}
|
|
26228
|
-
|
|
26229
|
-
|
|
26230
|
-
|
|
26231
|
-
|
|
26232
|
-
|
|
26233
|
-
|
|
26234
|
-
|
|
26235
|
-
|
|
26236
|
-
|
|
26237
|
-
|
|
26238
|
-
|
|
26239
|
-
|
|
26240
|
-
}
|
|
26247
|
+
for (const handler of eventHandlers) {
|
|
26248
|
+
const wrappedCode = `function __${handler.eventName}_handler() { ${handler.code} }`;
|
|
26249
|
+
try {
|
|
26250
|
+
const ir = await analyze(wrappedCode, filePath, "javascript", options);
|
|
26251
|
+
scriptResults.push({ ir, lineOffset: handler.line });
|
|
26252
|
+
} catch (e) {
|
|
26253
|
+
logger.warn("Failed to analyze event handler", {
|
|
26254
|
+
filePath,
|
|
26255
|
+
eventName: handler.eventName,
|
|
26256
|
+
line: handler.line,
|
|
26257
|
+
error: e instanceof Error ? e.message : String(e)
|
|
26258
|
+
});
|
|
26259
|
+
}
|
|
26241
26260
|
}
|
|
26261
|
+
const attributeFindings = runHtmlAttributeSecurityChecks(tree.rootNode, filePath);
|
|
26262
|
+
const result = mergeHtmlResults(meta, scriptResults, attributeFindings);
|
|
26263
|
+
logger.debug("HTML analysis complete", {
|
|
26264
|
+
filePath,
|
|
26265
|
+
scriptBlocks: scriptResults.length,
|
|
26266
|
+
attributeFindings: attributeFindings.length,
|
|
26267
|
+
totalFindings: result.findings?.length ?? 0
|
|
26268
|
+
});
|
|
26269
|
+
return result;
|
|
26270
|
+
} finally {
|
|
26271
|
+
disposeTree(tree);
|
|
26242
26272
|
}
|
|
26243
|
-
const attributeFindings = runHtmlAttributeSecurityChecks(tree.rootNode, filePath);
|
|
26244
|
-
const result = mergeHtmlResults(meta, scriptResults, attributeFindings);
|
|
26245
|
-
logger.debug("HTML analysis complete", {
|
|
26246
|
-
filePath,
|
|
26247
|
-
scriptBlocks: scriptResults.length,
|
|
26248
|
-
attributeFindings: attributeFindings.length,
|
|
26249
|
-
totalFindings: result.findings?.length ?? 0
|
|
26250
|
-
});
|
|
26251
|
-
return result;
|
|
26252
26273
|
}
|
|
26253
26274
|
async function analyzeForAPI(code, filePath, language, options = {}) {
|
|
26254
26275
|
const startTime = performance.now();
|
|
@@ -26258,75 +26279,79 @@ async function analyzeForAPI(code, filePath, language, options = {}) {
|
|
|
26258
26279
|
const parseStart = performance.now();
|
|
26259
26280
|
const tree = await parse(code, language);
|
|
26260
26281
|
const parseTime = performance.now() - parseStart;
|
|
26261
|
-
|
|
26262
|
-
|
|
26263
|
-
|
|
26264
|
-
|
|
26265
|
-
|
|
26266
|
-
|
|
26267
|
-
|
|
26268
|
-
|
|
26269
|
-
|
|
26270
|
-
filteredSinks
|
|
26271
|
-
|
|
26272
|
-
|
|
26273
|
-
|
|
26274
|
-
|
|
26275
|
-
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
|
|
26279
|
-
|
|
26280
|
-
|
|
26281
|
-
|
|
26282
|
-
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
|
|
26286
|
-
|
|
26287
|
-
|
|
26288
|
-
|
|
26289
|
-
|
|
26290
|
-
|
|
26291
|
-
|
|
26292
|
-
|
|
26293
|
-
|
|
26294
|
-
|
|
26295
|
-
|
|
26296
|
-
|
|
26297
|
-
|
|
26298
|
-
|
|
26299
|
-
|
|
26300
|
-
const
|
|
26301
|
-
|
|
26302
|
-
|
|
26303
|
-
|
|
26304
|
-
|
|
26305
|
-
|
|
26306
|
-
|
|
26307
|
-
|
|
26308
|
-
|
|
26309
|
-
|
|
26310
|
-
|
|
26311
|
-
|
|
26282
|
+
try {
|
|
26283
|
+
const analysisStart = performance.now();
|
|
26284
|
+
const nodeCache = collectAllNodes(tree.rootNode, getNodeTypesForLanguage(language));
|
|
26285
|
+
const types = extractTypes(tree, nodeCache, language);
|
|
26286
|
+
const calls = extractCalls(tree, nodeCache, language);
|
|
26287
|
+
const constPropResult = analyzeConstantPropagation(tree, code);
|
|
26288
|
+
const config = options.taintConfig ?? getDefaultConfig();
|
|
26289
|
+
const taint = analyzeTaint(calls, types, config);
|
|
26290
|
+
let filteredSinks = taint.sinks.filter((sink) => !constPropResult.unreachableLines.has(sink.line));
|
|
26291
|
+
filteredSinks = filterCleanVariableSinks(
|
|
26292
|
+
filteredSinks,
|
|
26293
|
+
calls,
|
|
26294
|
+
constPropResult.tainted,
|
|
26295
|
+
constPropResult.symbols,
|
|
26296
|
+
void 0,
|
|
26297
|
+
constPropResult.sanitizedVars,
|
|
26298
|
+
constPropResult.synchronizedLines
|
|
26299
|
+
);
|
|
26300
|
+
filteredSinks = filterSanitizedSinks(filteredSinks, taint.sanitizers ?? [], calls);
|
|
26301
|
+
let pythonTaintedVars = /* @__PURE__ */ new Map();
|
|
26302
|
+
if (language === "python") {
|
|
26303
|
+
pythonTaintedVars = buildPythonTaintedVars(code);
|
|
26304
|
+
const pythonSanitizedVars = buildPythonSanitizedVars(code, pythonTaintedVars);
|
|
26305
|
+
const sourceLines = code.split("\n");
|
|
26306
|
+
filteredSinks = filteredSinks.filter((sink) => {
|
|
26307
|
+
if (sink.type !== "xpath_injection") return true;
|
|
26308
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
26309
|
+
const taintedVarOnLine = [...pythonTaintedVars.keys()].find(
|
|
26310
|
+
(v) => new RegExp(`\\b${v}\\b`).test(sinkLineText)
|
|
26311
|
+
);
|
|
26312
|
+
if (!taintedVarOnLine) return false;
|
|
26313
|
+
if (pythonSanitizedVars.has(taintedVarOnLine)) return false;
|
|
26314
|
+
if (new RegExp(`\\.xpath\\s*\\([^)]*\\b\\w+\\s*=\\s*\\b${taintedVarOnLine}\\b`).test(sinkLineText)) return false;
|
|
26315
|
+
return true;
|
|
26316
|
+
});
|
|
26317
|
+
}
|
|
26318
|
+
const vulnerabilities = findVulnerabilities(taint.sources, filteredSinks, calls, constPropResult);
|
|
26319
|
+
if (language === "python") {
|
|
26320
|
+
const trustViolations = findPythonTrustBoundaryViolations(code, pythonTaintedVars);
|
|
26321
|
+
for (const v of trustViolations) {
|
|
26322
|
+
const alreadyReported = vulnerabilities.some(
|
|
26323
|
+
(existing) => existing.sink.line === v.sinkLine && existing.type === "trust_boundary"
|
|
26324
|
+
);
|
|
26325
|
+
if (!alreadyReported) {
|
|
26326
|
+
vulnerabilities.push({
|
|
26327
|
+
type: "trust_boundary",
|
|
26328
|
+
cwe: "CWE-501",
|
|
26329
|
+
severity: "medium",
|
|
26330
|
+
source: { line: v.sourceLine, type: "http_param" },
|
|
26331
|
+
sink: { line: v.sinkLine, type: "trust_boundary" },
|
|
26332
|
+
confidence: 0.85
|
|
26333
|
+
});
|
|
26334
|
+
}
|
|
26312
26335
|
}
|
|
26313
26336
|
}
|
|
26337
|
+
const analysisTime = performance.now() - analysisStart;
|
|
26338
|
+
const totalTime = performance.now() - startTime;
|
|
26339
|
+
return {
|
|
26340
|
+
success: true,
|
|
26341
|
+
analysis: {
|
|
26342
|
+
sources: taint.sources,
|
|
26343
|
+
sinks: filteredSinks,
|
|
26344
|
+
vulnerabilities
|
|
26345
|
+
},
|
|
26346
|
+
meta: {
|
|
26347
|
+
parseTimeMs: Math.round(parseTime),
|
|
26348
|
+
analysisTimeMs: Math.round(analysisTime),
|
|
26349
|
+
totalTimeMs: Math.round(totalTime)
|
|
26350
|
+
}
|
|
26351
|
+
};
|
|
26352
|
+
} finally {
|
|
26353
|
+
disposeTree(tree);
|
|
26314
26354
|
}
|
|
26315
|
-
const analysisTime = performance.now() - analysisStart;
|
|
26316
|
-
const totalTime = performance.now() - startTime;
|
|
26317
|
-
return {
|
|
26318
|
-
success: true,
|
|
26319
|
-
analysis: {
|
|
26320
|
-
sources: taint.sources,
|
|
26321
|
-
sinks: filteredSinks,
|
|
26322
|
-
vulnerabilities
|
|
26323
|
-
},
|
|
26324
|
-
meta: {
|
|
26325
|
-
parseTimeMs: Math.round(parseTime),
|
|
26326
|
-
analysisTimeMs: Math.round(analysisTime),
|
|
26327
|
-
totalTimeMs: Math.round(totalTime)
|
|
26328
|
-
}
|
|
26329
|
-
};
|
|
26330
26355
|
}
|
|
26331
26356
|
function findVulnerabilities(sources, sinks, calls, constPropResult) {
|
|
26332
26357
|
const vulnerabilities = [];
|
|
@@ -4062,6 +4062,7 @@ var parserInitialized = false;
|
|
|
4062
4062
|
var parserInitializing = null;
|
|
4063
4063
|
var loadedLanguages = /* @__PURE__ */ new Map();
|
|
4064
4064
|
var loadingLanguages = /* @__PURE__ */ new Map();
|
|
4065
|
+
var cachedParsers = /* @__PURE__ */ new Map();
|
|
4065
4066
|
var configuredLanguagePaths = {};
|
|
4066
4067
|
var configuredLanguageModules = {};
|
|
4067
4068
|
async function initParser(options = {}) {
|
|
@@ -4131,9 +4132,14 @@ async function loadLanguage(language, wasmPath) {
|
|
|
4131
4132
|
return loadPromise;
|
|
4132
4133
|
}
|
|
4133
4134
|
async function createParser(language) {
|
|
4135
|
+
const cached = cachedParsers.get(language);
|
|
4136
|
+
if (cached) {
|
|
4137
|
+
return cached;
|
|
4138
|
+
}
|
|
4134
4139
|
const lang = await loadLanguage(language);
|
|
4135
4140
|
const parser = new Parser();
|
|
4136
4141
|
parser.setLanguage(lang);
|
|
4142
|
+
cachedParsers.set(language, parser);
|
|
4137
4143
|
return parser;
|
|
4138
4144
|
}
|
|
4139
4145
|
async function parse(code, language) {
|
|
@@ -4230,9 +4236,19 @@ function isLanguageLoaded(language) {
|
|
|
4230
4236
|
return loadedLanguages.has(language);
|
|
4231
4237
|
}
|
|
4232
4238
|
function resetParser() {
|
|
4233
|
-
|
|
4239
|
+
for (const parser of cachedParsers.values()) {
|
|
4240
|
+
try {
|
|
4241
|
+
parser.delete();
|
|
4242
|
+
} catch {
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
cachedParsers.clear();
|
|
4234
4246
|
loadedLanguages.clear();
|
|
4247
|
+
loadingLanguages.clear();
|
|
4235
4248
|
configuredLanguagePaths = {};
|
|
4249
|
+
configuredLanguageModules = {};
|
|
4250
|
+
parserInitialized = false;
|
|
4251
|
+
parserInitializing = null;
|
|
4236
4252
|
}
|
|
4237
4253
|
|
|
4238
4254
|
// src/core/extractors/meta.ts
|
|
@@ -3997,6 +3997,7 @@ var parserInitialized = false;
|
|
|
3997
3997
|
var parserInitializing = null;
|
|
3998
3998
|
var loadedLanguages = /* @__PURE__ */ new Map();
|
|
3999
3999
|
var loadingLanguages = /* @__PURE__ */ new Map();
|
|
4000
|
+
var cachedParsers = /* @__PURE__ */ new Map();
|
|
4000
4001
|
var configuredLanguagePaths = {};
|
|
4001
4002
|
var configuredLanguageModules = {};
|
|
4002
4003
|
async function initParser(options = {}) {
|
|
@@ -4066,9 +4067,14 @@ async function loadLanguage(language, wasmPath) {
|
|
|
4066
4067
|
return loadPromise;
|
|
4067
4068
|
}
|
|
4068
4069
|
async function createParser(language) {
|
|
4070
|
+
const cached = cachedParsers.get(language);
|
|
4071
|
+
if (cached) {
|
|
4072
|
+
return cached;
|
|
4073
|
+
}
|
|
4069
4074
|
const lang = await loadLanguage(language);
|
|
4070
4075
|
const parser = new Parser();
|
|
4071
4076
|
parser.setLanguage(lang);
|
|
4077
|
+
cachedParsers.set(language, parser);
|
|
4072
4078
|
return parser;
|
|
4073
4079
|
}
|
|
4074
4080
|
async function parse(code, language) {
|
|
@@ -4165,9 +4171,19 @@ function isLanguageLoaded(language) {
|
|
|
4165
4171
|
return loadedLanguages.has(language);
|
|
4166
4172
|
}
|
|
4167
4173
|
function resetParser() {
|
|
4168
|
-
|
|
4174
|
+
for (const parser of cachedParsers.values()) {
|
|
4175
|
+
try {
|
|
4176
|
+
parser.delete();
|
|
4177
|
+
} catch {
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
cachedParsers.clear();
|
|
4169
4181
|
loadedLanguages.clear();
|
|
4182
|
+
loadingLanguages.clear();
|
|
4170
4183
|
configuredLanguagePaths = {};
|
|
4184
|
+
configuredLanguageModules = {};
|
|
4185
|
+
parserInitialized = false;
|
|
4186
|
+
parserInitializing = null;
|
|
4171
4187
|
}
|
|
4172
4188
|
|
|
4173
4189
|
// src/core/extractors/meta.ts
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core module index - re-exports parser and extractors
|
|
3
3
|
*/
|
|
4
|
-
export { initParser, loadLanguage, createParser, parse, walkTree, findNodes, findAncestor, getNodeText, collectAllNodes, getNodesFromCache, isInitialized, isLanguageLoaded, resetParser, type SupportedLanguage, type SyntaxNode, type Node, type NodeCache, type Language, type Tree, } from './parser.js';
|
|
4
|
+
export { initParser, loadLanguage, createParser, createFreshParser, parse, disposeTree, walkTree, findNodes, findAncestor, getNodeText, collectAllNodes, getNodesFromCache, isInitialized, isLanguageLoaded, resetParser, type SupportedLanguage, type SyntaxNode, type Node, type NodeCache, type Language, type Tree, } from './parser.js';
|
|
5
5
|
export { extractMeta, extractTypes, extractCalls, extractImports, extractExports, buildCFG, buildDFG, } from './extractors/index.js';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,IAAI,GACV,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,cAAc,EACd,QAAQ,EACR,QAAQ,GACT,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,IAAI,GACV,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,cAAc,EACd,QAAQ,EACR,QAAQ,GACT,MAAM,uBAAuB,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Core module index - re-exports parser and extractors
|
|
3
3
|
*/
|
|
4
4
|
// Parser
|
|
5
|
-
export { initParser, loadLanguage, createParser, parse, walkTree, findNodes, findAncestor, getNodeText, collectAllNodes, getNodesFromCache, isInitialized, isLanguageLoaded, resetParser, } from './parser.js';
|
|
5
|
+
export { initParser, loadLanguage, createParser, createFreshParser, parse, disposeTree, walkTree, findNodes, findAncestor, getNodeText, collectAllNodes, getNodesFromCache, isInitialized, isLanguageLoaded, resetParser, } from './parser.js';
|
|
6
6
|
// Extractors
|
|
7
7
|
export { extractMeta, extractTypes, extractCalls, extractImports, extractExports, buildCFG, buildDFG, } from './extractors/index.js';
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,SAAS;AACT,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,WAAW,GAOZ,MAAM,aAAa,CAAC;AAErB,aAAa;AACb,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,cAAc,EACd,QAAQ,EACR,QAAQ,GACT,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,SAAS;AACT,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,WAAW,GAOZ,MAAM,aAAa,CAAC;AAErB,aAAa;AACb,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,cAAc,EACd,QAAQ,EACR,QAAQ,GACT,MAAM,uBAAuB,CAAC"}
|