circle-ir 3.126.0 → 3.129.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/analysis/passes/language-sources-pass.d.ts +72 -0
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +674 -0
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/browser/circle-ir.js +405 -0
- package/package.json +1 -1
|
@@ -23650,6 +23650,10 @@ var LanguageSourcesPass = class {
|
|
|
23650
23650
|
if (language === "go") {
|
|
23651
23651
|
additionalSanitizers.push(...findGoMapAllowlistGuardSanitizers(code));
|
|
23652
23652
|
additionalSanitizers.push(...findGoHtmlTemplateImportSanitizers(code));
|
|
23653
|
+
const goMisconfigFindings = findGoPatternFindings(code, graph.ir.meta.file);
|
|
23654
|
+
for (const finding of goMisconfigFindings) {
|
|
23655
|
+
ctx.addFinding(finding);
|
|
23656
|
+
}
|
|
23653
23657
|
}
|
|
23654
23658
|
if (language === "python") {
|
|
23655
23659
|
additionalSanitizers.push(...findPythonNetlocAllowlistGuardSanitizers(code));
|
|
@@ -23657,6 +23661,7 @@ var LanguageSourcesPass = class {
|
|
|
23657
23661
|
additionalSanitizers.push(...findPythonRegexAllowlistWrapperSanitizers(code));
|
|
23658
23662
|
additionalSanitizers.push(...findPythonSetMembershipXssGuardSanitizers(code));
|
|
23659
23663
|
additionalSanitizers.push(...findPythonDefusedXmlSanitizers(code));
|
|
23664
|
+
additionalSanitizers.push(...findPythonJinjaAutoescapeSanitizers(code));
|
|
23660
23665
|
const pyMisconfigFindings = findPythonPatternFindings(code, graph.ir.meta.file);
|
|
23661
23666
|
for (const finding of pyMisconfigFindings) {
|
|
23662
23667
|
ctx.addFinding(finding);
|
|
@@ -23665,10 +23670,23 @@ var LanguageSourcesPass = class {
|
|
|
23665
23670
|
if (language === "rust") {
|
|
23666
23671
|
additionalSanitizers.push(...findRustSetAllowlistGuardSanitizers(code));
|
|
23667
23672
|
additionalSanitizers.push(...findRustCanonicalizeGuardSanitizers(code));
|
|
23673
|
+
additionalSanitizers.push(...findRustArgvCommandSanitizers(code));
|
|
23668
23674
|
const rustMisconfigFindings = findRustPatternFindings(code, graph.ir.meta.file);
|
|
23669
23675
|
for (const finding of rustMisconfigFindings) {
|
|
23670
23676
|
ctx.addFinding(finding);
|
|
23671
23677
|
}
|
|
23678
|
+
for (const finding of findRustHardcodedCredentialFindings(code, graph.ir.meta.file)) {
|
|
23679
|
+
ctx.addFinding(finding);
|
|
23680
|
+
}
|
|
23681
|
+
for (const finding of findRustInsecureCookieFindings(code, graph.ir.meta.file)) {
|
|
23682
|
+
ctx.addFinding(finding);
|
|
23683
|
+
}
|
|
23684
|
+
for (const finding of findRustJwtVerifyDisabledFindings(code, graph.ir.meta.file)) {
|
|
23685
|
+
ctx.addFinding(finding);
|
|
23686
|
+
}
|
|
23687
|
+
for (const finding of findRustWeakCryptoEcbFindings(code, graph.ir.meta.file)) {
|
|
23688
|
+
ctx.addFinding(finding);
|
|
23689
|
+
}
|
|
23672
23690
|
}
|
|
23673
23691
|
if (language === "javascript" || language === "typescript" || language === "htmljs") {
|
|
23674
23692
|
additionalSanitizers.push(...findJsSafeJsonParseSanitizers(code));
|
|
@@ -23676,9 +23694,20 @@ var LanguageSourcesPass = class {
|
|
|
23676
23694
|
additionalSanitizers.push(...findJsCsvFormulaPrefixSanitizers(code));
|
|
23677
23695
|
additionalSanitizers.push(...findJsWrapperFunctionSanitizers(code));
|
|
23678
23696
|
additionalSanitizers.push(...findJsSsrfAllowlistGuardSanitizers(code));
|
|
23697
|
+
for (const finding of findJsPatternFindings(code, graph.ir.meta.file)) {
|
|
23698
|
+
ctx.addFinding(finding);
|
|
23699
|
+
}
|
|
23679
23700
|
}
|
|
23680
23701
|
if (language === "java") {
|
|
23681
23702
|
additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
|
|
23703
|
+
additionalSanitizers.push(
|
|
23704
|
+
...findJavaPathNormalizeStartsWithGuardSanitizers(code)
|
|
23705
|
+
);
|
|
23706
|
+
additionalSanitizers.push(...findJavaInlineCrlfStripLogSanitizers(code));
|
|
23707
|
+
additionalSanitizers.push(...findJavaArgvFormExecSanitizers(code));
|
|
23708
|
+
for (const finding of findJavaPatternFindings(code, graph.ir.meta.file)) {
|
|
23709
|
+
ctx.addFinding(finding);
|
|
23710
|
+
}
|
|
23682
23711
|
}
|
|
23683
23712
|
if (language === "python" || language === "javascript" || language === "typescript" || language === "go") {
|
|
23684
23713
|
const exfilFindings = findExternalSecretExfiltrationFindings(
|
|
@@ -25425,6 +25454,136 @@ function findJsSsrfAllowlistGuardSanitizers(code) {
|
|
|
25425
25454
|
}
|
|
25426
25455
|
return sanitizers;
|
|
25427
25456
|
}
|
|
25457
|
+
function findJavaPathNormalizeStartsWithGuardSanitizers(code) {
|
|
25458
|
+
const sanitizers = [];
|
|
25459
|
+
const lines = code.split("\n");
|
|
25460
|
+
const resolveNormalizeRe = /\b([A-Za-z_]\w*)\s*=\s*([A-Za-z_]\w*)\s*\.\s*resolve\s*\([^)]*\)\s*\.\s*normalize\s*\(\s*\)/;
|
|
25461
|
+
const startsWithGuardRe = (varName, rootName) => new RegExp(
|
|
25462
|
+
`if\\s*\\(\\s*!\\s*${varName}\\s*\\.\\s*startsWith\\s*\\(\\s*${rootName}\\s*\\)\\s*\\)`
|
|
25463
|
+
);
|
|
25464
|
+
const terminatorRe = /\b(throw|return)\b/;
|
|
25465
|
+
const candidates = [];
|
|
25466
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25467
|
+
const m = resolveNormalizeRe.exec(lines[i2]);
|
|
25468
|
+
if (!m) continue;
|
|
25469
|
+
candidates.push({ line: i2 + 1, fullVar: m[1], rootVar: m[2] });
|
|
25470
|
+
}
|
|
25471
|
+
if (candidates.length === 0) return sanitizers;
|
|
25472
|
+
for (const c of candidates) {
|
|
25473
|
+
const guardRe = startsWithGuardRe(c.fullVar, c.rootVar);
|
|
25474
|
+
let guardLine = -1;
|
|
25475
|
+
for (let l = c.line; l < Math.min(lines.length, c.line + 6); l++) {
|
|
25476
|
+
if (!guardRe.test(lines[l])) continue;
|
|
25477
|
+
if (terminatorRe.test(lines[l]) || l + 1 < lines.length && terminatorRe.test(lines[l + 1])) {
|
|
25478
|
+
guardLine = l + 1;
|
|
25479
|
+
break;
|
|
25480
|
+
}
|
|
25481
|
+
}
|
|
25482
|
+
if (guardLine < 0) continue;
|
|
25483
|
+
const varRefRe = new RegExp(`\\b${c.fullVar}\\b`);
|
|
25484
|
+
sanitizers.push({
|
|
25485
|
+
type: "java_path_normalize_startswith_guard",
|
|
25486
|
+
method: "normalize",
|
|
25487
|
+
line: c.line,
|
|
25488
|
+
sanitizes: ["path_traversal", "external_taint_escape"]
|
|
25489
|
+
});
|
|
25490
|
+
for (let l = c.line; l < lines.length; l++) {
|
|
25491
|
+
if (!varRefRe.test(lines[l])) continue;
|
|
25492
|
+
sanitizers.push({
|
|
25493
|
+
type: "java_path_normalize_startswith_guard",
|
|
25494
|
+
method: "normalize",
|
|
25495
|
+
line: l + 1,
|
|
25496
|
+
sanitizes: ["path_traversal", "external_taint_escape"]
|
|
25497
|
+
});
|
|
25498
|
+
}
|
|
25499
|
+
}
|
|
25500
|
+
return sanitizers;
|
|
25501
|
+
}
|
|
25502
|
+
function findJavaInlineCrlfStripLogSanitizers(code) {
|
|
25503
|
+
const sanitizers = [];
|
|
25504
|
+
const lines = code.split("\n");
|
|
25505
|
+
const logCallStart = /\b(?:log|logger|LOG|LOGGER|slog|LOGGER_)\s*\.\s*(?:info|warn|error|debug|trace|fatal|severe|warning|fine|finer|finest|config)\s*\(/;
|
|
25506
|
+
const crlfReplaceAll = /\.\s*replaceAll\s*\(\s*"\[[^"]*\\\\?[rnt][^"]*\]"/;
|
|
25507
|
+
const crlfReplaceChar = /\.\s*replace\s*\(\s*'(?:\\\\?[rnt])'\s*,/;
|
|
25508
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25509
|
+
const text = lines[i2];
|
|
25510
|
+
if (!logCallStart.test(text)) continue;
|
|
25511
|
+
if (!crlfReplaceAll.test(text) && !crlfReplaceChar.test(text)) continue;
|
|
25512
|
+
sanitizers.push({
|
|
25513
|
+
type: "java_inline_crlf_strip_log",
|
|
25514
|
+
method: "replaceAll",
|
|
25515
|
+
line: i2 + 1,
|
|
25516
|
+
sanitizes: ["log_injection", "external_taint_escape"]
|
|
25517
|
+
});
|
|
25518
|
+
}
|
|
25519
|
+
return sanitizers;
|
|
25520
|
+
}
|
|
25521
|
+
function findJavaArgvFormExecSanitizers(code) {
|
|
25522
|
+
const sanitizers = [];
|
|
25523
|
+
const lines = code.split("\n");
|
|
25524
|
+
const argvExecRe = /\.\s*exec\s*\(\s*new\s+String\s*\[\s*\]\s*\{/;
|
|
25525
|
+
const argvPbRe = /\bnew\s+ProcessBuilder\s*\(\s*new\s+String\s*\[\s*\]\s*\{/;
|
|
25526
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25527
|
+
const text = lines[i2];
|
|
25528
|
+
if (!argvExecRe.test(text) && !argvPbRe.test(text)) continue;
|
|
25529
|
+
sanitizers.push({
|
|
25530
|
+
type: "java_argv_form_exec",
|
|
25531
|
+
method: "exec",
|
|
25532
|
+
line: i2 + 1,
|
|
25533
|
+
sanitizes: ["command_injection", "external_taint_escape"]
|
|
25534
|
+
});
|
|
25535
|
+
}
|
|
25536
|
+
return sanitizers;
|
|
25537
|
+
}
|
|
25538
|
+
function findRustArgvCommandSanitizers(code) {
|
|
25539
|
+
const sanitizers = [];
|
|
25540
|
+
const lines = code.split("\n");
|
|
25541
|
+
const argvCommandRe = /\bCommand\s*::\s*new\s*\(\s*"[^"]*"\s*\)\s*\.\s*arg\s*\(/;
|
|
25542
|
+
const shellArgvRe = /\bCommand\s*::\s*new\s*\(\s*"(?:sh|bash|zsh|ksh|dash|cmd(?:\.exe)?|powershell|pwsh)"\s*\)\s*\.\s*arg\s*\(\s*"-c"/;
|
|
25543
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25544
|
+
const text = lines[i2];
|
|
25545
|
+
if (!argvCommandRe.test(text)) continue;
|
|
25546
|
+
if (shellArgvRe.test(text)) continue;
|
|
25547
|
+
sanitizers.push({
|
|
25548
|
+
type: "rust_argv_command",
|
|
25549
|
+
method: "arg",
|
|
25550
|
+
line: i2 + 1,
|
|
25551
|
+
sanitizes: ["command_injection", "external_taint_escape"]
|
|
25552
|
+
});
|
|
25553
|
+
}
|
|
25554
|
+
return sanitizers;
|
|
25555
|
+
}
|
|
25556
|
+
function findPythonJinjaAutoescapeSanitizers(code) {
|
|
25557
|
+
const sanitizers = [];
|
|
25558
|
+
const lines = code.split("\n");
|
|
25559
|
+
const envAssignRe = /\b([A-Za-z_]\w*)\s*=\s*Environment\s*\(/;
|
|
25560
|
+
const autoescapeOnRe = /\bautoescape\s*=/;
|
|
25561
|
+
const autoescapeOffRe = /\bautoescape\s*=\s*(?:False|None|0)\b/;
|
|
25562
|
+
const envVars = /* @__PURE__ */ new Set();
|
|
25563
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25564
|
+
const m = envAssignRe.exec(lines[i2]);
|
|
25565
|
+
if (!m) continue;
|
|
25566
|
+
if (!autoescapeOnRe.test(lines[i2])) continue;
|
|
25567
|
+
if (autoescapeOffRe.test(lines[i2])) continue;
|
|
25568
|
+
envVars.add(m[1]);
|
|
25569
|
+
}
|
|
25570
|
+
if (envVars.size === 0) return sanitizers;
|
|
25571
|
+
for (const envVar of envVars) {
|
|
25572
|
+
const renderChainRe = new RegExp(
|
|
25573
|
+
`\\b${envVar}\\s*\\.\\s*get_template\\s*\\([^)]*\\)\\s*\\.\\s*render\\s*\\(`
|
|
25574
|
+
);
|
|
25575
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25576
|
+
if (!renderChainRe.test(lines[i2])) continue;
|
|
25577
|
+
sanitizers.push({
|
|
25578
|
+
type: "python_jinja_autoescape",
|
|
25579
|
+
method: "render",
|
|
25580
|
+
line: i2 + 1,
|
|
25581
|
+
sanitizes: ["xss", "external_taint_escape"]
|
|
25582
|
+
});
|
|
25583
|
+
}
|
|
25584
|
+
}
|
|
25585
|
+
return sanitizers;
|
|
25586
|
+
}
|
|
25428
25587
|
function collectEnvSecretVars(lines, language) {
|
|
25429
25588
|
const out2 = /* @__PURE__ */ new Map();
|
|
25430
25589
|
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
@@ -25807,6 +25966,252 @@ function findRustPatternFindings(code, file) {
|
|
|
25807
25966
|
}
|
|
25808
25967
|
return out2;
|
|
25809
25968
|
}
|
|
25969
|
+
function findRustHardcodedCredentialFindings(code, file) {
|
|
25970
|
+
const out2 = [];
|
|
25971
|
+
const lines = code.split("\n");
|
|
25972
|
+
const re = /\b(?:pub\s+)?(?:const|static)\s+([A-Z][A-Z0-9_]*)\s*:\s*&\s*'?[a-z_]*\s*str\s*=\s*"([^"]+)"/;
|
|
25973
|
+
const nameRe = /(?:^|_)(?:API[_]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PWD|AUTH)(?:_|$)/i;
|
|
25974
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25975
|
+
const raw = lines[i2];
|
|
25976
|
+
const trimmed = raw.trim();
|
|
25977
|
+
if (!trimmed || trimmed.startsWith("//")) continue;
|
|
25978
|
+
const m = trimmed.match(re);
|
|
25979
|
+
if (!m) continue;
|
|
25980
|
+
const name2 = m[1];
|
|
25981
|
+
const value = m[2];
|
|
25982
|
+
if (!nameRe.test(name2)) continue;
|
|
25983
|
+
if (value.length < 8) continue;
|
|
25984
|
+
if (/^(?:xxx|todo|fixme|placeholder|changeme)/i.test(value)) continue;
|
|
25985
|
+
out2.push({
|
|
25986
|
+
id: `hardcoded-credential-${file}-${i2 + 1}`,
|
|
25987
|
+
pass: "language-sources",
|
|
25988
|
+
category: "security",
|
|
25989
|
+
rule_id: "hardcoded-credential",
|
|
25990
|
+
cwe: "CWE-798",
|
|
25991
|
+
severity: "high",
|
|
25992
|
+
level: "error",
|
|
25993
|
+
message: `Hardcoded credential: const ${name2} contains a literal secret value`,
|
|
25994
|
+
file,
|
|
25995
|
+
line: i2 + 1,
|
|
25996
|
+
snippet: trimmed.substring(0, 100)
|
|
25997
|
+
});
|
|
25998
|
+
}
|
|
25999
|
+
return out2;
|
|
26000
|
+
}
|
|
26001
|
+
function findRustInsecureCookieFindings(code, file) {
|
|
26002
|
+
const out2 = [];
|
|
26003
|
+
const lines = code.split("\n");
|
|
26004
|
+
const builderRe = /\bCookie\s*::\s*build\s*\(/;
|
|
26005
|
+
const insecureFlagRe = /\.\s*(?:secure|http_only)\s*\(\s*false\s*\)/;
|
|
26006
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
26007
|
+
const raw = lines[i2];
|
|
26008
|
+
const trimmed = raw.trim();
|
|
26009
|
+
if (!trimmed || trimmed.startsWith("//")) continue;
|
|
26010
|
+
if (!builderRe.test(trimmed)) continue;
|
|
26011
|
+
if (!insecureFlagRe.test(trimmed)) continue;
|
|
26012
|
+
out2.push({
|
|
26013
|
+
id: `insecure-cookie-${file}-${i2 + 1}`,
|
|
26014
|
+
pass: "language-sources",
|
|
26015
|
+
category: "security",
|
|
26016
|
+
rule_id: "insecure-cookie",
|
|
26017
|
+
cwe: "CWE-1004",
|
|
26018
|
+
severity: "medium",
|
|
26019
|
+
level: "warning",
|
|
26020
|
+
message: "Insecure cookie: Cookie::build chain disables Secure / HttpOnly flag(s)",
|
|
26021
|
+
file,
|
|
26022
|
+
line: i2 + 1,
|
|
26023
|
+
snippet: trimmed.substring(0, 100)
|
|
26024
|
+
});
|
|
26025
|
+
}
|
|
26026
|
+
return out2;
|
|
26027
|
+
}
|
|
26028
|
+
function findRustJwtVerifyDisabledFindings(code, file) {
|
|
26029
|
+
const out2 = [];
|
|
26030
|
+
const lines = code.split("\n");
|
|
26031
|
+
const re = /\.\s*insecure_disable_signature_validation\s*\(/;
|
|
26032
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
26033
|
+
const raw = lines[i2];
|
|
26034
|
+
const trimmed = raw.trim();
|
|
26035
|
+
if (!trimmed || trimmed.startsWith("//")) continue;
|
|
26036
|
+
if (!re.test(trimmed)) continue;
|
|
26037
|
+
out2.push({
|
|
26038
|
+
id: `jwt-verify-disabled-${file}-${i2 + 1}`,
|
|
26039
|
+
pass: "language-sources",
|
|
26040
|
+
category: "security",
|
|
26041
|
+
rule_id: "jwt-verify-disabled",
|
|
26042
|
+
cwe: "CWE-347",
|
|
26043
|
+
severity: "critical",
|
|
26044
|
+
level: "error",
|
|
26045
|
+
message: "JWT signature verification disabled: Validation::insecure_disable_signature_validation() forfeits signature enforcement",
|
|
26046
|
+
file,
|
|
26047
|
+
line: i2 + 1,
|
|
26048
|
+
snippet: trimmed.substring(0, 100)
|
|
26049
|
+
});
|
|
26050
|
+
}
|
|
26051
|
+
return out2;
|
|
26052
|
+
}
|
|
26053
|
+
function findRustWeakCryptoEcbFindings(code, file) {
|
|
26054
|
+
const out2 = [];
|
|
26055
|
+
const lines = code.split("\n");
|
|
26056
|
+
const ctorRe = /\bAes(?:128|192|256)(?:Ecb)?\s*::\s*new\s*\(/;
|
|
26057
|
+
const blockOpRe = /\.\s*(encrypt_block|decrypt_block)\s*\(/;
|
|
26058
|
+
let sawCtor = false;
|
|
26059
|
+
for (const line of lines) {
|
|
26060
|
+
if (ctorRe.test(line)) {
|
|
26061
|
+
sawCtor = true;
|
|
26062
|
+
break;
|
|
26063
|
+
}
|
|
26064
|
+
}
|
|
26065
|
+
if (!sawCtor) return out2;
|
|
26066
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
26067
|
+
const raw = lines[i2];
|
|
26068
|
+
const trimmed = raw.trim();
|
|
26069
|
+
if (!trimmed || trimmed.startsWith("//")) continue;
|
|
26070
|
+
if (!blockOpRe.test(trimmed)) continue;
|
|
26071
|
+
out2.push({
|
|
26072
|
+
id: `weak-crypto-${file}-${i2 + 1}`,
|
|
26073
|
+
pass: "language-sources",
|
|
26074
|
+
category: "security",
|
|
26075
|
+
rule_id: "weak-crypto",
|
|
26076
|
+
cwe: "CWE-327",
|
|
26077
|
+
severity: "high",
|
|
26078
|
+
level: "error",
|
|
26079
|
+
message: "Weak crypto (ECB mode): raw Aes::encrypt_block/decrypt_block leaks repeating-block patterns. Use AES-GCM, AES-CTR, or AES-CBC with an HMAC.",
|
|
26080
|
+
file,
|
|
26081
|
+
line: i2 + 1,
|
|
26082
|
+
snippet: trimmed.substring(0, 100)
|
|
26083
|
+
});
|
|
26084
|
+
}
|
|
26085
|
+
return out2;
|
|
26086
|
+
}
|
|
26087
|
+
function findJavaPatternFindings(code, file) {
|
|
26088
|
+
const out2 = [];
|
|
26089
|
+
const lines = code.split("\n");
|
|
26090
|
+
const jwtDecodeRe = /\bJWT\s*\.\s*decode\s*\(/;
|
|
26091
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
26092
|
+
const raw = lines[i2];
|
|
26093
|
+
const trimmed = raw.trim();
|
|
26094
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("*")) continue;
|
|
26095
|
+
if (!jwtDecodeRe.test(trimmed)) continue;
|
|
26096
|
+
if (/\.\s*verify\s*\(/.test(trimmed)) continue;
|
|
26097
|
+
out2.push({
|
|
26098
|
+
id: `jwt-verify-disabled-${file}-${i2 + 1}-decode`,
|
|
26099
|
+
pass: "language-sources",
|
|
26100
|
+
category: "security",
|
|
26101
|
+
rule_id: "jwt-verify-disabled",
|
|
26102
|
+
cwe: "CWE-347",
|
|
26103
|
+
severity: "critical",
|
|
26104
|
+
level: "error",
|
|
26105
|
+
message: "JWT signature not verified: auth0 `JWT.decode(token)` parses without checking the signature. Use `JWT.require(<algorithm>).build().verify(token)` to enforce verification.",
|
|
26106
|
+
file,
|
|
26107
|
+
line: i2 + 1,
|
|
26108
|
+
snippet: trimmed.substring(0, 100)
|
|
26109
|
+
});
|
|
26110
|
+
}
|
|
26111
|
+
const anonStartRe = /\bnew\s+X509TrustManager\s*\(\s*\)\s*\{/;
|
|
26112
|
+
const checkServerSig = /\bcheckServerTrusted\s*\([^)]*\)\s*(?:throws\s+[^\{]*)?\{\s*\}/;
|
|
26113
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
26114
|
+
const raw = lines[i2];
|
|
26115
|
+
if (!anonStartRe.test(raw)) continue;
|
|
26116
|
+
const end = Math.min(lines.length, i2 + 16);
|
|
26117
|
+
let foundAt = -1;
|
|
26118
|
+
for (let j = i2; j < end; j++) {
|
|
26119
|
+
if (checkServerSig.test(lines[j])) {
|
|
26120
|
+
foundAt = j;
|
|
26121
|
+
break;
|
|
26122
|
+
}
|
|
26123
|
+
}
|
|
26124
|
+
if (foundAt < 0) continue;
|
|
26125
|
+
out2.push({
|
|
26126
|
+
id: `tls-verify-disabled-${file}-${foundAt + 1}`,
|
|
26127
|
+
pass: "language-sources",
|
|
26128
|
+
category: "security",
|
|
26129
|
+
rule_id: "tls-verify-disabled",
|
|
26130
|
+
cwe: "CWE-295",
|
|
26131
|
+
severity: "high",
|
|
26132
|
+
level: "error",
|
|
26133
|
+
message: "TLS certificate verification disabled: anonymous X509TrustManager with empty checkServerTrusted body accepts every certificate.",
|
|
26134
|
+
file,
|
|
26135
|
+
line: foundAt + 1,
|
|
26136
|
+
snippet: lines[foundAt].trim().substring(0, 100)
|
|
26137
|
+
});
|
|
26138
|
+
}
|
|
26139
|
+
return out2;
|
|
26140
|
+
}
|
|
26141
|
+
function findGoPatternFindings(code, file) {
|
|
26142
|
+
const out2 = [];
|
|
26143
|
+
const lines = code.split("\n");
|
|
26144
|
+
const cipherVars = /* @__PURE__ */ new Set();
|
|
26145
|
+
const ctorRe = /\b([a-zA-Z_]\w*)\s*(?:,\s*[a-zA-Z_]\w*)?\s*:?=\s*aes\.NewCipher\s*\(/;
|
|
26146
|
+
for (const line of lines) {
|
|
26147
|
+
const m = line.match(ctorRe);
|
|
26148
|
+
if (m) cipherVars.add(m[1]);
|
|
26149
|
+
}
|
|
26150
|
+
if (cipherVars.size === 0) return out2;
|
|
26151
|
+
for (const v of Array.from(cipherVars)) {
|
|
26152
|
+
const wrapRe = new RegExp(
|
|
26153
|
+
`\\bcipher\\.New(?:GCM|CBCEncrypter|CBCDecrypter|CTR|OFB|CFBEncrypter|CFBDecrypter)\\s*\\(\\s*${v}\\b`
|
|
26154
|
+
);
|
|
26155
|
+
for (const line of lines) {
|
|
26156
|
+
if (wrapRe.test(line)) {
|
|
26157
|
+
cipherVars.delete(v);
|
|
26158
|
+
break;
|
|
26159
|
+
}
|
|
26160
|
+
}
|
|
26161
|
+
}
|
|
26162
|
+
if (cipherVars.size === 0) return out2;
|
|
26163
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
26164
|
+
const raw = lines[i2];
|
|
26165
|
+
const trimmed = raw.trim();
|
|
26166
|
+
if (!trimmed || trimmed.startsWith("//")) continue;
|
|
26167
|
+
for (const v of cipherVars) {
|
|
26168
|
+
const opRe = new RegExp(`\\b${v}\\s*\\.\\s*(?:Encrypt|Decrypt)\\s*\\(`);
|
|
26169
|
+
if (!opRe.test(trimmed)) continue;
|
|
26170
|
+
out2.push({
|
|
26171
|
+
id: `weak-crypto-${file}-${i2 + 1}`,
|
|
26172
|
+
pass: "language-sources",
|
|
26173
|
+
category: "security",
|
|
26174
|
+
rule_id: "weak-crypto",
|
|
26175
|
+
cwe: "CWE-327",
|
|
26176
|
+
severity: "high",
|
|
26177
|
+
level: "error",
|
|
26178
|
+
message: "Weak crypto (ECB mode): raw aes.Cipher.Encrypt/Decrypt on a block leaks repeating-block patterns. Wrap with cipher.NewGCM, cipher.NewCTR, or cipher.NewCBCEncrypter + HMAC.",
|
|
26179
|
+
file,
|
|
26180
|
+
line: i2 + 1,
|
|
26181
|
+
snippet: trimmed.substring(0, 100)
|
|
26182
|
+
});
|
|
26183
|
+
break;
|
|
26184
|
+
}
|
|
26185
|
+
}
|
|
26186
|
+
return out2;
|
|
26187
|
+
}
|
|
26188
|
+
function findJsPatternFindings(code, file) {
|
|
26189
|
+
const out2 = [];
|
|
26190
|
+
const lines = code.split("\n");
|
|
26191
|
+
const parseRe = /\blibxml(?:js)?\s*\.\s*parseXml(?:String)?\s*\(/;
|
|
26192
|
+
const noentTrueRe = /\bnoent\s*:\s*true\b/;
|
|
26193
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
26194
|
+
const raw = lines[i2];
|
|
26195
|
+
const trimmed = raw.trim();
|
|
26196
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("*")) continue;
|
|
26197
|
+
if (!parseRe.test(trimmed)) continue;
|
|
26198
|
+
if (!noentTrueRe.test(trimmed)) continue;
|
|
26199
|
+
out2.push({
|
|
26200
|
+
id: `xml-entity-expansion-${file}-${i2 + 1}`,
|
|
26201
|
+
pass: "language-sources",
|
|
26202
|
+
category: "security",
|
|
26203
|
+
rule_id: "xml-entity-expansion",
|
|
26204
|
+
cwe: "CWE-611",
|
|
26205
|
+
severity: "high",
|
|
26206
|
+
level: "error",
|
|
26207
|
+
message: "XML external entity resolution enabled: libxmljs parseXml called with `noent: true` resolves external entities (XXE / billion-laughs). Omit the flag or set `noent: false`.",
|
|
26208
|
+
file,
|
|
26209
|
+
line: i2 + 1,
|
|
26210
|
+
snippet: trimmed.substring(0, 100)
|
|
26211
|
+
});
|
|
26212
|
+
}
|
|
26213
|
+
return out2;
|
|
26214
|
+
}
|
|
25810
26215
|
|
|
25811
26216
|
// src/analysis/passes/sink-filter-pass.ts
|
|
25812
26217
|
var JS_XSS_SANITIZERS = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circle-ir",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.129.0",
|
|
4
4
|
"description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|