cognium-dev 3.141.0 → 3.146.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 +531 -20
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -5668,6 +5668,12 @@ function resolveReceiverType(receiver, context) {
|
|
|
5668
5668
|
}
|
|
5669
5669
|
if (receiver === "super")
|
|
5670
5670
|
return { simpleName: null, fqn: null };
|
|
5671
|
+
const ctorMatch = receiver.match(/^new\s+([A-Za-z_$][\w$.]*)\s*[<(]/);
|
|
5672
|
+
if (ctorMatch) {
|
|
5673
|
+
const ctorClass = ctorMatch[1];
|
|
5674
|
+
const simple = ctorClass.includes(".") ? ctorClass.substring(ctorClass.lastIndexOf(".") + 1) : ctorClass;
|
|
5675
|
+
return resolveFqn(simple, context);
|
|
5676
|
+
}
|
|
5671
5677
|
const declaredType = context.localVarTypes.get(receiver) ?? context.paramTypes.get(receiver) ?? context.fieldTypes.get(receiver);
|
|
5672
5678
|
if (declaredType) {
|
|
5673
5679
|
return resolveFqn(stripGenerics(declaredType), context);
|
|
@@ -10732,6 +10738,8 @@ var DEFAULT_SINKS = [
|
|
|
10732
10738
|
{ method: "sendError", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
10733
10739
|
{ method: "setHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
10734
10740
|
{ method: "addHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
10741
|
+
{ method: "Cookie", class: "constructor", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0, 1] },
|
|
10742
|
+
{ method: "addCookie", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0] },
|
|
10735
10743
|
{ method: "setContentType", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10736
10744
|
{ method: "setAttribute", class: "PageContext", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
10737
10745
|
{ method: "addAttribute", class: "Model", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
@@ -11668,11 +11676,62 @@ var DEFAULT_SANITIZERS = [
|
|
|
11668
11676
|
{ method: "UUID", class: "uuid", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11669
11677
|
{ method: "Decimal", class: "decimal", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] }
|
|
11670
11678
|
];
|
|
11679
|
+
var DEFAULT_SINK_SEMANTICS = [
|
|
11680
|
+
{
|
|
11681
|
+
signature: "Jedis#executeCommand",
|
|
11682
|
+
real_class: "db_protocol",
|
|
11683
|
+
overrides: ["command_injection", "code_injection"],
|
|
11684
|
+
note: "Redis wire-protocol serialization, not OS exec"
|
|
11685
|
+
},
|
|
11686
|
+
{
|
|
11687
|
+
signature: "Connection#executeCommand",
|
|
11688
|
+
real_class: "db_protocol",
|
|
11689
|
+
overrides: ["command_injection", "code_injection"],
|
|
11690
|
+
note: "Jedis abstract Connection base"
|
|
11691
|
+
},
|
|
11692
|
+
{
|
|
11693
|
+
signature: "JedisCluster#executeCommand",
|
|
11694
|
+
real_class: "db_protocol",
|
|
11695
|
+
overrides: ["command_injection", "code_injection"],
|
|
11696
|
+
note: "Jedis cluster client"
|
|
11697
|
+
},
|
|
11698
|
+
{
|
|
11699
|
+
signature: "Func1#exec",
|
|
11700
|
+
real_class: "functional_dispatch",
|
|
11701
|
+
overrides: ["command_injection", "code_injection"],
|
|
11702
|
+
note: "RxJava functional dispatch, not OS exec"
|
|
11703
|
+
},
|
|
11704
|
+
{
|
|
11705
|
+
signature: "Action0#call",
|
|
11706
|
+
real_class: "functional_dispatch",
|
|
11707
|
+
overrides: ["command_injection"],
|
|
11708
|
+
note: "RxJava Action0 dispatch"
|
|
11709
|
+
},
|
|
11710
|
+
{
|
|
11711
|
+
signature: "Action1#call",
|
|
11712
|
+
real_class: "functional_dispatch",
|
|
11713
|
+
overrides: ["command_injection"],
|
|
11714
|
+
note: "RxJava Action1 dispatch"
|
|
11715
|
+
},
|
|
11716
|
+
{
|
|
11717
|
+
signature: "Unsafe#defineAnonymousClass",
|
|
11718
|
+
real_class: "jdk_internal",
|
|
11719
|
+
overrides: ["code_injection"],
|
|
11720
|
+
note: "sun.misc.Unsafe JDK-internal reflective bridge"
|
|
11721
|
+
},
|
|
11722
|
+
{
|
|
11723
|
+
signature: "MethodHandle#invokeExact",
|
|
11724
|
+
real_class: "jdk_internal",
|
|
11725
|
+
overrides: ["code_injection"],
|
|
11726
|
+
note: "java.lang.invoke.MethodHandle — JDK-internal"
|
|
11727
|
+
}
|
|
11728
|
+
];
|
|
11671
11729
|
function getDefaultConfig() {
|
|
11672
11730
|
return {
|
|
11673
11731
|
sources: DEFAULT_SOURCES,
|
|
11674
11732
|
sinks: DEFAULT_SINKS,
|
|
11675
|
-
sanitizers: DEFAULT_SANITIZERS
|
|
11733
|
+
sanitizers: DEFAULT_SANITIZERS,
|
|
11734
|
+
sinkSemantics: DEFAULT_SINK_SEMANTICS
|
|
11676
11735
|
};
|
|
11677
11736
|
}
|
|
11678
11737
|
var DEFAULT_HEADER_RULES = [
|
|
@@ -12001,7 +12060,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
12001
12060
|
severity: "medium",
|
|
12002
12061
|
line: paramLine,
|
|
12003
12062
|
confidence: param.type ? 0.7 : 0.5,
|
|
12004
|
-
in_method: method.name
|
|
12063
|
+
in_method: method.name,
|
|
12064
|
+
...language === "java" ? { variable: param.name } : {}
|
|
12005
12065
|
});
|
|
12006
12066
|
}
|
|
12007
12067
|
}
|
|
@@ -12499,7 +12559,19 @@ function isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines) {
|
|
|
12499
12559
|
return false;
|
|
12500
12560
|
}
|
|
12501
12561
|
var TEMPLATE_LITERAL_RECEIVER_RE = /^Template\(\s*(?:"[^"\\]*"|'[^'\\]*')\s*\)$/;
|
|
12502
|
-
|
|
12562
|
+
var JINJA_AUTOESCAPE_TRUE_RE = /\bEnvironment\s*\([^)]*\bautoescape\s*=\s*True\b/;
|
|
12563
|
+
var JINJA_SELECT_AUTOESCAPE_RE = /\bEnvironment\s*\([^)]*\bautoescape\s*=\s*select_autoescape\s*\(/;
|
|
12564
|
+
var JINJA_AUTOESCAPE_FALSE_RE = /\bEnvironment\s*\([^)]*\bautoescape\s*=\s*False\b/;
|
|
12565
|
+
function fileHasSafeJinjaEnvironment(sourceLines) {
|
|
12566
|
+
if (!sourceLines || sourceLines.length === 0)
|
|
12567
|
+
return false;
|
|
12568
|
+
const text = sourceLines.join(`
|
|
12569
|
+
`);
|
|
12570
|
+
if (JINJA_AUTOESCAPE_FALSE_RE.test(text))
|
|
12571
|
+
return false;
|
|
12572
|
+
return JINJA_AUTOESCAPE_TRUE_RE.test(text) || JINJA_SELECT_AUTOESCAPE_RE.test(text);
|
|
12573
|
+
}
|
|
12574
|
+
function isSafeJinjaRenderCall(call, pattern, language, sourceLines) {
|
|
12503
12575
|
if (language !== "python")
|
|
12504
12576
|
return false;
|
|
12505
12577
|
if (pattern.type !== "xss" && pattern.type !== "code_injection")
|
|
@@ -12515,7 +12587,10 @@ function isSafeJinjaRenderCall(call, pattern, language) {
|
|
|
12515
12587
|
}
|
|
12516
12588
|
if (method === "render") {
|
|
12517
12589
|
const receiver = (call.receiver ?? "").trim();
|
|
12518
|
-
|
|
12590
|
+
if (TEMPLATE_LITERAL_RECEIVER_RE.test(receiver))
|
|
12591
|
+
return true;
|
|
12592
|
+
if (fileHasSafeJinjaEnvironment(sourceLines))
|
|
12593
|
+
return true;
|
|
12519
12594
|
}
|
|
12520
12595
|
return false;
|
|
12521
12596
|
}
|
|
@@ -12563,7 +12638,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
12563
12638
|
if (isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines)) {
|
|
12564
12639
|
continue;
|
|
12565
12640
|
}
|
|
12566
|
-
if (isSafeJinjaRenderCall(call, pattern, language)) {
|
|
12641
|
+
if (isSafeJinjaRenderCall(call, pattern, language, sourceLines)) {
|
|
12567
12642
|
continue;
|
|
12568
12643
|
}
|
|
12569
12644
|
const location = formatCallLocation(call);
|
|
@@ -12571,6 +12646,8 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
12571
12646
|
const confidence = calculateSinkConfidence(call, pattern);
|
|
12572
12647
|
const existing = sinkMap.get(key);
|
|
12573
12648
|
if (!existing || confidence > existing.confidence) {
|
|
12649
|
+
const receiverType = call.receiver_type;
|
|
12650
|
+
const simpleClass = receiverType ? receiverType.split(".").pop() || undefined : undefined;
|
|
12574
12651
|
sinkMap.set(key, {
|
|
12575
12652
|
type: pattern.type,
|
|
12576
12653
|
cwe: pattern.cwe,
|
|
@@ -12578,7 +12655,8 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
12578
12655
|
line: call.location.line,
|
|
12579
12656
|
confidence,
|
|
12580
12657
|
method: call.method_name,
|
|
12581
|
-
argPositions: pattern.arg_positions
|
|
12658
|
+
argPositions: pattern.arg_positions,
|
|
12659
|
+
class: simpleClass
|
|
12582
12660
|
});
|
|
12583
12661
|
}
|
|
12584
12662
|
}
|
|
@@ -13478,12 +13556,12 @@ function formatCallCode(call) {
|
|
|
13478
13556
|
// ../circle-ir/dist/analysis/findings.js
|
|
13479
13557
|
function canSourceReachSink(sourceType, sinkType) {
|
|
13480
13558
|
const sourceToSinkMapping = {
|
|
13481
|
-
http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary"],
|
|
13559
|
+
http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization"],
|
|
13482
13560
|
http_body: ["sql_injection", "command_injection", "deserialization", "xxe", "xss", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary"],
|
|
13483
13561
|
http_header: ["sql_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary"],
|
|
13484
13562
|
http_cookie: ["sql_injection", "xss", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary"],
|
|
13485
13563
|
http_path: ["path_traversal", "sql_injection", "ssrf", "mybatis_mapper_call", "open_redirect", "trust_boundary"],
|
|
13486
|
-
http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary"],
|
|
13564
|
+
http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization"],
|
|
13487
13565
|
io_input: ["command_injection", "path_traversal", "deserialization", "xxe", "code_injection", "xss", "ssrf"],
|
|
13488
13566
|
env_input: ["command_injection", "path_traversal"],
|
|
13489
13567
|
db_input: ["xss", "sql_injection"],
|
|
@@ -13496,6 +13574,15 @@ function canSourceReachSink(sourceType, sinkType) {
|
|
|
13496
13574
|
const validSinks = sourceToSinkMapping[sourceType];
|
|
13497
13575
|
return validSinks ? validSinks.includes(sinkType) : false;
|
|
13498
13576
|
}
|
|
13577
|
+
function sourceSemanticsAllowed(source, sinkType) {
|
|
13578
|
+
if (source.constant === true) {
|
|
13579
|
+
return false;
|
|
13580
|
+
}
|
|
13581
|
+
if (source.spi === true) {
|
|
13582
|
+
return sinkType === "code_injection";
|
|
13583
|
+
}
|
|
13584
|
+
return true;
|
|
13585
|
+
}
|
|
13499
13586
|
|
|
13500
13587
|
// ../circle-ir/dist/analysis/findings-instrumentation.js
|
|
13501
13588
|
var instrumentEnabled = false;
|
|
@@ -22781,6 +22868,8 @@ class LanguageSourcesPass {
|
|
|
22781
22868
|
additionalSanitizers.push(...findJsSsrfAllowlistGuardSanitizers(code));
|
|
22782
22869
|
additionalSanitizers.push(...findJsArgvFormExecSanitizers(code));
|
|
22783
22870
|
additionalSanitizers.push(...findJsParameterizedSqlSanitizers(code));
|
|
22871
|
+
additionalSanitizers.push(...findJsPathResolveStartsWithGuardSanitizers(code));
|
|
22872
|
+
additionalSanitizers.push(...findJsCommandAllowlistGuardSanitizers(code));
|
|
22784
22873
|
for (const finding of findJsPatternFindings(code, graph.ir.meta.file)) {
|
|
22785
22874
|
ctx.addFinding(finding);
|
|
22786
22875
|
}
|
|
@@ -23602,6 +23691,68 @@ function buildRustTaintedVars(sourceCode, seedVars) {
|
|
|
23602
23691
|
}
|
|
23603
23692
|
return derived;
|
|
23604
23693
|
}
|
|
23694
|
+
function buildJavaTaintedVars(sourceCode, seedVars) {
|
|
23695
|
+
const derived = new Map;
|
|
23696
|
+
const knownTainted = new Set(seedVars);
|
|
23697
|
+
const lines = sourceCode.split(`
|
|
23698
|
+
`);
|
|
23699
|
+
const declRe = /^\s*(?:public|private|protected|static|final|volatile|transient|\s)*\s*(?:[A-Za-z_][\w.]*(?:\s*<[^>]*>)?(?:\s*\[\s*\])*)\s+([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
23700
|
+
const assignRe = /^\s*([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
23701
|
+
const JAVA_KEYWORDS = new Set([
|
|
23702
|
+
"if",
|
|
23703
|
+
"else",
|
|
23704
|
+
"while",
|
|
23705
|
+
"for",
|
|
23706
|
+
"do",
|
|
23707
|
+
"switch",
|
|
23708
|
+
"case",
|
|
23709
|
+
"return",
|
|
23710
|
+
"throw",
|
|
23711
|
+
"try",
|
|
23712
|
+
"catch",
|
|
23713
|
+
"finally",
|
|
23714
|
+
"new",
|
|
23715
|
+
"this",
|
|
23716
|
+
"super",
|
|
23717
|
+
"break",
|
|
23718
|
+
"continue",
|
|
23719
|
+
"default",
|
|
23720
|
+
"class",
|
|
23721
|
+
"interface",
|
|
23722
|
+
"enum"
|
|
23723
|
+
]);
|
|
23724
|
+
let changed = true;
|
|
23725
|
+
let guard = 0;
|
|
23726
|
+
while (changed && guard < lines.length + 2) {
|
|
23727
|
+
changed = false;
|
|
23728
|
+
guard++;
|
|
23729
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
23730
|
+
const line = lines[i2];
|
|
23731
|
+
const trimmed = line.trimStart();
|
|
23732
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("*"))
|
|
23733
|
+
continue;
|
|
23734
|
+
const declMatch = declRe.exec(line);
|
|
23735
|
+
const assignMatch = !declMatch ? assignRe.exec(line) : null;
|
|
23736
|
+
const m = declMatch ?? assignMatch;
|
|
23737
|
+
if (!m)
|
|
23738
|
+
continue;
|
|
23739
|
+
const lhs = m[1];
|
|
23740
|
+
const rhs = m[2];
|
|
23741
|
+
if (JAVA_KEYWORDS.has(lhs))
|
|
23742
|
+
continue;
|
|
23743
|
+
if (knownTainted.has(lhs))
|
|
23744
|
+
continue;
|
|
23745
|
+
const escaped = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23746
|
+
const ref = [...knownTainted].some((v) => new RegExp(`(?<![\\p{L}\\p{N}_])${escaped(v)}(?![\\p{L}\\p{N}_])`, "u").test(rhs));
|
|
23747
|
+
if (ref) {
|
|
23748
|
+
derived.set(lhs, i2 + 1);
|
|
23749
|
+
knownTainted.add(lhs);
|
|
23750
|
+
changed = true;
|
|
23751
|
+
}
|
|
23752
|
+
}
|
|
23753
|
+
}
|
|
23754
|
+
return derived;
|
|
23755
|
+
}
|
|
23605
23756
|
var BASH_POSITIONAL_PARAMS = new Set(["1", "2", "3", "4", "5", "6", "7", "8", "9", "@", "*"]);
|
|
23606
23757
|
var BASH_UNTRUSTED_ENV_PATTERNS = [
|
|
23607
23758
|
/^USER_INPUT$/i,
|
|
@@ -23982,7 +24133,7 @@ function findBashRealpathPrefixGuardSanitizers(code) {
|
|
|
23982
24133
|
const caseOpen = /^\s*case\s+"?\$\{?\w+\}?"?\s+in\b/;
|
|
23983
24134
|
const esacClose = /^\s*esac\b/;
|
|
23984
24135
|
const armOpener = /^\s*([^)\s][^)]*?)\)/;
|
|
23985
|
-
const prefixArm = /^(?:"\$\{?\w+\}?"|"[^"]*"|\/[\w\-./]+|\$\{?\w+\}?|[\w\-./]+)(?:\/|\*)/;
|
|
24136
|
+
const prefixArm = /^(?:"\$\{?\w+\}?"|"[^"]*"|\/[\w\-./]+|\$\{?\w+\}?|https?:\/\/[\w\-.]+|[\w\-./]+)(?:\/|\*)/;
|
|
23986
24137
|
const catchAllArm = /^(?:\*|\\\*)$/;
|
|
23987
24138
|
let i2 = 0;
|
|
23988
24139
|
while (i2 < lines.length) {
|
|
@@ -24838,6 +24989,126 @@ function findJsParameterizedSqlSanitizers(code) {
|
|
|
24838
24989
|
}
|
|
24839
24990
|
return sanitizers;
|
|
24840
24991
|
}
|
|
24992
|
+
function findJsPathResolveStartsWithGuardSanitizers(code) {
|
|
24993
|
+
const sanitizers = [];
|
|
24994
|
+
const lines = code.split(`
|
|
24995
|
+
`);
|
|
24996
|
+
const resolveDeclRe = /\b(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*(?:(?:node:)?path|nodePath|Path)\s*\.\s*(?:resolve|join)\s*\(\s*([A-Za-z_]\w*(?:\.\w+)*)\s*,/;
|
|
24997
|
+
const startsWithGuardRe = (varName, rootName) => new RegExp(`if\\s*\\(\\s*!\\s*${varName}\\s*\\.\\s*startsWith\\s*\\(\\s*${rootName}\\b`);
|
|
24998
|
+
const terminatorRe = /\b(?:throw|return|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json))/;
|
|
24999
|
+
const candidates = [];
|
|
25000
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25001
|
+
const m = resolveDeclRe.exec(lines[i2]);
|
|
25002
|
+
if (!m)
|
|
25003
|
+
continue;
|
|
25004
|
+
candidates.push({ line: i2 + 1, fullVar: m[1], rootVar: m[2] });
|
|
25005
|
+
}
|
|
25006
|
+
if (candidates.length === 0)
|
|
25007
|
+
return sanitizers;
|
|
25008
|
+
for (const c of candidates) {
|
|
25009
|
+
const guardRe = startsWithGuardRe(c.fullVar, c.rootVar);
|
|
25010
|
+
let guardLine = -1;
|
|
25011
|
+
for (let l = c.line;l < Math.min(lines.length, c.line + 6); l++) {
|
|
25012
|
+
if (!guardRe.test(lines[l]))
|
|
25013
|
+
continue;
|
|
25014
|
+
if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
|
|
25015
|
+
guardLine = l + 1;
|
|
25016
|
+
break;
|
|
25017
|
+
}
|
|
25018
|
+
}
|
|
25019
|
+
if (guardLine < 0)
|
|
25020
|
+
continue;
|
|
25021
|
+
const varRefRe = new RegExp(`\\b${c.fullVar}\\b`);
|
|
25022
|
+
sanitizers.push({
|
|
25023
|
+
type: "js_path_resolve_startswith_guard",
|
|
25024
|
+
method: "startsWith",
|
|
25025
|
+
line: c.line,
|
|
25026
|
+
sanitizes: ["path_traversal", "external_taint_escape"]
|
|
25027
|
+
});
|
|
25028
|
+
for (let l = c.line;l < lines.length; l++) {
|
|
25029
|
+
if (!varRefRe.test(lines[l]))
|
|
25030
|
+
continue;
|
|
25031
|
+
sanitizers.push({
|
|
25032
|
+
type: "js_path_resolve_startswith_guard",
|
|
25033
|
+
method: "startsWith",
|
|
25034
|
+
line: l + 1,
|
|
25035
|
+
sanitizes: ["path_traversal", "external_taint_escape"]
|
|
25036
|
+
});
|
|
25037
|
+
}
|
|
25038
|
+
}
|
|
25039
|
+
return sanitizers;
|
|
25040
|
+
}
|
|
25041
|
+
function findJsCommandAllowlistGuardSanitizers(code) {
|
|
25042
|
+
const sanitizers = [];
|
|
25043
|
+
const lines = code.split(`
|
|
25044
|
+
`);
|
|
25045
|
+
const shoutyCase = /^[A-Z][A-Z0-9_]+$/;
|
|
25046
|
+
const allowlistTokens = /(?:allowed|accepted|whitelist|permitted|valid|approved|cmds|commands|tools)/i;
|
|
25047
|
+
const isAllowlistName = (n) => shoutyCase.test(n) || allowlistTokens.test(n);
|
|
25048
|
+
const guardHas = /\bif\s*\(\s*!\s*([A-Za-z_]\w*)\s*\.\s*(?:has|includes)\s*\(\s*([A-Za-z_]\w*)\s*\)\s*\)/;
|
|
25049
|
+
const guardIndexOf = /\bif\s*\(\s*([A-Za-z_]\w*)\s*\.\s*indexOf\s*\(\s*([A-Za-z_]\w*)\s*\)\s*(?:<\s*0|===\s*-1|==\s*-1)\s*\)/;
|
|
25050
|
+
const terminator = /\b(?:return|throw|res\s*\.\s*status\s*\([^)]*\)\s*\.\s*(?:send|end|json))/;
|
|
25051
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25052
|
+
const line = lines[i2];
|
|
25053
|
+
let m = guardHas.exec(line);
|
|
25054
|
+
let allow = null;
|
|
25055
|
+
let guardedVar = null;
|
|
25056
|
+
if (m) {
|
|
25057
|
+
allow = m[1];
|
|
25058
|
+
guardedVar = m[2];
|
|
25059
|
+
} else {
|
|
25060
|
+
m = guardIndexOf.exec(line);
|
|
25061
|
+
if (m) {
|
|
25062
|
+
allow = m[1];
|
|
25063
|
+
guardedVar = m[2];
|
|
25064
|
+
}
|
|
25065
|
+
}
|
|
25066
|
+
if (!allow || !guardedVar)
|
|
25067
|
+
continue;
|
|
25068
|
+
if (!isAllowlistName(allow))
|
|
25069
|
+
continue;
|
|
25070
|
+
let bodyHasTerminator = terminator.test(line);
|
|
25071
|
+
let blockEnd = i2;
|
|
25072
|
+
if (!bodyHasTerminator) {
|
|
25073
|
+
let braceDepth = 0;
|
|
25074
|
+
let started = false;
|
|
25075
|
+
const maxScan = Math.min(lines.length, i2 + 26);
|
|
25076
|
+
for (let j = i2;j < maxScan; j++) {
|
|
25077
|
+
const ln = lines[j];
|
|
25078
|
+
for (const ch of ln) {
|
|
25079
|
+
if (ch === "{") {
|
|
25080
|
+
braceDepth++;
|
|
25081
|
+
started = true;
|
|
25082
|
+
} else if (ch === "}") {
|
|
25083
|
+
braceDepth--;
|
|
25084
|
+
if (started && braceDepth === 0) {
|
|
25085
|
+
blockEnd = j;
|
|
25086
|
+
break;
|
|
25087
|
+
}
|
|
25088
|
+
}
|
|
25089
|
+
}
|
|
25090
|
+
if (started && j > i2 && terminator.test(ln))
|
|
25091
|
+
bodyHasTerminator = true;
|
|
25092
|
+
if (started && braceDepth === 0 && blockEnd !== i2)
|
|
25093
|
+
break;
|
|
25094
|
+
}
|
|
25095
|
+
if (!started || !bodyHasTerminator)
|
|
25096
|
+
continue;
|
|
25097
|
+
}
|
|
25098
|
+
const varRefRe = new RegExp(`\\b${guardedVar}\\b`);
|
|
25099
|
+
for (let l = blockEnd + 1;l < lines.length; l++) {
|
|
25100
|
+
if (!varRefRe.test(lines[l]))
|
|
25101
|
+
continue;
|
|
25102
|
+
sanitizers.push({
|
|
25103
|
+
type: "js_command_allowlist_guard",
|
|
25104
|
+
method: "if",
|
|
25105
|
+
line: l + 1,
|
|
25106
|
+
sanitizes: ["command_injection", "external_taint_escape"]
|
|
25107
|
+
});
|
|
25108
|
+
}
|
|
25109
|
+
}
|
|
25110
|
+
return sanitizers;
|
|
25111
|
+
}
|
|
24841
25112
|
function findJavaPathNormalizeStartsWithGuardSanitizers(code) {
|
|
24842
25113
|
const sanitizers = [];
|
|
24843
25114
|
const lines = code.split(`
|
|
@@ -24915,10 +25186,13 @@ function findJavaArgvFormExecSanitizers(code) {
|
|
|
24915
25186
|
`);
|
|
24916
25187
|
const argvExecRe = /\.\s*exec\s*\(\s*new\s+String\s*\[\s*\]\s*\{/;
|
|
24917
25188
|
const argvPbRe = /\bnew\s+ProcessBuilder\s*\(\s*new\s+String\s*\[\s*\]\s*\{/;
|
|
25189
|
+
const shellInStringRe = /new\s+String\s*\[\s*\]\s*\{\s*"(?:\/(?:usr\/)?bin\/(?:sh|bash|zsh|ksh|dash)|(?:sh|bash|zsh|ksh|dash)|cmd(?:\.exe)?|powershell(?:\.exe)?|pwsh)"\s*,\s*"(?:-c|\/c|-Command|-command)"/i;
|
|
24918
25190
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
24919
25191
|
const text = lines[i2];
|
|
24920
25192
|
if (!argvExecRe.test(text) && !argvPbRe.test(text))
|
|
24921
25193
|
continue;
|
|
25194
|
+
if (shellInStringRe.test(text))
|
|
25195
|
+
continue;
|
|
24922
25196
|
sanitizers.push({
|
|
24923
25197
|
type: "java_argv_form_exec",
|
|
24924
25198
|
method: "exec",
|
|
@@ -27069,6 +27343,20 @@ function findPythonMongoengineWhereNosqlInjectionFindings(code, file) {
|
|
|
27069
27343
|
}
|
|
27070
27344
|
return findings;
|
|
27071
27345
|
}
|
|
27346
|
+
function hasHostAllowlistBeforeSink(taintedVars, lines, sinkLineIdx) {
|
|
27347
|
+
for (let i2 = 0;i2 < sinkLineIdx; i2++) {
|
|
27348
|
+
const t = lines[i2];
|
|
27349
|
+
for (const v of taintedVars) {
|
|
27350
|
+
const containsRe = new RegExp(`\\.\\s*contains\\s*\\(\\s*${v}\\s*\\.\\s*getHost\\s*\\(\\s*\\)\\s*\\)`);
|
|
27351
|
+
if (containsRe.test(t))
|
|
27352
|
+
return true;
|
|
27353
|
+
const equalsRe = new RegExp(`\\b${v}\\s*\\.\\s*getHost\\s*\\(\\s*\\)\\s*\\.\\s*equals(?:IgnoreCase)?\\s*\\(\\s*"[^"]+"\\s*\\)`);
|
|
27354
|
+
if (equalsRe.test(t))
|
|
27355
|
+
return true;
|
|
27356
|
+
}
|
|
27357
|
+
}
|
|
27358
|
+
return false;
|
|
27359
|
+
}
|
|
27072
27360
|
function findJavaUrlOpenStreamSsrfFindings(code, file) {
|
|
27073
27361
|
const findings = [];
|
|
27074
27362
|
if (typeof code !== "string" || code.length === 0)
|
|
@@ -27133,6 +27421,8 @@ function findJavaUrlOpenStreamSsrfFindings(code, file) {
|
|
|
27133
27421
|
}
|
|
27134
27422
|
if (!tainted)
|
|
27135
27423
|
continue;
|
|
27424
|
+
if (hasHostAllowlistBeforeSink(taintedVars, lines, i2))
|
|
27425
|
+
continue;
|
|
27136
27426
|
const key = `${i2 + 1}:${op}`;
|
|
27137
27427
|
if (seen.has(key))
|
|
27138
27428
|
continue;
|
|
@@ -28443,6 +28733,94 @@ function findJsTemplateInjectionSstiFindings(code, file) {
|
|
|
28443
28733
|
return findings;
|
|
28444
28734
|
}
|
|
28445
28735
|
|
|
28736
|
+
// ../circle-ir/dist/analysis/passes/source-semantics-pass.js
|
|
28737
|
+
var DEMO_PATH_RE = /(?:^|\/)(?:demo|example|examples|samples|integration-tests|integration_tests)(?:\/|$)/i;
|
|
28738
|
+
var CONST_STRING_ASSIGN_RE = /^\s*(?:final\s+|static\s+final\s+)?[A-Za-z_][\w.<>\[\]]*\s+[A-Za-z_]\w*\s*=\s*"[^"]*"\s*;?\s*$/;
|
|
28739
|
+
var STATIC_FINAL_RE = /^\s*(?:public\s+|private\s+|protected\s+)?static\s+final\s+/;
|
|
28740
|
+
var ENUM_CONST_REF_RE = /=\s*[A-Z][A-Za-z0-9_]*\.[A-Z][A-Z0-9_]*\s*;?\s*$/;
|
|
28741
|
+
function isConstantSource(code) {
|
|
28742
|
+
if (!code)
|
|
28743
|
+
return false;
|
|
28744
|
+
if (CONST_STRING_ASSIGN_RE.test(code))
|
|
28745
|
+
return true;
|
|
28746
|
+
if (STATIC_FINAL_RE.test(code)) {
|
|
28747
|
+
const rhs = code.split("=").slice(1).join("=").trim();
|
|
28748
|
+
if (rhs.length === 0)
|
|
28749
|
+
return false;
|
|
28750
|
+
if (/^"[^"]*"\s*;?\s*$/.test(rhs))
|
|
28751
|
+
return true;
|
|
28752
|
+
if (/^-?\d+(?:\.\d+)?[fFdDlL]?\s*;?\s*$/.test(rhs))
|
|
28753
|
+
return true;
|
|
28754
|
+
if (/^(?:true|false)\s*;?\s*$/.test(rhs))
|
|
28755
|
+
return true;
|
|
28756
|
+
if (/^[A-Za-z_][\w.]*\s*;?\s*$/.test(rhs))
|
|
28757
|
+
return true;
|
|
28758
|
+
return false;
|
|
28759
|
+
}
|
|
28760
|
+
if (ENUM_CONST_REF_RE.test(code))
|
|
28761
|
+
return true;
|
|
28762
|
+
return false;
|
|
28763
|
+
}
|
|
28764
|
+
var SERVICE_LOADER_RE = /\bServiceLoader\.(?:load|loadInstalled|stream)\s*\(/;
|
|
28765
|
+
var CLASS_FOR_NAME_RE = /\bClass\.forName\s*\(/;
|
|
28766
|
+
var META_INF_SERVICES_RE = /getResources?\s*\(\s*"META-INF\/services\//;
|
|
28767
|
+
var SPI_WINDOW = 30;
|
|
28768
|
+
function isSpiSource(source, lines) {
|
|
28769
|
+
const code = source.code;
|
|
28770
|
+
if (!code)
|
|
28771
|
+
return false;
|
|
28772
|
+
if (SERVICE_LOADER_RE.test(code))
|
|
28773
|
+
return true;
|
|
28774
|
+
if (CLASS_FOR_NAME_RE.test(code)) {
|
|
28775
|
+
const start2 = Math.max(0, source.line - 1 - SPI_WINDOW);
|
|
28776
|
+
const end = Math.min(lines.length, source.line - 1 + SPI_WINDOW + 1);
|
|
28777
|
+
for (let i2 = start2;i2 < end; i2++) {
|
|
28778
|
+
if (META_INF_SERVICES_RE.test(lines[i2]))
|
|
28779
|
+
return true;
|
|
28780
|
+
}
|
|
28781
|
+
}
|
|
28782
|
+
return false;
|
|
28783
|
+
}
|
|
28784
|
+
function isDemoPathFile(file) {
|
|
28785
|
+
if (!file)
|
|
28786
|
+
return false;
|
|
28787
|
+
return DEMO_PATH_RE.test(file);
|
|
28788
|
+
}
|
|
28789
|
+
|
|
28790
|
+
class SourceSemanticsPass {
|
|
28791
|
+
name = "source-semantics";
|
|
28792
|
+
category = "security";
|
|
28793
|
+
run(ctx) {
|
|
28794
|
+
const { graph, code } = ctx;
|
|
28795
|
+
const sources = graph.ir.taint.sources;
|
|
28796
|
+
if (sources.length === 0) {
|
|
28797
|
+
return { constantCount: 0, spiCount: 0, demoPathCount: 0 };
|
|
28798
|
+
}
|
|
28799
|
+
const file = graph.ir.meta.file;
|
|
28800
|
+
const demoPath = isDemoPathFile(file);
|
|
28801
|
+
const lines = code.split(`
|
|
28802
|
+
`);
|
|
28803
|
+
let constantCount = 0;
|
|
28804
|
+
let spiCount = 0;
|
|
28805
|
+
let demoPathCount = 0;
|
|
28806
|
+
for (const source of sources) {
|
|
28807
|
+
if (isConstantSource(source.code)) {
|
|
28808
|
+
source.constant = true;
|
|
28809
|
+
constantCount++;
|
|
28810
|
+
}
|
|
28811
|
+
if (isSpiSource(source, lines)) {
|
|
28812
|
+
source.spi = true;
|
|
28813
|
+
spiCount++;
|
|
28814
|
+
}
|
|
28815
|
+
if (demoPath) {
|
|
28816
|
+
source.demoPath = true;
|
|
28817
|
+
demoPathCount++;
|
|
28818
|
+
}
|
|
28819
|
+
}
|
|
28820
|
+
return { constantCount, spiCount, demoPathCount };
|
|
28821
|
+
}
|
|
28822
|
+
}
|
|
28823
|
+
|
|
28446
28824
|
// ../circle-ir/dist/analysis/passes/sink-filter-pass.js
|
|
28447
28825
|
var JS_XSS_SANITIZERS = [
|
|
28448
28826
|
/\bDOMPurify\.sanitize\s*\(/,
|
|
@@ -28500,6 +28878,16 @@ var COMPILED_TEMPLATE_TYPES = new Set([
|
|
|
28500
28878
|
"VelocityTemplate",
|
|
28501
28879
|
"BeetlTemplate"
|
|
28502
28880
|
]);
|
|
28881
|
+
var TEMPLATE_ENGINE_LITERAL_TARGETS = new Set([
|
|
28882
|
+
"VelocityEngine",
|
|
28883
|
+
"Velocity",
|
|
28884
|
+
"TemplateEngine",
|
|
28885
|
+
"SpringTemplateEngine",
|
|
28886
|
+
"Configuration",
|
|
28887
|
+
"PebbleEngine",
|
|
28888
|
+
"Pebble",
|
|
28889
|
+
"Handlebars"
|
|
28890
|
+
]);
|
|
28503
28891
|
var REFLECTION_LITERAL_METHODS = new Set([
|
|
28504
28892
|
"forName",
|
|
28505
28893
|
"loadClass",
|
|
@@ -28532,7 +28920,7 @@ var PROTOCOL_CLIENT_PACKAGES = [
|
|
|
28532
28920
|
];
|
|
28533
28921
|
var OS_EXEC_RECEIVER_RE = /\b(?:Runtime|ProcessBuilder|DefaultExecutor|Executor|Exec|Launcher|ProcStarter|ProcessExecutor|RuntimeUtil)\s*[.(]/;
|
|
28534
28922
|
var PROCESS_BUILDER_ARGV_FORM_RE = /\bnew\s+ProcessBuilder\s*\(\s*(?:Arrays\.asList\b|List\.of\b|Collections\.singletonList\b|new\s+ArrayList\b|new\s+String\s*\[\s*\]\s*\{|"[^"]*"\s*,)/;
|
|
28535
|
-
var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b
|
|
28923
|
+
var JAVA_THROW_STATEMENT_RE = /^\s*throw\s+new\s+\w+(?:Exception|Error)\b[^;]*;\s*(?:\/\/.*|\/\*.*)?$/;
|
|
28536
28924
|
var JEXL_ENGINE_TYPES = new Set(["JexlEngine", "Jexl", "JxltEngine"]);
|
|
28537
28925
|
var JEXL_EXPRESSION_TYPE_RE = /(?:Jexl)?(?:Expression|Script|Template)(?:Script)?$/;
|
|
28538
28926
|
var TEMPLATE_COMPILE_RECEIVER_TYPES = new Set([
|
|
@@ -28958,6 +29346,23 @@ class SinkFilterPass {
|
|
|
28958
29346
|
const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
|
|
28959
29347
|
if (recvType && COMPILED_TEMPLATE_TYPES.has(recvType))
|
|
28960
29348
|
return false;
|
|
29349
|
+
if (recvType && TEMPLATE_ENGINE_LITERAL_TARGETS.has(recvType)) {
|
|
29350
|
+
const args2 = extractJavaCallArgs(method, sinkLineText);
|
|
29351
|
+
if (args2 !== null && args2.length >= 1 && isJavaLiteralOrAnnotationAccessor(args2[0])) {
|
|
29352
|
+
return false;
|
|
29353
|
+
}
|
|
29354
|
+
}
|
|
29355
|
+
}
|
|
29356
|
+
if (method === "evaluate" && receiver) {
|
|
29357
|
+
const recvType = resolveJavaReceiverType(receiver, sink.line, sourceLines);
|
|
29358
|
+
if (recvType && TEMPLATE_ENGINE_LITERAL_TARGETS.has(recvType)) {
|
|
29359
|
+
const args2 = extractJavaCallArgs(method, sinkLineText);
|
|
29360
|
+
if (args2 !== null && args2.length >= 1) {
|
|
29361
|
+
const last = args2[args2.length - 1] ?? "";
|
|
29362
|
+
if (isJavaLiteralOrAnnotationAccessor(last))
|
|
29363
|
+
return false;
|
|
29364
|
+
}
|
|
29365
|
+
}
|
|
28961
29366
|
}
|
|
28962
29367
|
if (method && REFLECTION_LITERAL_METHODS.has(method)) {
|
|
28963
29368
|
const args2 = extractJavaCallArgs(method, sinkLineText);
|
|
@@ -29024,7 +29429,7 @@ class SinkFilterPass {
|
|
|
29024
29429
|
filtered = filtered.filter((sink) => {
|
|
29025
29430
|
if (sink.type !== "command_injection")
|
|
29026
29431
|
return true;
|
|
29027
|
-
if (sink.method !== "ProcessBuilder")
|
|
29432
|
+
if (sink.method !== "ProcessBuilder" && sink.method !== "start")
|
|
29028
29433
|
return true;
|
|
29029
29434
|
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
29030
29435
|
if (!/\bnew\s+ProcessBuilder\s*\(/.test(sinkLineText))
|
|
@@ -29861,6 +30266,54 @@ function filterSanitizedSinks(sinks, sanitizers, calls) {
|
|
|
29861
30266
|
});
|
|
29862
30267
|
}
|
|
29863
30268
|
|
|
30269
|
+
// ../circle-ir/dist/analysis/passes/sink-semantics-pass.js
|
|
30270
|
+
function buildRegistry(entries) {
|
|
30271
|
+
const registry = new Map;
|
|
30272
|
+
for (const entry of entries) {
|
|
30273
|
+
const existing = registry.get(entry.signature);
|
|
30274
|
+
if (existing) {
|
|
30275
|
+
for (const t of entry.overrides)
|
|
30276
|
+
existing.add(t);
|
|
30277
|
+
} else {
|
|
30278
|
+
registry.set(entry.signature, new Set(entry.overrides));
|
|
30279
|
+
}
|
|
30280
|
+
}
|
|
30281
|
+
return registry;
|
|
30282
|
+
}
|
|
30283
|
+
|
|
30284
|
+
class SinkSemanticsPass {
|
|
30285
|
+
name = "sink-semantics";
|
|
30286
|
+
category = "security";
|
|
30287
|
+
run(ctx) {
|
|
30288
|
+
const { graph, config } = ctx;
|
|
30289
|
+
const entries = config.sinkSemantics ?? [];
|
|
30290
|
+
if (entries.length === 0) {
|
|
30291
|
+
return { droppedCount: 0, registrySize: 0 };
|
|
30292
|
+
}
|
|
30293
|
+
const registry = buildRegistry(entries);
|
|
30294
|
+
const sinks = graph.ir.taint.sinks;
|
|
30295
|
+
let droppedCount = 0;
|
|
30296
|
+
const kept = sinks.filter((sink) => {
|
|
30297
|
+
if (!sink.class || !sink.method)
|
|
30298
|
+
return true;
|
|
30299
|
+
const signature = `${sink.class}#${sink.method}`;
|
|
30300
|
+
const overrides = registry.get(signature);
|
|
30301
|
+
if (!overrides)
|
|
30302
|
+
return true;
|
|
30303
|
+
if (overrides.has(sink.type)) {
|
|
30304
|
+
droppedCount++;
|
|
30305
|
+
return false;
|
|
30306
|
+
}
|
|
30307
|
+
return true;
|
|
30308
|
+
});
|
|
30309
|
+
if (droppedCount > 0) {
|
|
30310
|
+
sinks.length = 0;
|
|
30311
|
+
sinks.push(...kept);
|
|
30312
|
+
}
|
|
30313
|
+
return { droppedCount, registrySize: registry.size };
|
|
30314
|
+
}
|
|
30315
|
+
}
|
|
30316
|
+
|
|
29864
30317
|
// ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
|
|
29865
30318
|
class TaintPropagationPass {
|
|
29866
30319
|
name = "taint-propagation";
|
|
@@ -30555,6 +31008,27 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
30555
31008
|
}
|
|
30556
31009
|
}
|
|
30557
31010
|
}
|
|
31011
|
+
if (language === "java" && typeof code === "string" && sourcesWithVar.length > 0) {
|
|
31012
|
+
const seedVars = new Set(sourcesWithVar.map((s) => s.variable));
|
|
31013
|
+
const derived = buildJavaTaintedVars(code, seedVars);
|
|
31014
|
+
if (derived.size > 0) {
|
|
31015
|
+
let anchor = sourcesWithVar[0];
|
|
31016
|
+
for (const s of sourcesWithVar) {
|
|
31017
|
+
if (s.line < anchor.line)
|
|
31018
|
+
anchor = s;
|
|
31019
|
+
}
|
|
31020
|
+
const existingVars = new Set(sourcesWithVar.map((s) => s.variable));
|
|
31021
|
+
for (const [varName] of derived) {
|
|
31022
|
+
if (!varName || existingVars.has(varName))
|
|
31023
|
+
continue;
|
|
31024
|
+
sourcesWithVar.push({
|
|
31025
|
+
...anchor,
|
|
31026
|
+
variable: varName
|
|
31027
|
+
});
|
|
31028
|
+
existingVars.add(varName);
|
|
31029
|
+
}
|
|
31030
|
+
}
|
|
31031
|
+
}
|
|
30558
31032
|
const reCache = new Map;
|
|
30559
31033
|
for (const s of sourcesWithVar) {
|
|
30560
31034
|
if (reCache.has(s.variable))
|
|
@@ -30589,6 +31063,8 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
30589
31063
|
const re = reCache.get(source.variable);
|
|
30590
31064
|
if (!re || !re.test(expr))
|
|
30591
31065
|
continue;
|
|
31066
|
+
if (!sourceSemanticsAllowed(source, sink.type))
|
|
31067
|
+
continue;
|
|
30592
31068
|
if (flows.some((f) => f.source_line === source.line && f.sink_line === sink.line && f.sink_type === sink.type))
|
|
30593
31069
|
continue;
|
|
30594
31070
|
if (aliasSanitizedFor.get(source.variable)?.has(sink.type)) {
|
|
@@ -30616,8 +31092,6 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
30616
31092
|
}
|
|
30617
31093
|
const sourcesByLine = new Map;
|
|
30618
31094
|
for (const s of sources) {
|
|
30619
|
-
if (s.variable && s.variable.length > 0)
|
|
30620
|
-
continue;
|
|
30621
31095
|
const arr = sourcesByLine.get(s.line) ?? [];
|
|
30622
31096
|
arr.push(s);
|
|
30623
31097
|
sourcesByLine.set(s.line, arr);
|
|
@@ -30631,6 +31105,20 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
30631
31105
|
for (const source of colocSources) {
|
|
30632
31106
|
if (!canSourceReachSink(source.type, sink.type))
|
|
30633
31107
|
continue;
|
|
31108
|
+
if (!sourceSemanticsAllowed(source, sink.type))
|
|
31109
|
+
continue;
|
|
31110
|
+
const sourceVar = source.variable;
|
|
31111
|
+
if (sourceVar && sourceVar.length > 0) {
|
|
31112
|
+
const sinkCode = sink.code;
|
|
31113
|
+
if (!sinkCode) {
|
|
31114
|
+
continue;
|
|
31115
|
+
}
|
|
31116
|
+
const assignMatch = sinkCode.match(/^\s*(?:[A-Za-z_][\w.<>[\]\s,?]*\s+)?[A-Za-z_]\w*\s*=(?!=)\s*/);
|
|
31117
|
+
const rhs = assignMatch ? sinkCode.slice(assignMatch[0].length) : sinkCode;
|
|
31118
|
+
if (new RegExp(`\\b${sourceVar}\\b`).test(rhs)) {
|
|
31119
|
+
continue;
|
|
31120
|
+
}
|
|
31121
|
+
}
|
|
30634
31122
|
if (source.type === "file_input" && sink.type === "path_traversal" && sink.method && source.location.includes(`${sink.method}(`)) {
|
|
30635
31123
|
continue;
|
|
30636
31124
|
}
|
|
@@ -31219,7 +31707,9 @@ var TIER_1_METHOD_ANNOTATIONS = new Set([
|
|
|
31219
31707
|
"DELETE",
|
|
31220
31708
|
"PATCH",
|
|
31221
31709
|
"HEAD",
|
|
31222
|
-
"OPTIONS"
|
|
31710
|
+
"OPTIONS",
|
|
31711
|
+
"DataBoundConstructor",
|
|
31712
|
+
"DataBoundSetter"
|
|
31223
31713
|
]);
|
|
31224
31714
|
var TIER_1_CLASS_ANNOTATIONS = new Set([
|
|
31225
31715
|
"RestController",
|
|
@@ -31244,7 +31734,13 @@ var TIER_1_BY_SUPERTYPE = new Map([
|
|
|
31244
31734
|
["ChannelInboundHandler", new Set(["channelRead", "channelReadComplete"])],
|
|
31245
31735
|
["ChannelInboundHandlerAdapter", new Set(["channelRead", "channelReadComplete"])],
|
|
31246
31736
|
["ChannelDuplexHandler", new Set(["channelRead", "channelReadComplete"])],
|
|
31247
|
-
["NettyRequestProcessor", new Set(["process"])]
|
|
31737
|
+
["NettyRequestProcessor", new Set(["process"])],
|
|
31738
|
+
["Converter", new Set(["marshal", "unmarshal"])],
|
|
31739
|
+
["SingleValueConverter", new Set(["fromString", "toString"])],
|
|
31740
|
+
["ConverterMatcher", new Set(["marshal", "unmarshal"])],
|
|
31741
|
+
["AbstractReflectionConverter", new Set(["marshal", "unmarshal", "doMarshal", "doUnmarshal"])],
|
|
31742
|
+
["AbstractSingleValueConverter", new Set(["fromString", "toString"])],
|
|
31743
|
+
["AbstractCollectionConverter", new Set(["marshal", "unmarshal"])]
|
|
31248
31744
|
]);
|
|
31249
31745
|
var TIER_3_CLASS_SUFFIXES = [
|
|
31250
31746
|
"Util",
|
|
@@ -35754,6 +36250,14 @@ function isProtocolMandatedCryptoFile(file, code) {
|
|
|
35754
36250
|
}
|
|
35755
36251
|
|
|
35756
36252
|
// ../circle-ir/dist/analysis/passes/scan-secrets-pass.js
|
|
36253
|
+
function applyDemoDowngrade(demoPath, severity, level) {
|
|
36254
|
+
if (!demoPath)
|
|
36255
|
+
return { severity, level };
|
|
36256
|
+
if (severity === "high") {
|
|
36257
|
+
return { severity: "low", level: "note" };
|
|
36258
|
+
}
|
|
36259
|
+
return { severity, level };
|
|
36260
|
+
}
|
|
35757
36261
|
var TEST_PATH_RE3 = /(?:^|[\\/])(?:test|tests|spec|specs|__tests?__|__mocks?__|fixtures?|testdata)(?:[\\/]|$)/i;
|
|
35758
36262
|
var TEST_FILENAME_RE = /(?:\.(?:test|spec)\.[cm]?[jt]sx?|_test\.go|_test\.py|Test\.java|Tests\.java)$/i;
|
|
35759
36263
|
function isTestFile(file) {
|
|
@@ -36104,6 +36608,7 @@ class ScanSecretsPass {
|
|
|
36104
36608
|
if (isTestFile(file) || isGeneratedFile(file)) {
|
|
36105
36609
|
return { providerFindings: 0, entropyFindings: 0 };
|
|
36106
36610
|
}
|
|
36611
|
+
const demoPath = DEMO_PATH_RE.test(file);
|
|
36107
36612
|
const lines = ctx.code.split(`
|
|
36108
36613
|
`);
|
|
36109
36614
|
const prior = ctx.getFindings?.() ?? [];
|
|
@@ -36134,14 +36639,15 @@ class ScanSecretsPass {
|
|
|
36134
36639
|
if (seen.has(key))
|
|
36135
36640
|
continue;
|
|
36136
36641
|
seen.add(key);
|
|
36642
|
+
const dg = applyDemoDowngrade(demoPath, pattern.severity, pattern.level);
|
|
36137
36643
|
ctx.addFinding({
|
|
36138
36644
|
id: `hardcoded-credential-${file}-${lineNum}`,
|
|
36139
36645
|
pass: this.name,
|
|
36140
36646
|
category: this.category,
|
|
36141
36647
|
rule_id: "hardcoded-credential",
|
|
36142
36648
|
cwe: "CWE-798",
|
|
36143
|
-
severity:
|
|
36144
|
-
level:
|
|
36649
|
+
severity: dg.severity,
|
|
36650
|
+
level: dg.level,
|
|
36145
36651
|
message: `Hardcoded credential: ${pattern.name} detected`,
|
|
36146
36652
|
file,
|
|
36147
36653
|
line: lineNum,
|
|
@@ -36163,14 +36669,15 @@ class ScanSecretsPass {
|
|
|
36163
36669
|
if (seen.has(key))
|
|
36164
36670
|
continue;
|
|
36165
36671
|
seen.add(key);
|
|
36672
|
+
const dg = applyDemoDowngrade(demoPath, "high", "error");
|
|
36166
36673
|
ctx.addFinding({
|
|
36167
36674
|
id: `hardcoded-credential-${file}-${lineNum}`,
|
|
36168
36675
|
pass: this.name,
|
|
36169
36676
|
category: this.category,
|
|
36170
36677
|
rule_id: "hardcoded-credential",
|
|
36171
36678
|
cwe: "CWE-798",
|
|
36172
|
-
severity:
|
|
36173
|
-
level:
|
|
36679
|
+
severity: dg.severity,
|
|
36680
|
+
level: dg.level,
|
|
36174
36681
|
message: `Hardcoded credential: \`${hit.name}\` assigned a literal value`,
|
|
36175
36682
|
file,
|
|
36176
36683
|
line: lineNum,
|
|
@@ -40791,7 +41298,11 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
40791
41298
|
pipeline.add(new TaintMatcherPass);
|
|
40792
41299
|
pipeline.add(new ConstantPropagationPass(tree));
|
|
40793
41300
|
pipeline.add(new LanguageSourcesPass);
|
|
41301
|
+
if (!disabledPasses.has("source-semantics"))
|
|
41302
|
+
pipeline.add(new SourceSemanticsPass);
|
|
40794
41303
|
pipeline.add(new SinkFilterPass);
|
|
41304
|
+
if (!disabledPasses.has("sink-semantics"))
|
|
41305
|
+
pipeline.add(new SinkSemanticsPass);
|
|
40795
41306
|
pipeline.add(new TaintPropagationPass);
|
|
40796
41307
|
pipeline.add(new InterproceduralPass({
|
|
40797
41308
|
enableEntryPointGate: options.enableEntryPointGate ?? true
|
|
@@ -41719,7 +42230,7 @@ var colors = {
|
|
|
41719
42230
|
};
|
|
41720
42231
|
|
|
41721
42232
|
// src/version.ts
|
|
41722
|
-
var version = "3.
|
|
42233
|
+
var version = "3.146.0";
|
|
41723
42234
|
|
|
41724
42235
|
// src/formatters.ts
|
|
41725
42236
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.146.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",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@cognium/project-profile-detect": "^1.1.0",
|
|
69
|
-
"circle-ir": "^3.
|
|
69
|
+
"circle-ir": "^3.146.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|