cognium-dev 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/cli.js +203 -182
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -3261,6 +3261,7 @@ var parserInitialized = false;
|
|
|
3261
3261
|
var parserInitializing = null;
|
|
3262
3262
|
var loadedLanguages = new Map;
|
|
3263
3263
|
var loadingLanguages = new Map;
|
|
3264
|
+
var cachedParsers = new Map;
|
|
3264
3265
|
var configuredLanguagePaths = {};
|
|
3265
3266
|
var configuredLanguageModules = {};
|
|
3266
3267
|
async function initParser(options = {}) {
|
|
@@ -3330,9 +3331,14 @@ async function loadLanguage(language, wasmPath) {
|
|
|
3330
3331
|
return loadPromise;
|
|
3331
3332
|
}
|
|
3332
3333
|
async function createParser(language) {
|
|
3334
|
+
const cached = cachedParsers.get(language);
|
|
3335
|
+
if (cached) {
|
|
3336
|
+
return cached;
|
|
3337
|
+
}
|
|
3333
3338
|
const lang = await loadLanguage(language);
|
|
3334
3339
|
const parser = new Parser;
|
|
3335
3340
|
parser.setLanguage(lang);
|
|
3341
|
+
cachedParsers.set(language, parser);
|
|
3336
3342
|
return parser;
|
|
3337
3343
|
}
|
|
3338
3344
|
async function parse(code, language) {
|
|
@@ -3343,6 +3349,13 @@ async function parse(code, language) {
|
|
|
3343
3349
|
}
|
|
3344
3350
|
return tree;
|
|
3345
3351
|
}
|
|
3352
|
+
function disposeTree(tree) {
|
|
3353
|
+
if (!tree)
|
|
3354
|
+
return;
|
|
3355
|
+
try {
|
|
3356
|
+
tree.delete();
|
|
3357
|
+
} catch {}
|
|
3358
|
+
}
|
|
3346
3359
|
function walkTree(node, visitor) {
|
|
3347
3360
|
visitor(node);
|
|
3348
3361
|
for (let i2 = 0;i2 < node.childCount; i2++) {
|
|
@@ -25761,194 +25774,202 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
25761
25774
|
}
|
|
25762
25775
|
logger.debug("Analyzing file", { filePath, language, codeLength: code.length });
|
|
25763
25776
|
const tree = await parse(code, language);
|
|
25764
|
-
|
|
25765
|
-
|
|
25766
|
-
|
|
25767
|
-
|
|
25768
|
-
|
|
25769
|
-
|
|
25770
|
-
|
|
25771
|
-
|
|
25772
|
-
|
|
25773
|
-
|
|
25774
|
-
|
|
25775
|
-
|
|
25776
|
-
|
|
25777
|
-
|
|
25778
|
-
|
|
25779
|
-
|
|
25780
|
-
|
|
25781
|
-
|
|
25782
|
-
|
|
25783
|
-
|
|
25784
|
-
|
|
25785
|
-
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25792
|
-
|
|
25793
|
-
|
|
25794
|
-
|
|
25795
|
-
|
|
25796
|
-
|
|
25797
|
-
|
|
25798
|
-
|
|
25799
|
-
|
|
25800
|
-
|
|
25801
|
-
|
|
25802
|
-
|
|
25803
|
-
|
|
25804
|
-
|
|
25805
|
-
|
|
25806
|
-
|
|
25807
|
-
|
|
25808
|
-
|
|
25809
|
-
|
|
25810
|
-
|
|
25811
|
-
|
|
25812
|
-
|
|
25813
|
-
|
|
25814
|
-
|
|
25815
|
-
|
|
25816
|
-
|
|
25817
|
-
|
|
25818
|
-
|
|
25819
|
-
|
|
25820
|
-
|
|
25821
|
-
|
|
25822
|
-
|
|
25823
|
-
|
|
25824
|
-
|
|
25825
|
-
|
|
25826
|
-
|
|
25827
|
-
|
|
25828
|
-
|
|
25829
|
-
|
|
25830
|
-
|
|
25831
|
-
|
|
25832
|
-
|
|
25833
|
-
|
|
25834
|
-
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
25839
|
-
|
|
25840
|
-
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
|
|
25854
|
-
|
|
25855
|
-
|
|
25856
|
-
|
|
25857
|
-
|
|
25858
|
-
|
|
25859
|
-
|
|
25860
|
-
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
|
|
25864
|
-
|
|
25865
|
-
|
|
25866
|
-
|
|
25867
|
-
|
|
25868
|
-
|
|
25869
|
-
|
|
25870
|
-
|
|
25871
|
-
|
|
25872
|
-
|
|
25873
|
-
|
|
25874
|
-
|
|
25875
|
-
|
|
25876
|
-
|
|
25877
|
-
|
|
25878
|
-
|
|
25879
|
-
|
|
25880
|
-
|
|
25881
|
-
|
|
25882
|
-
|
|
25883
|
-
|
|
25884
|
-
|
|
25885
|
-
|
|
25886
|
-
|
|
25887
|
-
|
|
25888
|
-
|
|
25889
|
-
|
|
25890
|
-
|
|
25891
|
-
|
|
25892
|
-
|
|
25893
|
-
|
|
25894
|
-
|
|
25895
|
-
|
|
25896
|
-
|
|
25897
|
-
|
|
25898
|
-
|
|
25899
|
-
|
|
25900
|
-
|
|
25777
|
+
try {
|
|
25778
|
+
logger.trace("Parsed AST", { rootNodeType: tree.rootNode.type });
|
|
25779
|
+
const nodeCache = collectAllNodes(tree.rootNode, getNodeTypesForLanguage(language));
|
|
25780
|
+
const meta = extractMeta(code, tree, filePath, language);
|
|
25781
|
+
const types = extractTypes(tree, nodeCache, language);
|
|
25782
|
+
const calls = extractCalls(tree, nodeCache, language);
|
|
25783
|
+
const imports = extractImports(tree, language);
|
|
25784
|
+
const exports = extractExports(types);
|
|
25785
|
+
const cfg = buildCFG(tree, language);
|
|
25786
|
+
const dfg = buildDFG(tree, nodeCache, language);
|
|
25787
|
+
const graph = new CodeGraph({
|
|
25788
|
+
meta,
|
|
25789
|
+
types,
|
|
25790
|
+
calls,
|
|
25791
|
+
cfg,
|
|
25792
|
+
dfg,
|
|
25793
|
+
taint: { sources: [], sinks: [], sanitizers: [] },
|
|
25794
|
+
imports,
|
|
25795
|
+
exports,
|
|
25796
|
+
unresolved: [],
|
|
25797
|
+
enriched: {}
|
|
25798
|
+
});
|
|
25799
|
+
const config = options.taintConfig ?? getDefaultConfig();
|
|
25800
|
+
const disabledPasses = new Set(options.disabledPasses ?? []);
|
|
25801
|
+
const passOpts = options.passOptions ?? {};
|
|
25802
|
+
const pipeline = new AnalysisPipeline;
|
|
25803
|
+
pipeline.add(new TaintMatcherPass);
|
|
25804
|
+
pipeline.add(new ConstantPropagationPass(tree));
|
|
25805
|
+
pipeline.add(new LanguageSourcesPass);
|
|
25806
|
+
pipeline.add(new SinkFilterPass);
|
|
25807
|
+
pipeline.add(new TaintPropagationPass);
|
|
25808
|
+
pipeline.add(new InterproceduralPass);
|
|
25809
|
+
if (!disabledPasses.has("scan-secrets"))
|
|
25810
|
+
pipeline.add(new ScanSecretsPass);
|
|
25811
|
+
if (!disabledPasses.has("dead-code"))
|
|
25812
|
+
pipeline.add(new DeadCodePass);
|
|
25813
|
+
if (!disabledPasses.has("missing-await"))
|
|
25814
|
+
pipeline.add(new MissingAwaitPass);
|
|
25815
|
+
if (!disabledPasses.has("n-plus-one"))
|
|
25816
|
+
pipeline.add(new NPlusOnePass);
|
|
25817
|
+
if (!disabledPasses.has("missing-public-doc"))
|
|
25818
|
+
pipeline.add(new MissingPublicDocPass);
|
|
25819
|
+
if (!disabledPasses.has("todo-in-prod"))
|
|
25820
|
+
pipeline.add(new TodoInProdPass);
|
|
25821
|
+
if (!disabledPasses.has("string-concat-loop"))
|
|
25822
|
+
pipeline.add(new StringConcatLoopPass);
|
|
25823
|
+
if (!disabledPasses.has("sync-io-async"))
|
|
25824
|
+
pipeline.add(new SyncIoAsyncPass);
|
|
25825
|
+
if (!disabledPasses.has("unchecked-return"))
|
|
25826
|
+
pipeline.add(new UncheckedReturnPass);
|
|
25827
|
+
if (!disabledPasses.has("null-deref"))
|
|
25828
|
+
pipeline.add(new NullDerefPass);
|
|
25829
|
+
if (!disabledPasses.has("resource-leak"))
|
|
25830
|
+
pipeline.add(new ResourceLeakPass);
|
|
25831
|
+
if (!disabledPasses.has("variable-shadowing"))
|
|
25832
|
+
pipeline.add(new VariableShadowingPass);
|
|
25833
|
+
if (!disabledPasses.has("leaked-global"))
|
|
25834
|
+
pipeline.add(new LeakedGlobalPass);
|
|
25835
|
+
if (!disabledPasses.has("unused-variable"))
|
|
25836
|
+
pipeline.add(new UnusedVariablePass);
|
|
25837
|
+
if (!disabledPasses.has("dependency-fan-out"))
|
|
25838
|
+
pipeline.add(new DependencyFanOutPass(passOpts.dependencyFanOut));
|
|
25839
|
+
if (!disabledPasses.has("stale-doc-ref"))
|
|
25840
|
+
pipeline.add(new StaleDocRefPass);
|
|
25841
|
+
if (!disabledPasses.has("infinite-loop"))
|
|
25842
|
+
pipeline.add(new InfiniteLoopPass);
|
|
25843
|
+
if (!disabledPasses.has("deep-inheritance"))
|
|
25844
|
+
pipeline.add(new DeepInheritancePass);
|
|
25845
|
+
if (!disabledPasses.has("redundant-loop-computation"))
|
|
25846
|
+
pipeline.add(new RedundantLoopPass);
|
|
25847
|
+
if (!disabledPasses.has("unbounded-collection"))
|
|
25848
|
+
pipeline.add(new UnboundedCollectionPass(passOpts.unboundedCollection));
|
|
25849
|
+
if (!disabledPasses.has("serial-await"))
|
|
25850
|
+
pipeline.add(new SerialAwaitPass);
|
|
25851
|
+
if (!disabledPasses.has("react-inline-jsx"))
|
|
25852
|
+
pipeline.add(new ReactInlineJsxPass);
|
|
25853
|
+
if (!disabledPasses.has("swallowed-exception"))
|
|
25854
|
+
pipeline.add(new SwallowedExceptionPass);
|
|
25855
|
+
if (!disabledPasses.has("broad-catch"))
|
|
25856
|
+
pipeline.add(new BroadCatchPass);
|
|
25857
|
+
if (!disabledPasses.has("unhandled-exception"))
|
|
25858
|
+
pipeline.add(new UnhandledExceptionPass);
|
|
25859
|
+
if (!disabledPasses.has("double-close"))
|
|
25860
|
+
pipeline.add(new DoubleClosePass);
|
|
25861
|
+
if (!disabledPasses.has("use-after-close"))
|
|
25862
|
+
pipeline.add(new UseAfterClosePass);
|
|
25863
|
+
if (!disabledPasses.has("cleanup-verify"))
|
|
25864
|
+
pipeline.add(new CleanupVerifyPass);
|
|
25865
|
+
if (!disabledPasses.has("missing-override"))
|
|
25866
|
+
pipeline.add(new MissingOverridePass);
|
|
25867
|
+
if (!disabledPasses.has("unused-interface-method"))
|
|
25868
|
+
pipeline.add(new UnusedInterfaceMethodPass);
|
|
25869
|
+
if (!disabledPasses.has("blocking-main-thread"))
|
|
25870
|
+
pipeline.add(new BlockingMainThreadPass);
|
|
25871
|
+
if (!disabledPasses.has("excessive-allocation"))
|
|
25872
|
+
pipeline.add(new ExcessiveAllocationPass);
|
|
25873
|
+
if (!disabledPasses.has("missing-stream"))
|
|
25874
|
+
pipeline.add(new MissingStreamPass);
|
|
25875
|
+
if (!disabledPasses.has("god-class"))
|
|
25876
|
+
pipeline.add(new GodClassPass);
|
|
25877
|
+
if (!disabledPasses.has("naming-convention"))
|
|
25878
|
+
pipeline.add(new NamingConventionPass(passOpts.namingConvention));
|
|
25879
|
+
if (!disabledPasses.has("security-headers"))
|
|
25880
|
+
pipeline.add(new SecurityHeadersPass(passOpts.securityHeaders));
|
|
25881
|
+
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
25882
|
+
const sinkFilter = results.get("sink-filter");
|
|
25883
|
+
const interProc = results.get("interprocedural");
|
|
25884
|
+
const taint = {
|
|
25885
|
+
sources: sinkFilter.sources,
|
|
25886
|
+
sinks: [...sinkFilter.sinks, ...interProc.additionalSinks],
|
|
25887
|
+
sanitizers: sinkFilter.sanitizers,
|
|
25888
|
+
flows: interProc.additionalFlows,
|
|
25889
|
+
interprocedural: interProc.interprocedural
|
|
25890
|
+
};
|
|
25891
|
+
const unresolved = detectUnresolved(calls, types, dfg);
|
|
25892
|
+
const enriched = buildEnriched(types, calls, taint.sources, taint.sinks);
|
|
25893
|
+
const metricValues = new MetricRunner().run({ meta, types, calls, cfg, dfg, taint, imports, exports, unresolved, enriched }, code, language);
|
|
25894
|
+
logger.debug("Analysis complete", {
|
|
25895
|
+
filePath,
|
|
25896
|
+
finalSources: taint.sources.length,
|
|
25897
|
+
finalSinks: taint.sinks.length,
|
|
25898
|
+
flows: taint.flows?.length ?? 0,
|
|
25899
|
+
unresolvedItems: unresolved.length
|
|
25900
|
+
});
|
|
25901
|
+
return {
|
|
25902
|
+
meta,
|
|
25903
|
+
types,
|
|
25904
|
+
calls,
|
|
25905
|
+
cfg,
|
|
25906
|
+
dfg,
|
|
25907
|
+
taint,
|
|
25908
|
+
imports,
|
|
25909
|
+
exports,
|
|
25910
|
+
unresolved,
|
|
25911
|
+
enriched,
|
|
25912
|
+
findings: findings.length > 0 ? findings : undefined,
|
|
25913
|
+
metrics: { file: filePath, metrics: metricValues }
|
|
25914
|
+
};
|
|
25915
|
+
} finally {
|
|
25916
|
+
disposeTree(tree);
|
|
25917
|
+
}
|
|
25901
25918
|
}
|
|
25902
25919
|
async function analyzeHtmlFile(code, filePath, options) {
|
|
25903
25920
|
logger.debug("Analyzing HTML file", { filePath, codeLength: code.length });
|
|
25904
25921
|
const tree = await parse(code, "html");
|
|
25905
|
-
|
|
25906
|
-
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
|
|
25917
|
-
|
|
25918
|
-
|
|
25919
|
-
|
|
25920
|
-
|
|
25921
|
-
|
|
25922
|
-
|
|
25923
|
-
|
|
25924
|
-
|
|
25925
|
-
|
|
25926
|
-
|
|
25922
|
+
try {
|
|
25923
|
+
const meta = extractMeta(code, tree, filePath, "html");
|
|
25924
|
+
const { scriptBlocks, eventHandlers } = extractHtmlContent(tree.rootNode);
|
|
25925
|
+
logger.debug("HTML extraction", {
|
|
25926
|
+
filePath,
|
|
25927
|
+
inlineScripts: scriptBlocks.filter((b) => b.kind === "inline").length,
|
|
25928
|
+
externalScripts: scriptBlocks.filter((b) => b.kind === "external-src").length,
|
|
25929
|
+
eventHandlers: eventHandlers.length
|
|
25930
|
+
});
|
|
25931
|
+
const scriptResults = [];
|
|
25932
|
+
for (const block of scriptBlocks) {
|
|
25933
|
+
if (block.kind !== "inline" || !block.code.trim())
|
|
25934
|
+
continue;
|
|
25935
|
+
const scriptLang = block.scriptType === "ts" || block.scriptType === "typescript" || block.scriptType === "text/typescript" ? "typescript" : "javascript";
|
|
25936
|
+
try {
|
|
25937
|
+
const ir = await analyze(block.code, filePath, scriptLang, options);
|
|
25938
|
+
scriptResults.push({ ir, lineOffset: block.lineOffset });
|
|
25939
|
+
} catch (e) {
|
|
25940
|
+
logger.warn("Failed to analyze script block", {
|
|
25941
|
+
filePath,
|
|
25942
|
+
lineOffset: block.lineOffset,
|
|
25943
|
+
error: e instanceof Error ? e.message : String(e)
|
|
25944
|
+
});
|
|
25945
|
+
}
|
|
25927
25946
|
}
|
|
25928
|
-
|
|
25929
|
-
|
|
25930
|
-
|
|
25931
|
-
|
|
25932
|
-
|
|
25933
|
-
|
|
25934
|
-
|
|
25935
|
-
|
|
25936
|
-
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
25940
|
-
}
|
|
25947
|
+
for (const handler of eventHandlers) {
|
|
25948
|
+
const wrappedCode = `function __${handler.eventName}_handler() { ${handler.code} }`;
|
|
25949
|
+
try {
|
|
25950
|
+
const ir = await analyze(wrappedCode, filePath, "javascript", options);
|
|
25951
|
+
scriptResults.push({ ir, lineOffset: handler.line });
|
|
25952
|
+
} catch (e) {
|
|
25953
|
+
logger.warn("Failed to analyze event handler", {
|
|
25954
|
+
filePath,
|
|
25955
|
+
eventName: handler.eventName,
|
|
25956
|
+
line: handler.line,
|
|
25957
|
+
error: e instanceof Error ? e.message : String(e)
|
|
25958
|
+
});
|
|
25959
|
+
}
|
|
25941
25960
|
}
|
|
25961
|
+
const attributeFindings = runHtmlAttributeSecurityChecks(tree.rootNode, filePath);
|
|
25962
|
+
const result = mergeHtmlResults(meta, scriptResults, attributeFindings);
|
|
25963
|
+
logger.debug("HTML analysis complete", {
|
|
25964
|
+
filePath,
|
|
25965
|
+
scriptBlocks: scriptResults.length,
|
|
25966
|
+
attributeFindings: attributeFindings.length,
|
|
25967
|
+
totalFindings: result.findings?.length ?? 0
|
|
25968
|
+
});
|
|
25969
|
+
return result;
|
|
25970
|
+
} finally {
|
|
25971
|
+
disposeTree(tree);
|
|
25942
25972
|
}
|
|
25943
|
-
const attributeFindings = runHtmlAttributeSecurityChecks(tree.rootNode, filePath);
|
|
25944
|
-
const result = mergeHtmlResults(meta, scriptResults, attributeFindings);
|
|
25945
|
-
logger.debug("HTML analysis complete", {
|
|
25946
|
-
filePath,
|
|
25947
|
-
scriptBlocks: scriptResults.length,
|
|
25948
|
-
attributeFindings: attributeFindings.length,
|
|
25949
|
-
totalFindings: result.findings?.length ?? 0
|
|
25950
|
-
});
|
|
25951
|
-
return result;
|
|
25952
25973
|
}
|
|
25953
25974
|
async function analyzeProject(files, options = {}) {
|
|
25954
25975
|
const fileAnalyses = [];
|
|
@@ -26038,7 +26059,7 @@ var colors = {
|
|
|
26038
26059
|
};
|
|
26039
26060
|
|
|
26040
26061
|
// src/version.ts
|
|
26041
|
-
var version = "3.
|
|
26062
|
+
var version = "3.28.0";
|
|
26042
26063
|
|
|
26043
26064
|
// src/formatters.ts
|
|
26044
26065
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.28.0",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"registry": "https://registry.npmjs.org/"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"circle-ir": "^3.
|
|
68
|
+
"circle-ir": "^3.28.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|