cognium-dev 3.58.0 → 3.62.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 +520 -48
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -3612,7 +3612,7 @@ function detectLanguage(tree) {
|
|
|
3612
3612
|
}
|
|
3613
3613
|
function extractTypes(tree, cache, language) {
|
|
3614
3614
|
const effectiveLanguage = language ?? detectLanguage(tree);
|
|
3615
|
-
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
3615
|
+
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
3616
3616
|
const isPython = effectiveLanguage === "python";
|
|
3617
3617
|
const isRust = effectiveLanguage === "rust";
|
|
3618
3618
|
if (effectiveLanguage === "go") {
|
|
@@ -5041,7 +5041,7 @@ function detectLanguageFromTree(tree, cache) {
|
|
|
5041
5041
|
function extractCalls(tree, cache, language) {
|
|
5042
5042
|
const calls = [];
|
|
5043
5043
|
const detectedLanguage = language ?? detectLanguageFromTree(tree, cache);
|
|
5044
|
-
const isJavaScript = detectedLanguage === "javascript" || detectedLanguage === "typescript";
|
|
5044
|
+
const isJavaScript = detectedLanguage === "javascript" || detectedLanguage === "typescript" || detectedLanguage === "tsx";
|
|
5045
5045
|
const isPython = detectedLanguage === "python";
|
|
5046
5046
|
const isRust = detectedLanguage === "rust";
|
|
5047
5047
|
if (detectedLanguage === "go") {
|
|
@@ -5090,8 +5090,149 @@ function extractJavaScriptCalls(tree, cache) {
|
|
|
5090
5090
|
calls.push(callInfo);
|
|
5091
5091
|
}
|
|
5092
5092
|
}
|
|
5093
|
+
const jsxAttributes = getNodesFromCache(tree.rootNode, "jsx_attribute", cache);
|
|
5094
|
+
for (const attr of jsxAttributes) {
|
|
5095
|
+
const callInfo = extractJSXAttributeSink(attr);
|
|
5096
|
+
if (callInfo) {
|
|
5097
|
+
calls.push(callInfo);
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
const assignments = getNodesFromCache(tree.rootNode, "assignment_expression", cache);
|
|
5101
|
+
for (const assign of assignments) {
|
|
5102
|
+
const callInfo = extractDomPropertyAssignmentSink(assign);
|
|
5103
|
+
if (callInfo) {
|
|
5104
|
+
calls.push(callInfo);
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5093
5107
|
return calls;
|
|
5094
5108
|
}
|
|
5109
|
+
var DOM_XSS_ASSIGNMENT_PROPERTIES = new Set([
|
|
5110
|
+
"innerHTML",
|
|
5111
|
+
"outerHTML"
|
|
5112
|
+
]);
|
|
5113
|
+
function extractDomPropertyAssignmentSink(node) {
|
|
5114
|
+
const leftNode = node.childForFieldName("left");
|
|
5115
|
+
const rightNode = node.childForFieldName("right");
|
|
5116
|
+
if (!leftNode || !rightNode)
|
|
5117
|
+
return null;
|
|
5118
|
+
if (leftNode.type !== "member_expression")
|
|
5119
|
+
return null;
|
|
5120
|
+
const propertyNode = leftNode.childForFieldName("property");
|
|
5121
|
+
const objectNode = leftNode.childForFieldName("object");
|
|
5122
|
+
if (!propertyNode)
|
|
5123
|
+
return null;
|
|
5124
|
+
const propertyName = getNodeText(propertyNode);
|
|
5125
|
+
if (!DOM_XSS_ASSIGNMENT_PROPERTIES.has(propertyName))
|
|
5126
|
+
return null;
|
|
5127
|
+
const receiver = objectNode ? getNodeText(objectNode) : null;
|
|
5128
|
+
const expression = getNodeText(rightNode);
|
|
5129
|
+
const { variable, literal } = analyzeJSArgument(rightNode);
|
|
5130
|
+
const enclosingFunc = findJSEnclosingFunction(node);
|
|
5131
|
+
return {
|
|
5132
|
+
method_name: propertyName,
|
|
5133
|
+
receiver,
|
|
5134
|
+
arguments: [
|
|
5135
|
+
{
|
|
5136
|
+
position: 0,
|
|
5137
|
+
expression,
|
|
5138
|
+
variable,
|
|
5139
|
+
literal
|
|
5140
|
+
}
|
|
5141
|
+
],
|
|
5142
|
+
location: {
|
|
5143
|
+
line: node.startPosition.row + 1,
|
|
5144
|
+
column: node.startPosition.column
|
|
5145
|
+
},
|
|
5146
|
+
in_method: enclosingFunc,
|
|
5147
|
+
resolved: true,
|
|
5148
|
+
resolution: {
|
|
5149
|
+
status: "resolved",
|
|
5150
|
+
target: `DOM.${propertyName}`
|
|
5151
|
+
}
|
|
5152
|
+
};
|
|
5153
|
+
}
|
|
5154
|
+
function extractJSXAttributeSink(attr) {
|
|
5155
|
+
let nameNode = null;
|
|
5156
|
+
for (let i2 = 0;i2 < attr.childCount; i2++) {
|
|
5157
|
+
const child = attr.child(i2);
|
|
5158
|
+
if (child && child.type === "property_identifier") {
|
|
5159
|
+
nameNode = child;
|
|
5160
|
+
break;
|
|
5161
|
+
}
|
|
5162
|
+
}
|
|
5163
|
+
if (!nameNode)
|
|
5164
|
+
return null;
|
|
5165
|
+
const attrName = getNodeText(nameNode);
|
|
5166
|
+
if (attrName !== "dangerouslySetInnerHTML")
|
|
5167
|
+
return null;
|
|
5168
|
+
let valueExpr = null;
|
|
5169
|
+
for (let i2 = 0;i2 < attr.childCount; i2++) {
|
|
5170
|
+
const child = attr.child(i2);
|
|
5171
|
+
if (child && child.type === "jsx_expression") {
|
|
5172
|
+
valueExpr = child;
|
|
5173
|
+
break;
|
|
5174
|
+
}
|
|
5175
|
+
}
|
|
5176
|
+
if (!valueExpr)
|
|
5177
|
+
return null;
|
|
5178
|
+
let htmlValue = null;
|
|
5179
|
+
for (let i2 = 0;i2 < valueExpr.childCount; i2++) {
|
|
5180
|
+
const inner = valueExpr.child(i2);
|
|
5181
|
+
if (!inner || inner.type !== "object")
|
|
5182
|
+
continue;
|
|
5183
|
+
for (let j = 0;j < inner.childCount; j++) {
|
|
5184
|
+
const pair = inner.child(j);
|
|
5185
|
+
if (!pair || pair.type !== "pair")
|
|
5186
|
+
continue;
|
|
5187
|
+
const keyNode = pair.childForFieldName("key");
|
|
5188
|
+
if (!keyNode)
|
|
5189
|
+
continue;
|
|
5190
|
+
const keyText = getNodeText(keyNode).replace(/^["']|["']$/g, "");
|
|
5191
|
+
if (keyText === "__html") {
|
|
5192
|
+
htmlValue = pair.childForFieldName("value");
|
|
5193
|
+
break;
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
if (htmlValue)
|
|
5197
|
+
break;
|
|
5198
|
+
}
|
|
5199
|
+
if (!htmlValue) {
|
|
5200
|
+
for (let i2 = 0;i2 < valueExpr.childCount; i2++) {
|
|
5201
|
+
const inner = valueExpr.child(i2);
|
|
5202
|
+
if (inner && inner.type !== "{" && inner.type !== "}") {
|
|
5203
|
+
htmlValue = inner;
|
|
5204
|
+
break;
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
if (!htmlValue)
|
|
5209
|
+
return null;
|
|
5210
|
+
const expression = getNodeText(htmlValue);
|
|
5211
|
+
const { variable, literal } = analyzeJSArgument(htmlValue);
|
|
5212
|
+
const enclosingFunc = findJSEnclosingFunction(attr);
|
|
5213
|
+
return {
|
|
5214
|
+
method_name: "dangerouslySetInnerHTML",
|
|
5215
|
+
receiver: null,
|
|
5216
|
+
arguments: [
|
|
5217
|
+
{
|
|
5218
|
+
position: 0,
|
|
5219
|
+
expression,
|
|
5220
|
+
variable,
|
|
5221
|
+
literal
|
|
5222
|
+
}
|
|
5223
|
+
],
|
|
5224
|
+
location: {
|
|
5225
|
+
line: attr.startPosition.row + 1,
|
|
5226
|
+
column: attr.startPosition.column
|
|
5227
|
+
},
|
|
5228
|
+
in_method: enclosingFunc,
|
|
5229
|
+
resolved: true,
|
|
5230
|
+
resolution: {
|
|
5231
|
+
status: "resolved",
|
|
5232
|
+
target: "react.dangerouslySetInnerHTML"
|
|
5233
|
+
}
|
|
5234
|
+
};
|
|
5235
|
+
}
|
|
5095
5236
|
function buildJSResolutionContext(tree, cache) {
|
|
5096
5237
|
const context = {
|
|
5097
5238
|
functionNames: new Set,
|
|
@@ -6632,7 +6773,7 @@ function detectLanguage2(tree) {
|
|
|
6632
6773
|
}
|
|
6633
6774
|
function extractImports(tree, language) {
|
|
6634
6775
|
const effectiveLanguage = language ?? detectLanguage2(tree);
|
|
6635
|
-
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
6776
|
+
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
6636
6777
|
const isPython = effectiveLanguage === "python";
|
|
6637
6778
|
const isRust = effectiveLanguage === "rust";
|
|
6638
6779
|
if (effectiveLanguage === "go") {
|
|
@@ -7347,7 +7488,7 @@ function detectLanguage3(tree) {
|
|
|
7347
7488
|
}
|
|
7348
7489
|
function buildCFG(tree, language) {
|
|
7349
7490
|
const effectiveLanguage = language ?? detectLanguage3(tree);
|
|
7350
|
-
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
7491
|
+
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
7351
7492
|
const allBlocks = [];
|
|
7352
7493
|
const allEdges = [];
|
|
7353
7494
|
let blockIdCounter = 0;
|
|
@@ -7928,7 +8069,7 @@ function detectLanguage4(tree) {
|
|
|
7928
8069
|
}
|
|
7929
8070
|
function buildDFG(tree, cache, language) {
|
|
7930
8071
|
const effectiveLanguage = language ?? detectLanguage4(tree);
|
|
7931
|
-
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript";
|
|
8072
|
+
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
7932
8073
|
if (isJavaScript) {
|
|
7933
8074
|
return buildJavaScriptDFG(tree, cache);
|
|
7934
8075
|
}
|
|
@@ -8749,7 +8890,18 @@ function buildBashDFG(tree) {
|
|
|
8749
8890
|
if (varNameNode) {
|
|
8750
8891
|
const varName = getNodeText(varNameNode);
|
|
8751
8892
|
if (varName && !varName.startsWith("?") && !varName.startsWith("#")) {
|
|
8752
|
-
|
|
8893
|
+
let reachingDef = findReachingDef(varName, scopeStack);
|
|
8894
|
+
if (reachingDef === null && !positionalParams.includes(varName)) {
|
|
8895
|
+
const def = {
|
|
8896
|
+
id: defIdCounter++,
|
|
8897
|
+
variable: varName,
|
|
8898
|
+
line: 0,
|
|
8899
|
+
kind: "param"
|
|
8900
|
+
};
|
|
8901
|
+
defs.push(def);
|
|
8902
|
+
scopeStack[0].set(varName, def.id);
|
|
8903
|
+
reachingDef = def.id;
|
|
8904
|
+
}
|
|
8753
8905
|
uses.push({
|
|
8754
8906
|
id: useIdCounter++,
|
|
8755
8907
|
variable: varName,
|
|
@@ -8762,7 +8914,18 @@ function buildBashDFG(tree) {
|
|
|
8762
8914
|
const varNameNode = node.namedChildCount > 0 ? node.namedChild(0) : null;
|
|
8763
8915
|
if (varNameNode && varNameNode.type === "variable_name") {
|
|
8764
8916
|
const varName = getNodeText(varNameNode);
|
|
8765
|
-
|
|
8917
|
+
let reachingDef = findReachingDef(varName, scopeStack);
|
|
8918
|
+
if (reachingDef === null && !positionalParams.includes(varName)) {
|
|
8919
|
+
const def = {
|
|
8920
|
+
id: defIdCounter++,
|
|
8921
|
+
variable: varName,
|
|
8922
|
+
line: 0,
|
|
8923
|
+
kind: "param"
|
|
8924
|
+
};
|
|
8925
|
+
defs.push(def);
|
|
8926
|
+
scopeStack[0].set(varName, def.id);
|
|
8927
|
+
reachingDef = def.id;
|
|
8928
|
+
}
|
|
8766
8929
|
uses.push({
|
|
8767
8930
|
id: useIdCounter++,
|
|
8768
8931
|
variable: varName,
|
|
@@ -9234,7 +9397,7 @@ var FRAMEWORK_MODULE_PATTERNS = [
|
|
|
9234
9397
|
/^@nestjs\/core$/
|
|
9235
9398
|
];
|
|
9236
9399
|
function extractRuntimeRegistrations(tree, cache, language, imports) {
|
|
9237
|
-
if (language === "javascript" || language === "typescript") {
|
|
9400
|
+
if (language === "javascript" || language === "typescript" || language === "tsx") {
|
|
9238
9401
|
return extractJSRuntimeRegistrations(tree, cache, imports);
|
|
9239
9402
|
}
|
|
9240
9403
|
if (language === "python") {
|
|
@@ -10171,8 +10334,11 @@ var DEFAULT_SOURCES = [
|
|
|
10171
10334
|
{ method: "get", class: "cookies", type: "http_cookie", severity: "high", return_tainted: true },
|
|
10172
10335
|
{ property: "json", object: "request", type: "http_body", severity: "high", property_tainted: true },
|
|
10173
10336
|
{ property: "data", object: "request", type: "http_body", severity: "high", property_tainted: true },
|
|
10337
|
+
{ property: "stream", object: "request", type: "http_body", severity: "high", property_tainted: true },
|
|
10174
10338
|
{ property: "path", object: "request", type: "http_path", severity: "medium", property_tainted: true },
|
|
10175
10339
|
{ property: "query_string", object: "request", type: "http_query", severity: "high", property_tainted: true },
|
|
10340
|
+
{ method: "get_data", class: "request", type: "http_body", severity: "high", return_tainted: true },
|
|
10341
|
+
{ method: "get_json", class: "request", type: "http_body", severity: "high", return_tainted: true },
|
|
10176
10342
|
{ method: "get", class: "GET", type: "http_param", severity: "high", return_tainted: true },
|
|
10177
10343
|
{ method: "get", class: "POST", type: "http_param", severity: "high", return_tainted: true },
|
|
10178
10344
|
{ method: "get", class: "META", type: "http_header", severity: "high", return_tainted: true },
|
|
@@ -10335,13 +10501,13 @@ var DEFAULT_SINKS = [
|
|
|
10335
10501
|
{ method: "setExecutable", class: "ExecTask", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10336
10502
|
{ method: "setCommand", class: "ExecTask", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10337
10503
|
{ method: "execute", class: "Java", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10338
|
-
{ method: "bash", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10339
|
-
{ method: "shell", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10340
|
-
{ method: "sh", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10341
|
-
{ method: "spawn", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10342
|
-
{ method: "fork", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10343
|
-
{ method: "popen", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10344
|
-
{ method: "system", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10504
|
+
{ method: "bash", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10505
|
+
{ method: "shell", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10506
|
+
{ method: "sh", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10507
|
+
{ method: "spawn", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10508
|
+
{ method: "fork", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10509
|
+
{ method: "popen", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10510
|
+
{ method: "system", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10345
10511
|
{ method: "setCommandline", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10346
10512
|
{ method: "parse", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10347
10513
|
{ method: "addArgument", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -10404,6 +10570,9 @@ var DEFAULT_SINKS = [
|
|
|
10404
10570
|
{ method: "unzip", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
|
|
10405
10571
|
{ method: "extract", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
|
|
10406
10572
|
{ method: "extractAll", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
|
|
10573
|
+
{ method: "extractall", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10574
|
+
{ method: "ZipFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10575
|
+
{ method: "send_from_directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [1], languages: ["python"] },
|
|
10407
10576
|
{ method: "unjar", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
|
|
10408
10577
|
{ method: "PrintWriter", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10409
10578
|
{ method: "Scanner", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
@@ -11171,10 +11340,22 @@ var DEFAULT_SINKS = [
|
|
|
11171
11340
|
{ method: "redirect", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11172
11341
|
{ method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11173
11342
|
{ method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11174
|
-
{ method: "assign", class: "Object", type: "mass_assignment", cwe: "CWE-
|
|
11175
|
-
{ method: "
|
|
11176
|
-
{ method: "
|
|
11177
|
-
{ method: "
|
|
11343
|
+
{ method: "assign", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11344
|
+
{ method: "defineProperty", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2], languages: ["javascript", "typescript"] },
|
|
11345
|
+
{ method: "defineProperties", class: "Object", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
11346
|
+
{ method: "merge", class: "_", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11347
|
+
{ method: "merge", class: "lodash", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11348
|
+
{ method: "extend", class: "_", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11349
|
+
{ method: "extend", class: "lodash", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11350
|
+
{ method: "defaultsDeep", class: "_", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11351
|
+
{ method: "defaultsDeep", class: "lodash", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11352
|
+
{ method: "extend", class: "$", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11353
|
+
{ method: "extend", class: "jQuery", type: "mass_assignment", cwe: "CWE-1321", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11354
|
+
{ method: "innerHTML", type: "xss", cwe: "CWE-79", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11355
|
+
{ method: "outerHTML", type: "xss", cwe: "CWE-79", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11356
|
+
{ method: "unserialize", class: "serialize", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11357
|
+
{ method: "unserialize", class: "node-serialize", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11358
|
+
{ method: "unserialize", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] }
|
|
11178
11359
|
];
|
|
11179
11360
|
var DEFAULT_SANITIZERS = [
|
|
11180
11361
|
{ method: "setString", class: "PreparedStatement", removes: ["sql_injection"] },
|
|
@@ -11787,7 +11968,11 @@ function matchesSourcePattern(call, pattern) {
|
|
|
11787
11968
|
}
|
|
11788
11969
|
if (pattern.class && pattern.class !== "constructor") {
|
|
11789
11970
|
if (call.receiver_type && call.receiver_type === pattern.class) {} else if (call.receiver_type_fqn && call.receiver_type_fqn.endsWith("." + pattern.class)) {} else if (!call.receiver) {
|
|
11790
|
-
|
|
11971
|
+
const target = call.resolution?.target;
|
|
11972
|
+
const expectedTail = `${pattern.class}.${pattern.method}`;
|
|
11973
|
+
if (target && (target === expectedTail || target.endsWith("." + expectedTail))) {} else {
|
|
11974
|
+
return false;
|
|
11975
|
+
}
|
|
11791
11976
|
} else if (!receiverMightBeClass(call.receiver, pattern.class)) {
|
|
11792
11977
|
return false;
|
|
11793
11978
|
}
|
|
@@ -12044,7 +12229,11 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
|
|
|
12044
12229
|
}
|
|
12045
12230
|
return false;
|
|
12046
12231
|
} else if (!call.receiver && !call.receiver_type) {
|
|
12047
|
-
|
|
12232
|
+
const target = call.resolution?.target;
|
|
12233
|
+
const expectedTail = `${pattern.class}.${pattern.method}`;
|
|
12234
|
+
if (target && (target === expectedTail || target.endsWith("." + expectedTail))) {} else {
|
|
12235
|
+
return false;
|
|
12236
|
+
}
|
|
12048
12237
|
}
|
|
12049
12238
|
}
|
|
12050
12239
|
if (!pattern.class && call.receiver) {
|
|
@@ -15051,6 +15240,9 @@ class DefaultLanguageRegistry {
|
|
|
15051
15240
|
}
|
|
15052
15241
|
}
|
|
15053
15242
|
get(language) {
|
|
15243
|
+
if (language === "tsx") {
|
|
15244
|
+
return this.plugins.get("javascript");
|
|
15245
|
+
}
|
|
15054
15246
|
return this.plugins.get(language);
|
|
15055
15247
|
}
|
|
15056
15248
|
getForFile(filePath) {
|
|
@@ -17244,6 +17436,20 @@ class BashPlugin extends BaseLanguagePlugin {
|
|
|
17244
17436
|
cwe: "CWE-918",
|
|
17245
17437
|
severity: "high",
|
|
17246
17438
|
argPositions: [0]
|
|
17439
|
+
},
|
|
17440
|
+
{
|
|
17441
|
+
method: "source",
|
|
17442
|
+
type: "path_traversal",
|
|
17443
|
+
cwe: "CWE-98",
|
|
17444
|
+
severity: "critical",
|
|
17445
|
+
argPositions: [0]
|
|
17446
|
+
},
|
|
17447
|
+
{
|
|
17448
|
+
method: ".",
|
|
17449
|
+
type: "path_traversal",
|
|
17450
|
+
cwe: "CWE-98",
|
|
17451
|
+
severity: "critical",
|
|
17452
|
+
argPositions: [0]
|
|
17247
17453
|
}
|
|
17248
17454
|
];
|
|
17249
17455
|
}
|
|
@@ -20337,6 +20543,7 @@ function mergeHtmlResults(htmlMeta, scriptResults, attributeFindings) {
|
|
|
20337
20543
|
const allSources = [];
|
|
20338
20544
|
const allSinks = [];
|
|
20339
20545
|
const allSanitizers = [];
|
|
20546
|
+
const allFlows = [];
|
|
20340
20547
|
const allImports = [];
|
|
20341
20548
|
const allExports = [];
|
|
20342
20549
|
const allFindings = [];
|
|
@@ -20422,6 +20629,14 @@ function mergeHtmlResults(htmlMeta, scriptResults, attributeFindings) {
|
|
|
20422
20629
|
line: sanitizer.line + lineShift
|
|
20423
20630
|
});
|
|
20424
20631
|
}
|
|
20632
|
+
for (const flow of ir.taint.flows ?? []) {
|
|
20633
|
+
allFlows.push({
|
|
20634
|
+
...flow,
|
|
20635
|
+
source_line: flow.source_line + lineShift,
|
|
20636
|
+
sink_line: flow.sink_line + lineShift,
|
|
20637
|
+
path: flow.path.map((step) => ({ ...step, line: step.line + lineShift }))
|
|
20638
|
+
});
|
|
20639
|
+
}
|
|
20425
20640
|
for (const imp of ir.imports) {
|
|
20426
20641
|
allImports.push({
|
|
20427
20642
|
...imp,
|
|
@@ -20441,7 +20656,8 @@ function mergeHtmlResults(htmlMeta, scriptResults, attributeFindings) {
|
|
|
20441
20656
|
const taint = {
|
|
20442
20657
|
sources: allSources,
|
|
20443
20658
|
sinks: allSinks,
|
|
20444
|
-
sanitizers: allSanitizers.length > 0 ? allSanitizers : undefined
|
|
20659
|
+
sanitizers: allSanitizers.length > 0 ? allSanitizers : undefined,
|
|
20660
|
+
flows: allFlows.length > 0 ? allFlows : undefined
|
|
20445
20661
|
};
|
|
20446
20662
|
const cfg = {
|
|
20447
20663
|
blocks: allCfgBlocks,
|
|
@@ -20637,7 +20853,9 @@ class LanguageSourcesPass {
|
|
|
20637
20853
|
const constProp = ctx.getResult("constant-propagation");
|
|
20638
20854
|
const additionalSources = [];
|
|
20639
20855
|
const additionalSinks = [];
|
|
20856
|
+
const additionalSanitizers = [];
|
|
20640
20857
|
additionalSources.push(...findGetterSources(types, constProp.instanceFieldTaint, code));
|
|
20858
|
+
additionalSources.push(...findOopFieldReadSources(types, code, language));
|
|
20641
20859
|
additionalSources.push(...findJavaScriptAssignmentSources(code, language));
|
|
20642
20860
|
const jsDOMSinks = findJavaScriptDOMSinks(code, language);
|
|
20643
20861
|
for (const s of jsDOMSinks) {
|
|
@@ -20689,9 +20907,10 @@ class LanguageSourcesPass {
|
|
|
20689
20907
|
for (const finding of bashFindings) {
|
|
20690
20908
|
ctx.addFinding(finding);
|
|
20691
20909
|
}
|
|
20910
|
+
additionalSanitizers.push(...findBashRegexAllowlistSanitizers(code));
|
|
20692
20911
|
}
|
|
20693
20912
|
attachSourceLineCode(additionalSources, additionalSinks, code);
|
|
20694
|
-
return { additionalSources, additionalSinks, pyTaintedVars, pySanitizedVars, jsTaintedVars };
|
|
20913
|
+
return { additionalSources, additionalSinks, additionalSanitizers, pyTaintedVars, pySanitizedVars, jsTaintedVars };
|
|
20695
20914
|
}
|
|
20696
20915
|
}
|
|
20697
20916
|
function findGetterSources(types, instanceFieldTaint, _sourceCode) {
|
|
@@ -20743,6 +20962,128 @@ function findGetterSources(types, instanceFieldTaint, _sourceCode) {
|
|
|
20743
20962
|
}
|
|
20744
20963
|
return sources;
|
|
20745
20964
|
}
|
|
20965
|
+
function findOopFieldReadSources(types, sourceCode, language) {
|
|
20966
|
+
if (language !== "java" && language !== "python")
|
|
20967
|
+
return [];
|
|
20968
|
+
const sources = [];
|
|
20969
|
+
const lines = sourceCode.split(`
|
|
20970
|
+
`);
|
|
20971
|
+
const isPython = language === "python";
|
|
20972
|
+
const SELF = isPython ? "self" : "this";
|
|
20973
|
+
const javaHttpPattern = /\b(?:req|request|httpRequest|servletRequest|httpServletRequest)\.(?:getParameter|getParameterValues|getParameterMap|getHeader|getHeaders|getCookies|getQueryString|getPathInfo|getRequestURI|getRequestURL|getInputStream|getReader)\b/;
|
|
20974
|
+
const fieldAssignRe = new RegExp(`^\\s*${SELF}\\.([A-Za-z_]\\w*)\\s*=\\s*(.+?)(?:;\\s*)?$`);
|
|
20975
|
+
const commentPrefix = isPython ? "#" : "//";
|
|
20976
|
+
for (const type of types) {
|
|
20977
|
+
if (type.kind !== "class")
|
|
20978
|
+
continue;
|
|
20979
|
+
if (type.name === "<module>")
|
|
20980
|
+
continue;
|
|
20981
|
+
let ctor;
|
|
20982
|
+
for (const m of type.methods) {
|
|
20983
|
+
if (isPython) {
|
|
20984
|
+
if (m.name === "__init__") {
|
|
20985
|
+
ctor = m;
|
|
20986
|
+
break;
|
|
20987
|
+
}
|
|
20988
|
+
} else {
|
|
20989
|
+
if (m.name === type.name) {
|
|
20990
|
+
ctor = m;
|
|
20991
|
+
break;
|
|
20992
|
+
}
|
|
20993
|
+
}
|
|
20994
|
+
}
|
|
20995
|
+
if (!ctor)
|
|
20996
|
+
continue;
|
|
20997
|
+
const paramNames = new Set;
|
|
20998
|
+
for (const p of ctor.parameters) {
|
|
20999
|
+
if (p.name === "self" || p.name === "this")
|
|
21000
|
+
continue;
|
|
21001
|
+
paramNames.add(p.name);
|
|
21002
|
+
}
|
|
21003
|
+
const fieldTaint = new Map;
|
|
21004
|
+
const ctorStart = ctor.start_line;
|
|
21005
|
+
const ctorEnd = ctor.end_line;
|
|
21006
|
+
for (let i2 = ctorStart - 1;i2 < Math.min(ctorEnd, lines.length); i2++) {
|
|
21007
|
+
const line = lines[i2] ?? "";
|
|
21008
|
+
if (line.trim().startsWith(commentPrefix))
|
|
21009
|
+
continue;
|
|
21010
|
+
const m = line.match(fieldAssignRe);
|
|
21011
|
+
if (!m)
|
|
21012
|
+
continue;
|
|
21013
|
+
const fieldName = m[1];
|
|
21014
|
+
const rhs = m[2].trim().replace(/;\s*$/, "");
|
|
21015
|
+
let sourceType = null;
|
|
21016
|
+
if (paramNames.has(rhs)) {
|
|
21017
|
+
sourceType = "interprocedural_param";
|
|
21018
|
+
} else if (!isPython && javaHttpPattern.test(rhs)) {
|
|
21019
|
+
sourceType = "http_param";
|
|
21020
|
+
} else if (isPython) {
|
|
21021
|
+
for (const { pattern, type: type2 } of PYTHON_TAINTED_PATTERNS2) {
|
|
21022
|
+
if (pattern.test(rhs)) {
|
|
21023
|
+
sourceType = type2;
|
|
21024
|
+
break;
|
|
21025
|
+
}
|
|
21026
|
+
}
|
|
21027
|
+
}
|
|
21028
|
+
if (sourceType) {
|
|
21029
|
+
fieldTaint.set(fieldName, { line: i2 + 1, type: sourceType });
|
|
21030
|
+
}
|
|
21031
|
+
}
|
|
21032
|
+
if (fieldTaint.size === 0)
|
|
21033
|
+
continue;
|
|
21034
|
+
for (const [fieldName, info2] of fieldTaint) {
|
|
21035
|
+
sources.push({
|
|
21036
|
+
type: info2.type,
|
|
21037
|
+
location: `${type.name}.${SELF}.${fieldName} (constructor-injected field, #78)`,
|
|
21038
|
+
severity: "high",
|
|
21039
|
+
line: info2.line,
|
|
21040
|
+
confidence: 0.85,
|
|
21041
|
+
variable: `${SELF}.${fieldName}`
|
|
21042
|
+
});
|
|
21043
|
+
}
|
|
21044
|
+
for (const m of type.methods) {
|
|
21045
|
+
if (m === ctor)
|
|
21046
|
+
continue;
|
|
21047
|
+
const nonSelfParams = m.parameters.filter((p) => p.name !== "self" && p.name !== "this");
|
|
21048
|
+
if (nonSelfParams.length !== 0)
|
|
21049
|
+
continue;
|
|
21050
|
+
const mStart = m.start_line;
|
|
21051
|
+
const mEnd = m.end_line;
|
|
21052
|
+
let returnedField = null;
|
|
21053
|
+
let returnStatementCount = 0;
|
|
21054
|
+
const returnRe = new RegExp(`\\breturn\\s+${SELF}\\.([A-Za-z_]\\w*)\\s*[;}]?`);
|
|
21055
|
+
for (let i2 = mStart - 1;i2 < Math.min(mEnd, lines.length); i2++) {
|
|
21056
|
+
const raw = lines[i2] ?? "";
|
|
21057
|
+
const trimmed = raw.trim();
|
|
21058
|
+
if (!trimmed)
|
|
21059
|
+
continue;
|
|
21060
|
+
if (trimmed.startsWith(commentPrefix))
|
|
21061
|
+
continue;
|
|
21062
|
+
const rm = trimmed.match(returnRe);
|
|
21063
|
+
if (rm) {
|
|
21064
|
+
returnedField = rm[1];
|
|
21065
|
+
returnStatementCount++;
|
|
21066
|
+
} else if (/\breturn\b/.test(trimmed)) {
|
|
21067
|
+
returnStatementCount = 99;
|
|
21068
|
+
break;
|
|
21069
|
+
}
|
|
21070
|
+
}
|
|
21071
|
+
if (returnStatementCount === 1 && returnedField && fieldTaint.has(returnedField)) {
|
|
21072
|
+
const fieldInfo = fieldTaint.get(returnedField);
|
|
21073
|
+
const getterVar = isPython ? `${SELF}.${m.name}` : m.name;
|
|
21074
|
+
sources.push({
|
|
21075
|
+
type: fieldInfo.type,
|
|
21076
|
+
location: `${type.name}.${m.name} returns tainted field '${returnedField}' (#78)`,
|
|
21077
|
+
severity: "high",
|
|
21078
|
+
line: m.start_line,
|
|
21079
|
+
confidence: 0.85,
|
|
21080
|
+
variable: getterVar
|
|
21081
|
+
});
|
|
21082
|
+
}
|
|
21083
|
+
}
|
|
21084
|
+
}
|
|
21085
|
+
return sources;
|
|
21086
|
+
}
|
|
20746
21087
|
function findJavaScriptAssignmentSources(sourceCode, language) {
|
|
20747
21088
|
if (!["javascript", "typescript"].includes(language))
|
|
20748
21089
|
return [];
|
|
@@ -20820,62 +21161,62 @@ function buildPythonTaintedVars(sourceCode) {
|
|
|
20820
21161
|
const line = lines[i2];
|
|
20821
21162
|
if (line.trimStart().startsWith("#"))
|
|
20822
21163
|
continue;
|
|
20823
|
-
const subscriptAssign = line.match(/^\s*(\
|
|
21164
|
+
const subscriptAssign = line.match(/^\s*([\p{L}\p{N}_]+)\[(['"])([^'"]+)\2\]\s*=\s*(.+)$/u);
|
|
20824
21165
|
if (subscriptAssign) {
|
|
20825
21166
|
const [, container, , key, rhs2] = subscriptAssign;
|
|
20826
|
-
const isTaintedRhs = [...tainted.keys()].some((v) => new RegExp(
|
|
21167
|
+
const isTaintedRhs = [...tainted.keys()].some((v) => new RegExp(`(?<![\\p{L}\\p{N}_])${v}(?![\\p{L}\\p{N}_])`, "u").test(rhs2));
|
|
20827
21168
|
if (isTaintedRhs)
|
|
20828
21169
|
containerTainted.set(`${container}['${key}']`, i2 + 1);
|
|
20829
21170
|
continue;
|
|
20830
21171
|
}
|
|
20831
|
-
const setCallMatch = line.match(/^\s*(\
|
|
21172
|
+
const setCallMatch = line.match(/^\s*([\p{L}\p{N}_]+)\.set\s*\(\s*(['"])([^'"]+)\2\s*,\s*(['"])([^'"]+)\4\s*,\s*(.+?)\s*\)$/u);
|
|
20832
21173
|
if (setCallMatch) {
|
|
20833
21174
|
const [, obj, , section, , key, rhs2] = setCallMatch;
|
|
20834
|
-
const isTaintedRhs = [...tainted.keys()].some((v) => new RegExp(
|
|
21175
|
+
const isTaintedRhs = [...tainted.keys()].some((v) => new RegExp(`(?<![\\p{L}\\p{N}_])${v}(?![\\p{L}\\p{N}_])`, "u").test(rhs2));
|
|
20835
21176
|
if (isTaintedRhs)
|
|
20836
21177
|
containerTainted.set(`${obj}['${section}']['${key}']`, i2 + 1);
|
|
20837
21178
|
continue;
|
|
20838
21179
|
}
|
|
20839
|
-
const containerAppendMatch = line.match(/^\s*(\
|
|
21180
|
+
const containerAppendMatch = line.match(/^\s*([\p{L}\p{N}_]+)\.(append|extend|insert|add|push|put|appendleft)\s*\(\s*(.+?)\s*\)\s*$/u);
|
|
20840
21181
|
if (containerAppendMatch) {
|
|
20841
21182
|
const [, receiver, , argExpr] = containerAppendMatch;
|
|
20842
|
-
const argIsTainted = [...tainted.keys()].some((v) => new RegExp(
|
|
21183
|
+
const argIsTainted = [...tainted.keys()].some((v) => new RegExp(`(?<![\\p{L}\\p{N}_])${v}(?![\\p{L}\\p{N}_])`, "u").test(argExpr));
|
|
20843
21184
|
const argIsDirectSource = PYTHON_TAINTED_PATTERNS2.some((p) => p.pattern.test(argExpr));
|
|
20844
21185
|
if (argIsTainted || argIsDirectSource)
|
|
20845
21186
|
tainted.set(receiver, tainted.get(receiver) ?? i2 + 1);
|
|
20846
21187
|
continue;
|
|
20847
21188
|
}
|
|
20848
|
-
const augAssign = line.match(/^\s*(\
|
|
21189
|
+
const augAssign = line.match(/^\s*([\p{L}\p{N}_]+)\s*\+=\s*(.+)$/u);
|
|
20849
21190
|
if (augAssign) {
|
|
20850
21191
|
const [, augLhs, augRhs] = augAssign;
|
|
20851
|
-
const rhsTainted = [...tainted.keys()].some((v) => new RegExp(
|
|
21192
|
+
const rhsTainted = [...tainted.keys()].some((v) => new RegExp(`(?<![\\p{L}\\p{N}_])${v}(?![\\p{L}\\p{N}_])`, "u").test(augRhs));
|
|
20852
21193
|
if (rhsTainted || tainted.has(augLhs))
|
|
20853
21194
|
tainted.set(augLhs, tainted.get(augLhs) ?? i2 + 1);
|
|
20854
21195
|
continue;
|
|
20855
21196
|
}
|
|
20856
|
-
const forLoopMatch = line.match(/^\s*for\s+(\
|
|
21197
|
+
const forLoopMatch = line.match(/^\s*for\s+([\p{L}\p{N}_]+)\s+in\s+(.+?)(?:\s*:\s*)?$/u);
|
|
20857
21198
|
if (forLoopMatch) {
|
|
20858
21199
|
const [, iterVar, iterExpr] = forLoopMatch;
|
|
20859
21200
|
const isDirectSource2 = PYTHON_TAINTED_PATTERNS2.some((p) => p.pattern.test(iterExpr));
|
|
20860
|
-
const isPropagated = [...tainted.keys()].some((v) => new RegExp(
|
|
21201
|
+
const isPropagated = [...tainted.keys()].some((v) => new RegExp(`(?<![\\p{L}\\p{N}_])${v}(?![\\p{L}\\p{N}_])`, "u").test(iterExpr));
|
|
20861
21202
|
if (isDirectSource2 || isPropagated)
|
|
20862
21203
|
tainted.set(iterVar, i2 + 1);
|
|
20863
21204
|
continue;
|
|
20864
21205
|
}
|
|
20865
|
-
const assignMatch = line.match(/^\s*(\
|
|
21206
|
+
const assignMatch = line.match(/^\s*([\p{L}\p{N}_]+)\s*=\s*(.+)$/u);
|
|
20866
21207
|
if (!assignMatch)
|
|
20867
21208
|
continue;
|
|
20868
21209
|
const [, lhs, rhs] = assignMatch;
|
|
20869
21210
|
const isDirectSource = PYTHON_TAINTED_PATTERNS2.some((p) => p.pattern.test(rhs));
|
|
20870
21211
|
let propagatedFrom;
|
|
20871
|
-
const dictAccessMatch = rhs.trim().match(/^(\
|
|
21212
|
+
const dictAccessMatch = rhs.trim().match(/^([\p{L}\p{N}_]+)\[(['"])([^'"]+)\2\]$/u);
|
|
20872
21213
|
if (dictAccessMatch) {
|
|
20873
21214
|
const [, container, , key] = dictAccessMatch;
|
|
20874
21215
|
if (containerTainted.has(`${container}['${key}']`))
|
|
20875
21216
|
propagatedFrom = `${container}['${key}']`;
|
|
20876
21217
|
}
|
|
20877
21218
|
if (!propagatedFrom) {
|
|
20878
|
-
const confGetMatch = rhs.trim().match(/^(\
|
|
21219
|
+
const confGetMatch = rhs.trim().match(/^([\p{L}\p{N}_]+)\.get\s*\(\s*(['"])([^'"]+)\2\s*,\s*(['"])([^'"]+)\4\s*\)$/u);
|
|
20879
21220
|
if (confGetMatch) {
|
|
20880
21221
|
const [, obj, , section, , key] = confGetMatch;
|
|
20881
21222
|
if (containerTainted.has(`${obj}['${section}']['${key}']`))
|
|
@@ -20885,7 +21226,7 @@ function buildPythonTaintedVars(sourceCode) {
|
|
|
20885
21226
|
if (!propagatedFrom) {
|
|
20886
21227
|
const isSafeEnvRead = /\bos\.environ\.get\s*\(/.test(rhs) || /\bos\.getenv\s*\(/.test(rhs);
|
|
20887
21228
|
if (!isSafeEnvRead)
|
|
20888
|
-
propagatedFrom = [...tainted.keys()].find((v) => new RegExp(
|
|
21229
|
+
propagatedFrom = [...tainted.keys()].find((v) => new RegExp(`(?<![\\p{L}\\p{N}_])${v}(?![\\p{L}\\p{N}_])`, "u").test(rhs));
|
|
20889
21230
|
}
|
|
20890
21231
|
if (isDirectSource) {
|
|
20891
21232
|
tainted.set(lhs, i2 + 1);
|
|
@@ -21290,6 +21631,61 @@ function findBashPatternFindings(sourceCode, file) {
|
|
|
21290
21631
|
}
|
|
21291
21632
|
return findings;
|
|
21292
21633
|
}
|
|
21634
|
+
function findBashRegexAllowlistSanitizers(code) {
|
|
21635
|
+
const sanitizers = [];
|
|
21636
|
+
const lines = code.split(`
|
|
21637
|
+
`);
|
|
21638
|
+
const guardRe = /^\s*if\s+\[\[\s*!\s*"?\$\{?(\w+)\}?"?\s*=~\s*(\S+)\s*\]\]\s*;\s*then\s+(exit|return|die)\b/;
|
|
21639
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
21640
|
+
const m = guardRe.exec(lines[i2]);
|
|
21641
|
+
if (!m)
|
|
21642
|
+
continue;
|
|
21643
|
+
const regexLiteral = m[2];
|
|
21644
|
+
if (!isSafeBashAllowlistRegex(regexLiteral))
|
|
21645
|
+
continue;
|
|
21646
|
+
const ifLine1Indexed = i2 + 1;
|
|
21647
|
+
for (let l = ifLine1Indexed + 1;l <= lines.length; l++) {
|
|
21648
|
+
sanitizers.push({
|
|
21649
|
+
type: "regex_allowlist",
|
|
21650
|
+
method: "=~",
|
|
21651
|
+
line: l,
|
|
21652
|
+
sanitizes: [
|
|
21653
|
+
"command_injection",
|
|
21654
|
+
"path_traversal",
|
|
21655
|
+
"sql_injection",
|
|
21656
|
+
"code_injection",
|
|
21657
|
+
"ssrf",
|
|
21658
|
+
"xss",
|
|
21659
|
+
"open_redirect",
|
|
21660
|
+
"log_injection"
|
|
21661
|
+
]
|
|
21662
|
+
});
|
|
21663
|
+
}
|
|
21664
|
+
}
|
|
21665
|
+
return sanitizers;
|
|
21666
|
+
}
|
|
21667
|
+
function isSafeBashAllowlistRegex(literal) {
|
|
21668
|
+
if (!literal.startsWith("^") || !literal.endsWith("$"))
|
|
21669
|
+
return false;
|
|
21670
|
+
const body2 = literal.slice(1, -1);
|
|
21671
|
+
if (body2.length === 0)
|
|
21672
|
+
return false;
|
|
21673
|
+
if (body2.includes(".*") || body2.includes(".+"))
|
|
21674
|
+
return false;
|
|
21675
|
+
if (body2.includes("|"))
|
|
21676
|
+
return false;
|
|
21677
|
+
if (/\\\d/.test(body2))
|
|
21678
|
+
return false;
|
|
21679
|
+
const safeToken = /\[[^\]]+\][+*?]?|\\.|[A-Za-z0-9_\-./]|[+*?]/g;
|
|
21680
|
+
let consumed = 0;
|
|
21681
|
+
let match;
|
|
21682
|
+
while ((match = safeToken.exec(body2)) !== null) {
|
|
21683
|
+
if (match.index !== consumed)
|
|
21684
|
+
return false;
|
|
21685
|
+
consumed += match[0].length;
|
|
21686
|
+
}
|
|
21687
|
+
return consumed === body2.length;
|
|
21688
|
+
}
|
|
21293
21689
|
|
|
21294
21690
|
// ../circle-ir/dist/analysis/passes/sink-filter-pass.js
|
|
21295
21691
|
var JS_XSS_SANITIZERS = [
|
|
@@ -21326,7 +21722,10 @@ class SinkFilterPass {
|
|
|
21326
21722
|
sinks.push(s);
|
|
21327
21723
|
}
|
|
21328
21724
|
}
|
|
21329
|
-
const sanitizers =
|
|
21725
|
+
const sanitizers = [
|
|
21726
|
+
...taintMatcher.sanitizers,
|
|
21727
|
+
...langSources.additionalSanitizers ?? []
|
|
21728
|
+
];
|
|
21330
21729
|
let filtered = sinks.filter((sink) => !constProp.unreachableLines.has(sink.line));
|
|
21331
21730
|
filtered = filterCleanArraySinks(filtered, calls, constProp.taintedArrayElements, constProp.symbols);
|
|
21332
21731
|
filtered = filterCleanVariableSinks(filtered, calls, constProp.tainted, constProp.symbols, dfg, constProp.sanitizedVars, constProp.synchronizedLines, language);
|
|
@@ -21811,6 +22210,21 @@ function findInitialTaint(sources, callsByLine, defsByLine) {
|
|
|
21811
22210
|
});
|
|
21812
22211
|
}
|
|
21813
22212
|
}
|
|
22213
|
+
if (source.variable) {
|
|
22214
|
+
const paramDefs = defsByLine.get(0) ?? [];
|
|
22215
|
+
for (const def of paramDefs) {
|
|
22216
|
+
if (def.kind === "param" && def.variable === source.variable) {
|
|
22217
|
+
tainted.push({
|
|
22218
|
+
variable: def.variable,
|
|
22219
|
+
defId: def.id,
|
|
22220
|
+
line: def.line,
|
|
22221
|
+
sourceType: source.type,
|
|
22222
|
+
sourceLine: source.line,
|
|
22223
|
+
confidence: source.confidence
|
|
22224
|
+
});
|
|
22225
|
+
}
|
|
22226
|
+
}
|
|
22227
|
+
}
|
|
21814
22228
|
}
|
|
21815
22229
|
return tainted;
|
|
21816
22230
|
}
|
|
@@ -22384,7 +22798,7 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
22384
22798
|
if (reCache.has(s.variable))
|
|
22385
22799
|
continue;
|
|
22386
22800
|
const escaped = s.variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22387
|
-
reCache.set(s.variable, new RegExp(
|
|
22801
|
+
reCache.set(s.variable, new RegExp(`(?<![\\p{L}\\p{N}_])${escaped}(?![\\p{L}\\p{N}_])`, "u"));
|
|
22388
22802
|
}
|
|
22389
22803
|
const callsByLine = new Map;
|
|
22390
22804
|
for (const call of calls) {
|
|
@@ -22526,6 +22940,13 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
|
|
|
22526
22940
|
for (const def of graph.defsAtLine(source.line)) {
|
|
22527
22941
|
seedIds.add(def.id);
|
|
22528
22942
|
}
|
|
22943
|
+
if (source.variable) {
|
|
22944
|
+
for (const def of graph.defsAtLine(0)) {
|
|
22945
|
+
if (def.kind === "param" && def.variable === source.variable) {
|
|
22946
|
+
seedIds.add(def.id);
|
|
22947
|
+
}
|
|
22948
|
+
}
|
|
22949
|
+
}
|
|
22529
22950
|
}
|
|
22530
22951
|
const taintedDefIds = graph.propagateTaintedDefIds(seedIds);
|
|
22531
22952
|
const taintedVarsFromCP = options.taintedVariables ?? new Set;
|
|
@@ -22666,7 +23087,34 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
|
|
|
22666
23087
|
const targetMethod = getMethodNode(methodNodes, call.method_name);
|
|
22667
23088
|
if (!targetMethod) {
|
|
22668
23089
|
if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name)) {
|
|
22669
|
-
const
|
|
23090
|
+
const isBash = graph.ir.meta.language === "bash";
|
|
23091
|
+
const bashSafeBuiltins = new Set([
|
|
23092
|
+
"echo",
|
|
23093
|
+
"printf",
|
|
23094
|
+
"test",
|
|
23095
|
+
"[",
|
|
23096
|
+
"[[",
|
|
23097
|
+
"true",
|
|
23098
|
+
"false",
|
|
23099
|
+
":",
|
|
23100
|
+
"declare",
|
|
23101
|
+
"local",
|
|
23102
|
+
"export",
|
|
23103
|
+
"readonly",
|
|
23104
|
+
"typeset"
|
|
23105
|
+
]);
|
|
23106
|
+
if (isBash && bashSafeBuiltins.has(call.method_name)) {
|
|
23107
|
+
continue;
|
|
23108
|
+
}
|
|
23109
|
+
const sink = isBash ? {
|
|
23110
|
+
type: "command_injection",
|
|
23111
|
+
cwe: "CWE-78",
|
|
23112
|
+
location: `Tainted data (${taintedArgVars.join(", ")}) passed unquoted to shell utility ${call.method_name}`,
|
|
23113
|
+
line: call.location.line,
|
|
23114
|
+
confidence: 0.6,
|
|
23115
|
+
method: call.method_name,
|
|
23116
|
+
argPositions: taintedArgPositions
|
|
23117
|
+
} : {
|
|
22670
23118
|
type: "external_taint_escape",
|
|
22671
23119
|
cwe: "CWE-668",
|
|
22672
23120
|
location: `Tainted data (${taintedArgVars.join(", ")}) passed to external method ${call.receiver ? call.receiver + "." : ""}${call.method_name}()`,
|
|
@@ -23346,6 +23794,7 @@ function isPublicMethod(method, language) {
|
|
|
23346
23794
|
return method.modifiers.includes("public");
|
|
23347
23795
|
case "javascript":
|
|
23348
23796
|
case "typescript":
|
|
23797
|
+
case "tsx":
|
|
23349
23798
|
return !method.modifiers.includes("private") && !method.modifiers.includes("protected");
|
|
23350
23799
|
case "python":
|
|
23351
23800
|
return !method.name.startsWith("_");
|
|
@@ -23365,7 +23814,7 @@ class MissingPublicDocPass {
|
|
|
23365
23814
|
if (UTIL_DIR_RE.test(graph.ir.meta.file)) {
|
|
23366
23815
|
return { missingDocMethods: [], missingDocTypes: [] };
|
|
23367
23816
|
}
|
|
23368
|
-
if (!["java", "javascript", "typescript", "python"].includes(language)) {
|
|
23817
|
+
if (!["java", "javascript", "typescript", "tsx", "python"].includes(language)) {
|
|
23369
23818
|
return { missingDocMethods: [], missingDocTypes: [] };
|
|
23370
23819
|
}
|
|
23371
23820
|
const lines = code.split(`
|
|
@@ -23889,6 +24338,7 @@ function hasDeclKeyword(lineText, language) {
|
|
|
23889
24338
|
return /\b(?:int|long|float|double|boolean|byte|char|short|var|final)\b/.test(lineText) || /\b[A-Z]\w*(?:<[^>]*>)?\s+\w/.test(lineText);
|
|
23890
24339
|
case "javascript":
|
|
23891
24340
|
case "typescript":
|
|
24341
|
+
case "tsx":
|
|
23892
24342
|
return /\b(?:let|const|var)\s+[\w{[]/.test(lineText);
|
|
23893
24343
|
case "rust":
|
|
23894
24344
|
return /\blet\s+(?:mut\s+)?\w/.test(lineText);
|
|
@@ -28395,6 +28845,7 @@ class WeakRandomPass {
|
|
|
28395
28845
|
return "Use the `secrets` module (`secrets.token_bytes`, " + "`secrets.token_hex`, `secrets.choice`, `secrets.randbelow`).";
|
|
28396
28846
|
case "javascript":
|
|
28397
28847
|
case "typescript":
|
|
28848
|
+
case "tsx":
|
|
28398
28849
|
return "Use `crypto.randomBytes(n)` (Node.js) or " + "`crypto.getRandomValues(typedArray)` (browser).";
|
|
28399
28850
|
case "go":
|
|
28400
28851
|
return "Use `crypto/rand` instead of `math/rand`. Example: " + "`b := make([]byte, 32); _, _ = rand.Read(b)` (where `rand` is `crypto/rand`).";
|
|
@@ -28433,7 +28884,7 @@ class WeakRandomPass {
|
|
|
28433
28884
|
}
|
|
28434
28885
|
return null;
|
|
28435
28886
|
}
|
|
28436
|
-
if (language === "javascript" || language === "typescript") {
|
|
28887
|
+
if (language === "javascript" || language === "typescript" || language === "tsx") {
|
|
28437
28888
|
if (method === "random" && (receiver === "Math" || receiver.endsWith(".Math"))) {
|
|
28438
28889
|
return "Math.random";
|
|
28439
28890
|
}
|
|
@@ -30035,6 +30486,7 @@ function getNodeTypesForLanguage(language) {
|
|
|
30035
30486
|
]);
|
|
30036
30487
|
case "javascript":
|
|
30037
30488
|
case "typescript":
|
|
30489
|
+
case "tsx":
|
|
30038
30490
|
return new Set([
|
|
30039
30491
|
"call_expression",
|
|
30040
30492
|
"new_expression",
|
|
@@ -30047,7 +30499,12 @@ function getNodeTypesForLanguage(language) {
|
|
|
30047
30499
|
"import_statement",
|
|
30048
30500
|
"export_statement",
|
|
30049
30501
|
"member_expression",
|
|
30050
|
-
"assignment_expression"
|
|
30502
|
+
"assignment_expression",
|
|
30503
|
+
"jsx_element",
|
|
30504
|
+
"jsx_self_closing_element",
|
|
30505
|
+
"jsx_opening_element",
|
|
30506
|
+
"jsx_attribute",
|
|
30507
|
+
"jsx_expression"
|
|
30051
30508
|
]);
|
|
30052
30509
|
case "bash":
|
|
30053
30510
|
return new Set([
|
|
@@ -30111,8 +30568,15 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
30111
30568
|
if (language === "html") {
|
|
30112
30569
|
return analyzeHtmlFile(code, filePath, options);
|
|
30113
30570
|
}
|
|
30114
|
-
|
|
30115
|
-
|
|
30571
|
+
let parseGrammar = language;
|
|
30572
|
+
if (language === "javascript" || language === "typescript") {
|
|
30573
|
+
const lower = filePath.toLowerCase();
|
|
30574
|
+
if (lower.endsWith(".tsx") || lower.endsWith(".jsx")) {
|
|
30575
|
+
parseGrammar = "tsx";
|
|
30576
|
+
}
|
|
30577
|
+
}
|
|
30578
|
+
logger.debug("Analyzing file", { filePath, language, parseGrammar, codeLength: code.length });
|
|
30579
|
+
const tree = await parse(code, parseGrammar);
|
|
30116
30580
|
try {
|
|
30117
30581
|
logger.trace("Parsed AST", { rootNodeType: tree.rootNode.type });
|
|
30118
30582
|
const parseStatus = extractParseStatus(tree);
|
|
@@ -30440,7 +30904,7 @@ var colors = {
|
|
|
30440
30904
|
};
|
|
30441
30905
|
|
|
30442
30906
|
// src/version.ts
|
|
30443
|
-
var version = "3.
|
|
30907
|
+
var version = "3.62.0";
|
|
30444
30908
|
|
|
30445
30909
|
// src/formatters.ts
|
|
30446
30910
|
var SINK_SEVERITY = {
|
|
@@ -30463,7 +30927,11 @@ var SINK_SEVERITY = {
|
|
|
30463
30927
|
insecure_cookie: "low",
|
|
30464
30928
|
trust_boundary: "medium",
|
|
30465
30929
|
external_taint_escape: "medium",
|
|
30466
|
-
mybatis_mapper_call: "medium"
|
|
30930
|
+
mybatis_mapper_call: "medium",
|
|
30931
|
+
redos: "medium",
|
|
30932
|
+
format_string: "high",
|
|
30933
|
+
crlf: "medium",
|
|
30934
|
+
mass_assignment: "high"
|
|
30467
30935
|
};
|
|
30468
30936
|
var SINK_CWE = {
|
|
30469
30937
|
sql_injection: "CWE-89",
|
|
@@ -30485,7 +30953,11 @@ var SINK_CWE = {
|
|
|
30485
30953
|
insecure_cookie: "CWE-614",
|
|
30486
30954
|
trust_boundary: "CWE-501",
|
|
30487
30955
|
external_taint_escape: "CWE-20",
|
|
30488
|
-
mybatis_mapper_call: "CWE-89"
|
|
30956
|
+
mybatis_mapper_call: "CWE-89",
|
|
30957
|
+
redos: "CWE-1333",
|
|
30958
|
+
format_string: "CWE-134",
|
|
30959
|
+
crlf: "CWE-113",
|
|
30960
|
+
mass_assignment: "CWE-915"
|
|
30489
30961
|
};
|
|
30490
30962
|
var VULNERABILITY_HELP = {
|
|
30491
30963
|
sql_injection: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.62.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.62.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|