circle-ir 3.121.0 → 3.124.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/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +61 -16
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts +30 -0
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +940 -0
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +4 -0
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/browser/circle-ir.js +672 -3
- package/dist/core/circle-ir-core.cjs +5 -1
- package/dist/core/circle-ir-core.js +5 -1
- package/package.json +1 -1
|
@@ -12924,7 +12924,11 @@ var PYTHON_TAINTED_PATTERNS = [
|
|
|
12924
12924
|
{ pattern: /\brequest\.META\b/, sourceType: "http_header" },
|
|
12925
12925
|
{ pattern: /\brequest\.FILES\b/, sourceType: "file_input" },
|
|
12926
12926
|
{ pattern: /\brequest\.query_params\b/, sourceType: "http_param" },
|
|
12927
|
-
{ pattern: /\brequest\.path_params\b/, sourceType: "http_param" }
|
|
12927
|
+
{ pattern: /\brequest\.path_params\b/, sourceType: "http_param" },
|
|
12928
|
+
// Sprint 72 (#183 residual): builtin `input()` is a stdin user-input
|
|
12929
|
+
// source. Already registered in configs/sources/python.json; add to the
|
|
12930
|
+
// regex registry so it's recognized in subscript/var contexts.
|
|
12931
|
+
{ pattern: /\binput\s*\(/, sourceType: "io_input" }
|
|
12928
12932
|
];
|
|
12929
12933
|
function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy, language, code) {
|
|
12930
12934
|
const sourceLines = code !== void 0 ? code.split("\n") : void 0;
|
|
@@ -16226,7 +16230,32 @@ function analyzeInterprocedural(graphOrTypes, callsOrSources, dfgOrSinks, source
|
|
|
16226
16230
|
if (isBash && bashSafeBuiltins.has(call.method_name)) {
|
|
16227
16231
|
continue;
|
|
16228
16232
|
}
|
|
16229
|
-
|
|
16233
|
+
if (isBash && taintedArgPositions.length > 0) {
|
|
16234
|
+
const terminatorPos = call.arguments.filter((a) => a.expression === "--").map((a) => a.position).reduce((min, p) => min === null || p < min ? p : min, null);
|
|
16235
|
+
const earliestTainted = Math.min(...taintedArgPositions);
|
|
16236
|
+
if (terminatorPos !== null && terminatorPos < earliestTainted) {
|
|
16237
|
+
const allTaintedQuoted = taintedArgPositions.every((p) => {
|
|
16238
|
+
const a = call.arguments[p];
|
|
16239
|
+
if (!a) return false;
|
|
16240
|
+
const expr = a.expression.trim();
|
|
16241
|
+
return expr.startsWith('"') && expr.endsWith('"');
|
|
16242
|
+
});
|
|
16243
|
+
if (allTaintedQuoted) {
|
|
16244
|
+
continue;
|
|
16245
|
+
}
|
|
16246
|
+
}
|
|
16247
|
+
}
|
|
16248
|
+
const bashSqlCliTools = /* @__PURE__ */ new Set(["sqlite3", "mysql", "psql", "mariadb"]);
|
|
16249
|
+
const isBashSqlCli = isBash && bashSqlCliTools.has(call.method_name);
|
|
16250
|
+
const sink = isBashSqlCli ? {
|
|
16251
|
+
type: "sql_injection",
|
|
16252
|
+
cwe: "CWE-89",
|
|
16253
|
+
location: `Tainted data (${taintedArgVars.join(", ")}) interpolated into SQL query passed to ${call.method_name}`,
|
|
16254
|
+
line: call.location.line,
|
|
16255
|
+
confidence: 0.7,
|
|
16256
|
+
method: call.method_name,
|
|
16257
|
+
argPositions: taintedArgPositions
|
|
16258
|
+
} : isBash ? {
|
|
16230
16259
|
type: "command_injection",
|
|
16231
16260
|
cwe: "CWE-78",
|
|
16232
16261
|
location: `Tainted data (${taintedArgVars.join(", ")}) passed unquoted to shell utility ${call.method_name}`,
|
|
@@ -23528,7 +23557,12 @@ var PYTHON_TAINTED_PATTERNS2 = [
|
|
|
23528
23557
|
{ pattern: /\bget_form_parameter\s*\(/, type: "http_body" },
|
|
23529
23558
|
{ pattern: /\bget_query_parameter\s*\(/, type: "http_param" },
|
|
23530
23559
|
{ pattern: /\bget_header_value\s*\(/, type: "http_header" },
|
|
23531
|
-
{ pattern: /\bget_cookie_value\s*\(/, type: "http_cookie" }
|
|
23560
|
+
{ pattern: /\bget_cookie_value\s*\(/, type: "http_cookie" },
|
|
23561
|
+
// Sprint 72 (#183 residual): `input()` is registered in
|
|
23562
|
+
// configs/sources/python.json but was not in the forward-taint regex
|
|
23563
|
+
// registry, so `name = input()` was not added to pyTaintedVars. Closes
|
|
23564
|
+
// the deferred `getattr(obj, input())()` reflection-invocation shape.
|
|
23565
|
+
{ pattern: /\binput\s*\(/, type: "io_input" }
|
|
23532
23566
|
];
|
|
23533
23567
|
var LanguageSourcesPass = class {
|
|
23534
23568
|
name = "language-sources";
|
|
@@ -23587,6 +23621,21 @@ var LanguageSourcesPass = class {
|
|
|
23587
23621
|
});
|
|
23588
23622
|
}
|
|
23589
23623
|
}
|
|
23624
|
+
for (const r of findPythonReflectionInvocationSinks(code, pyTaintedVars)) {
|
|
23625
|
+
const alreadyExists = additionalSinks.some(
|
|
23626
|
+
(s) => s.line === r.sinkLine && s.type === "code_injection" && s.method === r.method
|
|
23627
|
+
);
|
|
23628
|
+
if (!alreadyExists) {
|
|
23629
|
+
additionalSinks.push({
|
|
23630
|
+
type: "code_injection",
|
|
23631
|
+
cwe: "CWE-94",
|
|
23632
|
+
line: r.sinkLine,
|
|
23633
|
+
location: `reflection invocation (getattr result called) with tainted attribute name at line ${r.sinkLine}`,
|
|
23634
|
+
method: r.method,
|
|
23635
|
+
confidence: 0.85
|
|
23636
|
+
});
|
|
23637
|
+
}
|
|
23638
|
+
}
|
|
23590
23639
|
}
|
|
23591
23640
|
const jsTaintedVars = buildJavaScriptTaintedVars(code, language);
|
|
23592
23641
|
if (language === "bash") {
|
|
@@ -23605,10 +23654,37 @@ var LanguageSourcesPass = class {
|
|
|
23605
23654
|
if (language === "python") {
|
|
23606
23655
|
additionalSanitizers.push(...findPythonNetlocAllowlistGuardSanitizers(code));
|
|
23607
23656
|
additionalSanitizers.push(...findPythonRangeCheckGuardSanitizers(code));
|
|
23657
|
+
const pyMisconfigFindings = findPythonPatternFindings(code, graph.ir.meta.file);
|
|
23658
|
+
for (const finding of pyMisconfigFindings) {
|
|
23659
|
+
ctx.addFinding(finding);
|
|
23660
|
+
}
|
|
23608
23661
|
}
|
|
23609
23662
|
if (language === "rust") {
|
|
23610
23663
|
additionalSanitizers.push(...findRustSetAllowlistGuardSanitizers(code));
|
|
23611
23664
|
additionalSanitizers.push(...findRustCanonicalizeGuardSanitizers(code));
|
|
23665
|
+
const rustMisconfigFindings = findRustPatternFindings(code, graph.ir.meta.file);
|
|
23666
|
+
for (const finding of rustMisconfigFindings) {
|
|
23667
|
+
ctx.addFinding(finding);
|
|
23668
|
+
}
|
|
23669
|
+
}
|
|
23670
|
+
if (language === "javascript" || language === "typescript" || language === "htmljs") {
|
|
23671
|
+
additionalSanitizers.push(...findJsSafeJsonParseSanitizers(code));
|
|
23672
|
+
additionalSanitizers.push(...findJsCryptoHashSanitizers(code));
|
|
23673
|
+
additionalSanitizers.push(...findJsCsvFormulaPrefixSanitizers(code));
|
|
23674
|
+
additionalSanitizers.push(...findJsWrapperFunctionSanitizers(code));
|
|
23675
|
+
}
|
|
23676
|
+
if (language === "java") {
|
|
23677
|
+
additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
|
|
23678
|
+
}
|
|
23679
|
+
if (language === "python" || language === "javascript" || language === "typescript" || language === "go") {
|
|
23680
|
+
const exfilFindings = findExternalSecretExfiltrationFindings(
|
|
23681
|
+
code,
|
|
23682
|
+
graph.ir.meta.file,
|
|
23683
|
+
language
|
|
23684
|
+
);
|
|
23685
|
+
for (const finding of exfilFindings) {
|
|
23686
|
+
ctx.addFinding(finding);
|
|
23687
|
+
}
|
|
23612
23688
|
}
|
|
23613
23689
|
attachSourceLineCode(additionalSources, additionalSinks, code);
|
|
23614
23690
|
return { additionalSources, additionalSinks, additionalSanitizers, pyTaintedVars, pySanitizedVars, jsTaintedVars };
|
|
@@ -24166,6 +24242,44 @@ function findPythonReturnXSSSinks(sourceCode, taintedVars) {
|
|
|
24166
24242
|
}
|
|
24167
24243
|
return sinks;
|
|
24168
24244
|
}
|
|
24245
|
+
function findPythonReflectionInvocationSinks(sourceCode, taintedVars) {
|
|
24246
|
+
if (taintedVars.size === 0) return [];
|
|
24247
|
+
const sinks = [];
|
|
24248
|
+
const lines = sourceCode.split("\n");
|
|
24249
|
+
const directRe = /\bgetattr\s*\(\s*[^,()]+\s*,\s*([A-Za-z_][\w]*)\s*\)\s*\(/;
|
|
24250
|
+
const bindRe = /^\s*([A-Za-z_][\w]*)\s*=\s*getattr\s*\(\s*[^,()]+\s*,\s*([A-Za-z_][\w]*)\s*\)\s*$/;
|
|
24251
|
+
const aliases = [];
|
|
24252
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
24253
|
+
const line = lines[i2];
|
|
24254
|
+
if (line.trimStart().startsWith("#")) continue;
|
|
24255
|
+
const dm = line.match(directRe);
|
|
24256
|
+
if (dm && taintedVars.has(dm[1])) {
|
|
24257
|
+
sinks.push({ sinkLine: i2 + 1, method: "getattr" });
|
|
24258
|
+
continue;
|
|
24259
|
+
}
|
|
24260
|
+
const bm = line.match(bindRe);
|
|
24261
|
+
if (bm && taintedVars.has(bm[2])) {
|
|
24262
|
+
aliases.push({ name: bm[1], bindLine: i2 + 1 });
|
|
24263
|
+
}
|
|
24264
|
+
}
|
|
24265
|
+
if (aliases.length === 0) return sinks;
|
|
24266
|
+
const firedBindLines = /* @__PURE__ */ new Set();
|
|
24267
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
24268
|
+
const line = lines[i2];
|
|
24269
|
+
if (line.trimStart().startsWith("#")) continue;
|
|
24270
|
+
const lineNum = i2 + 1;
|
|
24271
|
+
for (const a of aliases) {
|
|
24272
|
+
if (lineNum <= a.bindLine) continue;
|
|
24273
|
+
if (firedBindLines.has(a.bindLine)) continue;
|
|
24274
|
+
const invokeRe = new RegExp(`(?<![\\w.])${a.name}\\s*\\(`);
|
|
24275
|
+
if (invokeRe.test(line)) {
|
|
24276
|
+
sinks.push({ sinkLine: a.bindLine, method: "getattr" });
|
|
24277
|
+
firedBindLines.add(a.bindLine);
|
|
24278
|
+
}
|
|
24279
|
+
}
|
|
24280
|
+
}
|
|
24281
|
+
return sinks;
|
|
24282
|
+
}
|
|
24169
24283
|
function findJavaScriptDOMSinks(sourceCode, language) {
|
|
24170
24284
|
if (!["javascript", "typescript"].includes(language)) return [];
|
|
24171
24285
|
const sinks = [];
|
|
@@ -24383,6 +24497,7 @@ function findBashPatternFindings(sourceCode, file) {
|
|
|
24383
24497
|
const findings = [];
|
|
24384
24498
|
const lines = sourceCode.split("\n");
|
|
24385
24499
|
const checksumVerifiedTmpPaths = collectChecksumVerifiedTmpPaths(lines);
|
|
24500
|
+
const scriptHasVerifier = hasIntegrityVerifierAnywhere(lines);
|
|
24386
24501
|
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
24387
24502
|
const line = lines[i2];
|
|
24388
24503
|
const trimmed = line.trim();
|
|
@@ -24475,9 +24590,78 @@ function findBashPatternFindings(sourceCode, file) {
|
|
|
24475
24590
|
snippet: trimmed.substring(0, 80)
|
|
24476
24591
|
});
|
|
24477
24592
|
}
|
|
24593
|
+
if (!scriptHasVerifier) {
|
|
24594
|
+
const installer = matchUnverifiedPackageInstall(trimmed);
|
|
24595
|
+
if (installer) {
|
|
24596
|
+
findings.push({
|
|
24597
|
+
id: `unverified-package-install-${file}-${lineNumber}`,
|
|
24598
|
+
pass: "language-sources",
|
|
24599
|
+
category: "security",
|
|
24600
|
+
rule_id: "unverified-package-install",
|
|
24601
|
+
cwe: "CWE-494",
|
|
24602
|
+
severity: "high",
|
|
24603
|
+
level: "error",
|
|
24604
|
+
message: `Unverified package install via ${installer}: package contents are not integrity-checked (no gpg/sha256sum verify in script)`,
|
|
24605
|
+
file,
|
|
24606
|
+
line: lineNumber,
|
|
24607
|
+
snippet: trimmed.substring(0, 80)
|
|
24608
|
+
});
|
|
24609
|
+
}
|
|
24610
|
+
}
|
|
24611
|
+
const weakHashAlg = matchBashWeakHashCommand(trimmed);
|
|
24612
|
+
if (weakHashAlg) {
|
|
24613
|
+
findings.push({
|
|
24614
|
+
id: `weak-hash-${file}-${lineNumber}`,
|
|
24615
|
+
pass: "language-sources",
|
|
24616
|
+
category: "security",
|
|
24617
|
+
rule_id: "weak-hash",
|
|
24618
|
+
cwe: "CWE-328",
|
|
24619
|
+
severity: "medium",
|
|
24620
|
+
level: "warning",
|
|
24621
|
+
message: `Weak hash algorithm: ${weakHashAlg} is cryptographically broken. Use sha256sum or sha512sum`,
|
|
24622
|
+
file,
|
|
24623
|
+
line: lineNumber,
|
|
24624
|
+
snippet: trimmed.substring(0, 80)
|
|
24625
|
+
});
|
|
24626
|
+
}
|
|
24478
24627
|
}
|
|
24479
24628
|
return findings;
|
|
24480
24629
|
}
|
|
24630
|
+
function matchBashWeakHashCommand(line) {
|
|
24631
|
+
const re = /(?:^|[|;]|&&|\|\|)\s*(md5sum|sha1sum|md5|sha1)\b(?!\s*=)/;
|
|
24632
|
+
const m = line.match(re);
|
|
24633
|
+
if (!m) return null;
|
|
24634
|
+
return m[1];
|
|
24635
|
+
}
|
|
24636
|
+
function matchUnverifiedPackageInstall(line) {
|
|
24637
|
+
if (/\bdpkg\b/.test(line) && /(?:^|\s)(?:-[a-zA-Z]*[iIU][a-zA-Z]*|--install)\b/.test(line)) {
|
|
24638
|
+
return "dpkg";
|
|
24639
|
+
}
|
|
24640
|
+
if (/\brpm\b/.test(line) && /(?:^|\s)(?:-[a-zA-Z]*[iU][a-zA-Z]*|--install|--upgrade)\b/.test(line) && !/--(?:verify|checksig|erase|query)\b/.test(line)) {
|
|
24641
|
+
return "rpm";
|
|
24642
|
+
}
|
|
24643
|
+
const aptMatch = line.match(/\b(apt-get|apt|aptitude)\s+install\b/);
|
|
24644
|
+
if (aptMatch && /\.deb\b/.test(line)) {
|
|
24645
|
+
return aptMatch[1];
|
|
24646
|
+
}
|
|
24647
|
+
const yumMatch = line.match(/\b(yum|dnf|zypper)\s+install\b/);
|
|
24648
|
+
if (yumMatch && /\.rpm\b/.test(line)) {
|
|
24649
|
+
return yumMatch[1];
|
|
24650
|
+
}
|
|
24651
|
+
return null;
|
|
24652
|
+
}
|
|
24653
|
+
function hasIntegrityVerifierAnywhere(lines) {
|
|
24654
|
+
const sigRe = /\b(?:gpg(?:v|2)?|gpg)\s+(?:[^|]*\s)?--verify\b/;
|
|
24655
|
+
const rpmSigRe = /\brpm\s+(?:[^|]*\s)?--checksig\b/;
|
|
24656
|
+
const dpkgSigRe = /\bdpkg\s+(?:[^|]*\s)?--verify\b/;
|
|
24657
|
+
const sumRe = /\b(?:sha(?:1|224|256|384|512)sum|md5sum|cksum|b2sum)\s+(?:[^|]*\s)?(?:-c|--check)\b/;
|
|
24658
|
+
for (const line of lines) {
|
|
24659
|
+
if (sigRe.test(line) || rpmSigRe.test(line) || dpkgSigRe.test(line) || sumRe.test(line)) {
|
|
24660
|
+
return true;
|
|
24661
|
+
}
|
|
24662
|
+
}
|
|
24663
|
+
return false;
|
|
24664
|
+
}
|
|
24481
24665
|
function findBashRegexAllowlistSanitizers(code) {
|
|
24482
24666
|
const sanitizers = [];
|
|
24483
24667
|
const lines = code.split("\n");
|
|
@@ -24830,6 +25014,491 @@ function findRustCanonicalizeGuardSanitizers(code) {
|
|
|
24830
25014
|
}
|
|
24831
25015
|
return sanitizers;
|
|
24832
25016
|
}
|
|
25017
|
+
function findJavaSafeJsonParseSanitizers(code) {
|
|
25018
|
+
const sanitizers = [];
|
|
25019
|
+
const lines = code.split("\n");
|
|
25020
|
+
const safeParse = /\.(?:readValue|fromJson)\s*\(/;
|
|
25021
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25022
|
+
if (!safeParse.test(lines[i2])) continue;
|
|
25023
|
+
sanitizers.push({
|
|
25024
|
+
type: "java_safe_json_parse",
|
|
25025
|
+
method: "readValue",
|
|
25026
|
+
line: i2 + 1,
|
|
25027
|
+
sanitizes: ["external_taint_escape"]
|
|
25028
|
+
});
|
|
25029
|
+
}
|
|
25030
|
+
return sanitizers;
|
|
25031
|
+
}
|
|
25032
|
+
function findJsSafeJsonParseSanitizers(code) {
|
|
25033
|
+
const sanitizers = [];
|
|
25034
|
+
const lines = code.split("\n");
|
|
25035
|
+
const safeParse = /\bJSON\.parse\s*\(/;
|
|
25036
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25037
|
+
if (!safeParse.test(lines[i2])) continue;
|
|
25038
|
+
sanitizers.push({
|
|
25039
|
+
type: "js_safe_json_parse",
|
|
25040
|
+
method: "JSON.parse",
|
|
25041
|
+
line: i2 + 1,
|
|
25042
|
+
sanitizes: ["external_taint_escape"]
|
|
25043
|
+
});
|
|
25044
|
+
}
|
|
25045
|
+
return sanitizers;
|
|
25046
|
+
}
|
|
25047
|
+
function findJsCryptoHashSanitizers(code) {
|
|
25048
|
+
const sanitizers = [];
|
|
25049
|
+
const lines = code.split("\n");
|
|
25050
|
+
const hashCall = /\b(?:bcrypt|argon2)\s*\.\s*hash(?:Sync)?\s*\(|\bcrypto\s*\.\s*(?:scrypt(?:Sync)?|createHash)\s*\(|\.digest\s*\(/;
|
|
25051
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25052
|
+
if (!hashCall.test(lines[i2])) continue;
|
|
25053
|
+
sanitizers.push({
|
|
25054
|
+
type: "js_crypto_hash",
|
|
25055
|
+
method: "hash",
|
|
25056
|
+
line: i2 + 1,
|
|
25057
|
+
sanitizes: ["external_taint_escape"]
|
|
25058
|
+
});
|
|
25059
|
+
}
|
|
25060
|
+
return sanitizers;
|
|
25061
|
+
}
|
|
25062
|
+
function findJsCsvFormulaPrefixSanitizers(code) {
|
|
25063
|
+
const sanitizers = [];
|
|
25064
|
+
const lines = code.split("\n");
|
|
25065
|
+
const tickPrefix = /`'\$\{/;
|
|
25066
|
+
const concatPrefix = /["']\\?'["']\s*\+\s*[A-Za-z_]/;
|
|
25067
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25068
|
+
if (tickPrefix.test(lines[i2]) || concatPrefix.test(lines[i2])) {
|
|
25069
|
+
sanitizers.push({
|
|
25070
|
+
type: "js_csv_formula_prefix",
|
|
25071
|
+
method: "'-prefix",
|
|
25072
|
+
line: i2 + 1,
|
|
25073
|
+
sanitizes: ["external_taint_escape"]
|
|
25074
|
+
});
|
|
25075
|
+
}
|
|
25076
|
+
}
|
|
25077
|
+
return sanitizers;
|
|
25078
|
+
}
|
|
25079
|
+
function findJsWrapperFunctionSanitizers(code) {
|
|
25080
|
+
const sanitizers = [];
|
|
25081
|
+
const lines = code.split("\n");
|
|
25082
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
25083
|
+
const fnDeclRe = /\bfunction\s+([A-Za-z_]\w*)\s*\(/;
|
|
25084
|
+
const arrowDeclRe = /\b(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*(?:\([^)]*\)|[A-Za-z_]\w*)\s*=>/;
|
|
25085
|
+
const xssCharClass = /\.replace\s*\(\s*\/[^/]*[&<>"'][^/]*\//;
|
|
25086
|
+
const crlfCharClass = /\.replace\s*\(\s*\/[^/]*(?:\\r|\\n|\\t)[^/]*\//;
|
|
25087
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25088
|
+
const m = fnDeclRe.exec(lines[i2]) ?? arrowDeclRe.exec(lines[i2]);
|
|
25089
|
+
if (!m) continue;
|
|
25090
|
+
const name2 = m[1];
|
|
25091
|
+
const body2 = lines.slice(i2, Math.min(lines.length, i2 + 8)).join("\n");
|
|
25092
|
+
const kinds = /* @__PURE__ */ new Set();
|
|
25093
|
+
if (xssCharClass.test(body2)) {
|
|
25094
|
+
kinds.add("xss");
|
|
25095
|
+
kinds.add("external_taint_escape");
|
|
25096
|
+
}
|
|
25097
|
+
if (crlfCharClass.test(body2)) {
|
|
25098
|
+
kinds.add("log_injection");
|
|
25099
|
+
kinds.add("external_taint_escape");
|
|
25100
|
+
}
|
|
25101
|
+
if (kinds.size > 0) wrappers.set(name2, kinds);
|
|
25102
|
+
}
|
|
25103
|
+
if (wrappers.size === 0) return sanitizers;
|
|
25104
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25105
|
+
for (const [name2, kinds] of wrappers) {
|
|
25106
|
+
const declSelf = new RegExp(`\\b(?:function|const|let|var)\\s+${name2}\\b`);
|
|
25107
|
+
if (declSelf.test(lines[i2])) continue;
|
|
25108
|
+
const callRe = new RegExp(`\\b${name2}\\s*\\(`);
|
|
25109
|
+
if (!callRe.test(lines[i2])) continue;
|
|
25110
|
+
sanitizers.push({
|
|
25111
|
+
type: "js_wrapper_function",
|
|
25112
|
+
method: name2,
|
|
25113
|
+
line: i2 + 1,
|
|
25114
|
+
sanitizes: [...kinds]
|
|
25115
|
+
});
|
|
25116
|
+
}
|
|
25117
|
+
}
|
|
25118
|
+
return sanitizers;
|
|
25119
|
+
}
|
|
25120
|
+
function collectEnvSecretVars(lines, language) {
|
|
25121
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
25122
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25123
|
+
const line = lines[i2];
|
|
25124
|
+
if (language === "python") {
|
|
25125
|
+
const m = line.match(/^\s*(\w+)\s*=\s*os\.(?:environ\s*[\[.]|getenv\b)/);
|
|
25126
|
+
if (m) out2.set(m[1], i2 + 1);
|
|
25127
|
+
} else if (language === "javascript" || language === "typescript") {
|
|
25128
|
+
const m = line.match(/(?:^|\s|;)(?:const|let|var)\s+(\w+)\s*=\s*process\.env\b/);
|
|
25129
|
+
if (m) out2.set(m[1], i2 + 1);
|
|
25130
|
+
} else if (language === "go") {
|
|
25131
|
+
const m = line.match(/^\s*(\w+)\s*:?=\s*os\.Getenv\s*\(/);
|
|
25132
|
+
if (m) out2.set(m[1], i2 + 1);
|
|
25133
|
+
}
|
|
25134
|
+
}
|
|
25135
|
+
return out2;
|
|
25136
|
+
}
|
|
25137
|
+
function isExternalHostUrl(url) {
|
|
25138
|
+
const lower = url.toLowerCase();
|
|
25139
|
+
if (!/^https?:\/\//.test(lower)) return false;
|
|
25140
|
+
const host = lower.replace(/^https?:\/\//, "").split(/[/?#:]/)[0];
|
|
25141
|
+
if (!host) return false;
|
|
25142
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0" || host === "::1") return false;
|
|
25143
|
+
if (/^10\./.test(host)) return false;
|
|
25144
|
+
if (/^192\.168\./.test(host)) return false;
|
|
25145
|
+
if (/^172\.(1[6-9]|2\d|3[01])\./.test(host)) return false;
|
|
25146
|
+
if (host.includes(".internal.") || host.endsWith(".internal") || host.startsWith("internal.")) return false;
|
|
25147
|
+
if (host.endsWith(".local") || host.endsWith(".lan") || host.endsWith(".corp")) return false;
|
|
25148
|
+
if (!host.includes(".")) return false;
|
|
25149
|
+
return true;
|
|
25150
|
+
}
|
|
25151
|
+
function findBalancedCallEnd(code, start2) {
|
|
25152
|
+
let depth = 1;
|
|
25153
|
+
let i2 = start2;
|
|
25154
|
+
let inStr = null;
|
|
25155
|
+
while (i2 < code.length) {
|
|
25156
|
+
const ch = code[i2];
|
|
25157
|
+
if (inStr) {
|
|
25158
|
+
if (ch === "\\") {
|
|
25159
|
+
i2 += 2;
|
|
25160
|
+
continue;
|
|
25161
|
+
}
|
|
25162
|
+
if (ch === inStr) inStr = null;
|
|
25163
|
+
} else {
|
|
25164
|
+
if (ch === '"' || ch === "'" || ch === "`") inStr = ch;
|
|
25165
|
+
else if (ch === "(") depth++;
|
|
25166
|
+
else if (ch === ")") {
|
|
25167
|
+
depth--;
|
|
25168
|
+
if (depth === 0) return i2;
|
|
25169
|
+
}
|
|
25170
|
+
}
|
|
25171
|
+
i2++;
|
|
25172
|
+
}
|
|
25173
|
+
return -1;
|
|
25174
|
+
}
|
|
25175
|
+
function findKwargValueEnd(s, start2) {
|
|
25176
|
+
let depth = 0;
|
|
25177
|
+
let inStr = null;
|
|
25178
|
+
for (let i2 = start2; i2 < s.length; i2++) {
|
|
25179
|
+
const ch = s[i2];
|
|
25180
|
+
if (inStr) {
|
|
25181
|
+
if (ch === "\\") {
|
|
25182
|
+
i2++;
|
|
25183
|
+
continue;
|
|
25184
|
+
}
|
|
25185
|
+
if (ch === inStr) inStr = null;
|
|
25186
|
+
continue;
|
|
25187
|
+
}
|
|
25188
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
25189
|
+
inStr = ch;
|
|
25190
|
+
continue;
|
|
25191
|
+
}
|
|
25192
|
+
if (ch === "(" || ch === "[" || ch === "{") depth++;
|
|
25193
|
+
else if (ch === ")" || ch === "]" || ch === "}") {
|
|
25194
|
+
if (depth === 0) return i2;
|
|
25195
|
+
depth--;
|
|
25196
|
+
} else if (ch === "," && depth === 0) {
|
|
25197
|
+
return i2;
|
|
25198
|
+
}
|
|
25199
|
+
}
|
|
25200
|
+
return s.length;
|
|
25201
|
+
}
|
|
25202
|
+
function lineOfCharIndex(code, charIdx) {
|
|
25203
|
+
let line = 1;
|
|
25204
|
+
for (let i2 = 0; i2 < charIdx && i2 < code.length; i2++) {
|
|
25205
|
+
if (code[i2] === "\n") line++;
|
|
25206
|
+
}
|
|
25207
|
+
return line;
|
|
25208
|
+
}
|
|
25209
|
+
function makeExfilFinding(file, line, snippet, fired, receiver) {
|
|
25210
|
+
return {
|
|
25211
|
+
id: `external-secret-exfiltration-${file}-${line}`,
|
|
25212
|
+
pass: "language-sources",
|
|
25213
|
+
category: "security",
|
|
25214
|
+
rule_id: "external-secret-exfiltration",
|
|
25215
|
+
cwe: "CWE-200",
|
|
25216
|
+
severity: "high",
|
|
25217
|
+
level: "error",
|
|
25218
|
+
message: `Environment secret(s) ${fired.join(", ")} transmitted in request body to external host via ${receiver}`,
|
|
25219
|
+
file,
|
|
25220
|
+
line,
|
|
25221
|
+
snippet: snippet.substring(0, 120)
|
|
25222
|
+
};
|
|
25223
|
+
}
|
|
25224
|
+
function findExternalSecretExfiltrationFindings(code, file, language) {
|
|
25225
|
+
const out2 = [];
|
|
25226
|
+
const lines = code.split("\n");
|
|
25227
|
+
const secretVars = collectEnvSecretVars(lines, language);
|
|
25228
|
+
if (secretVars.size === 0) return out2;
|
|
25229
|
+
if (language === "python") {
|
|
25230
|
+
out2.push(...findPythonExfilCalls(code, file, secretVars));
|
|
25231
|
+
} else if (language === "javascript" || language === "typescript") {
|
|
25232
|
+
out2.push(...findJavaScriptExfilCalls(code, file, secretVars));
|
|
25233
|
+
} else if (language === "go") {
|
|
25234
|
+
out2.push(...findGoExfilCalls(code, file, secretVars));
|
|
25235
|
+
}
|
|
25236
|
+
return out2;
|
|
25237
|
+
}
|
|
25238
|
+
function findPythonExfilCalls(code, file, secretVars) {
|
|
25239
|
+
const out2 = [];
|
|
25240
|
+
const callRe = /\b(requests|httpx)\.(post|put|patch|delete|request)\s*\(/g;
|
|
25241
|
+
let m;
|
|
25242
|
+
while (m = callRe.exec(code)) {
|
|
25243
|
+
const argStart = m.index + m[0].length;
|
|
25244
|
+
const argEnd = findBalancedCallEnd(code, argStart);
|
|
25245
|
+
if (argEnd < 0) continue;
|
|
25246
|
+
const args2 = code.slice(argStart, argEnd);
|
|
25247
|
+
const urlMatch = args2.match(/^\s*["']([^"']+)["']/);
|
|
25248
|
+
if (!urlMatch) continue;
|
|
25249
|
+
if (!isExternalHostUrl(urlMatch[1])) continue;
|
|
25250
|
+
const headersIdx = args2.search(/\bheaders\s*=/);
|
|
25251
|
+
let headersStr = "";
|
|
25252
|
+
let bodyStr = args2;
|
|
25253
|
+
if (headersIdx >= 0) {
|
|
25254
|
+
const eqIdx = args2.indexOf("=", headersIdx);
|
|
25255
|
+
const valueStart = eqIdx + 1;
|
|
25256
|
+
const headersEnd = findKwargValueEnd(args2, valueStart);
|
|
25257
|
+
headersStr = args2.slice(valueStart, headersEnd);
|
|
25258
|
+
bodyStr = args2.slice(0, headersIdx) + " " + args2.slice(headersEnd);
|
|
25259
|
+
}
|
|
25260
|
+
const fired = [];
|
|
25261
|
+
for (const v of secretVars.keys()) {
|
|
25262
|
+
const re = new RegExp(`\\b${v}\\b`);
|
|
25263
|
+
if (re.test(bodyStr) && !re.test(headersStr)) fired.push(v);
|
|
25264
|
+
}
|
|
25265
|
+
if (fired.length > 0) {
|
|
25266
|
+
const line = lineOfCharIndex(code, m.index);
|
|
25267
|
+
out2.push(
|
|
25268
|
+
makeExfilFinding(file, line, code.slice(m.index, Math.min(argEnd + 1, m.index + 120)), fired, `${m[1]}.${m[2]}`)
|
|
25269
|
+
);
|
|
25270
|
+
}
|
|
25271
|
+
}
|
|
25272
|
+
return out2;
|
|
25273
|
+
}
|
|
25274
|
+
function findJavaScriptExfilCalls(code, file, secretVars) {
|
|
25275
|
+
const out2 = [];
|
|
25276
|
+
const allLines = code.split("\n");
|
|
25277
|
+
const carriers = /* @__PURE__ */ new Set();
|
|
25278
|
+
const carrierRe = /(?:^|[\s;{])(?:const|let|var)\s+(\w+)\s*=\s*([^;\n]+)/g;
|
|
25279
|
+
let cm;
|
|
25280
|
+
while (cm = carrierRe.exec(code)) {
|
|
25281
|
+
const name2 = cm[1];
|
|
25282
|
+
const rhs = cm[2];
|
|
25283
|
+
for (const v of secretVars.keys()) {
|
|
25284
|
+
if (new RegExp(`\\b${v}\\b`).test(rhs)) {
|
|
25285
|
+
carriers.add(name2);
|
|
25286
|
+
break;
|
|
25287
|
+
}
|
|
25288
|
+
}
|
|
25289
|
+
}
|
|
25290
|
+
const taintedRefs = /* @__PURE__ */ new Set([...secretVars.keys(), ...carriers]);
|
|
25291
|
+
const networkRe = /\b(https?\.(?:request|get)|fetch|axios\.(?:post|put|patch|request|delete))\s*\(/g;
|
|
25292
|
+
let m;
|
|
25293
|
+
while (m = networkRe.exec(code)) {
|
|
25294
|
+
const argStart = m.index + m[0].length;
|
|
25295
|
+
const argEnd = findBalancedCallEnd(code, argStart);
|
|
25296
|
+
if (argEnd < 0) continue;
|
|
25297
|
+
const args2 = code.slice(argStart, argEnd);
|
|
25298
|
+
const urlMatch = args2.match(/^\s*(?:["']([^"']+)["']|`([^`$]+)`)/);
|
|
25299
|
+
if (!urlMatch) continue;
|
|
25300
|
+
const url = urlMatch[1] || urlMatch[2];
|
|
25301
|
+
if (!isExternalHostUrl(url)) continue;
|
|
25302
|
+
const headersIdx = args2.search(/\bheaders\s*:/);
|
|
25303
|
+
let headersStr = "";
|
|
25304
|
+
let restStr = args2;
|
|
25305
|
+
if (headersIdx >= 0) {
|
|
25306
|
+
const valueStart = args2.indexOf(":", headersIdx) + 1;
|
|
25307
|
+
const headersEnd = findKwargValueEnd(args2, valueStart);
|
|
25308
|
+
headersStr = args2.slice(valueStart, headersEnd);
|
|
25309
|
+
restStr = args2.slice(0, headersIdx) + " " + args2.slice(headersEnd);
|
|
25310
|
+
}
|
|
25311
|
+
const fired = /* @__PURE__ */ new Set();
|
|
25312
|
+
for (const v of taintedRefs) {
|
|
25313
|
+
const re = new RegExp(`\\b${v}\\b`);
|
|
25314
|
+
if (re.test(restStr) && !re.test(headersStr)) fired.add(v);
|
|
25315
|
+
}
|
|
25316
|
+
const callLine = lineOfCharIndex(code, m.index);
|
|
25317
|
+
const windowEnd = Math.min(allLines.length, callLine + 20);
|
|
25318
|
+
const writeWindow = allLines.slice(callLine, windowEnd).join("\n");
|
|
25319
|
+
const writeRe = /\b\w+\.(?:write|end)\s*\(\s*(\w+)/g;
|
|
25320
|
+
let wm;
|
|
25321
|
+
while (wm = writeRe.exec(writeWindow)) {
|
|
25322
|
+
if (taintedRefs.has(wm[1])) fired.add(wm[1]);
|
|
25323
|
+
}
|
|
25324
|
+
if (fired.size > 0) {
|
|
25325
|
+
out2.push(
|
|
25326
|
+
makeExfilFinding(
|
|
25327
|
+
file,
|
|
25328
|
+
callLine,
|
|
25329
|
+
code.slice(m.index, Math.min(argEnd + 1, m.index + 120)),
|
|
25330
|
+
[...fired],
|
|
25331
|
+
m[1]
|
|
25332
|
+
)
|
|
25333
|
+
);
|
|
25334
|
+
}
|
|
25335
|
+
}
|
|
25336
|
+
return out2;
|
|
25337
|
+
}
|
|
25338
|
+
function findGoExfilCalls(code, file, secretVars) {
|
|
25339
|
+
const out2 = [];
|
|
25340
|
+
const patterns = [
|
|
25341
|
+
{ re: /\bhttp\.PostForm\s*\(/g, urlArgIndex: 0, receiver: "http.PostForm" },
|
|
25342
|
+
{ re: /\bhttp\.Post\s*\(/g, urlArgIndex: 0, receiver: "http.Post" },
|
|
25343
|
+
{ re: /\bhttp\.NewRequest\s*\(/g, urlArgIndex: 1, receiver: "http.NewRequest" }
|
|
25344
|
+
];
|
|
25345
|
+
for (const { re, urlArgIndex, receiver } of patterns) {
|
|
25346
|
+
let m;
|
|
25347
|
+
while (m = re.exec(code)) {
|
|
25348
|
+
const argStart = m.index + m[0].length;
|
|
25349
|
+
const argEnd = findBalancedCallEnd(code, argStart);
|
|
25350
|
+
if (argEnd < 0) continue;
|
|
25351
|
+
const args2 = code.slice(argStart, argEnd);
|
|
25352
|
+
let urlSearchSlice = args2;
|
|
25353
|
+
if (urlArgIndex === 1) {
|
|
25354
|
+
const firstComma = findKwargValueEnd(args2, 0);
|
|
25355
|
+
if (firstComma >= args2.length) continue;
|
|
25356
|
+
urlSearchSlice = args2.slice(firstComma + 1);
|
|
25357
|
+
}
|
|
25358
|
+
const urlMatch = urlSearchSlice.match(/^\s*["`]([^"`]+)["`]/);
|
|
25359
|
+
if (!urlMatch) continue;
|
|
25360
|
+
if (!isExternalHostUrl(urlMatch[1])) continue;
|
|
25361
|
+
const fired = [];
|
|
25362
|
+
for (const v of secretVars.keys()) {
|
|
25363
|
+
if (new RegExp(`\\b${v}\\b`).test(args2)) fired.push(v);
|
|
25364
|
+
}
|
|
25365
|
+
if (fired.length > 0) {
|
|
25366
|
+
const line = lineOfCharIndex(code, m.index);
|
|
25367
|
+
out2.push(
|
|
25368
|
+
makeExfilFinding(file, line, code.slice(m.index, Math.min(argEnd + 1, m.index + 120)), fired, receiver)
|
|
25369
|
+
);
|
|
25370
|
+
}
|
|
25371
|
+
}
|
|
25372
|
+
}
|
|
25373
|
+
return out2;
|
|
25374
|
+
}
|
|
25375
|
+
function findPythonPatternFindings(code, file) {
|
|
25376
|
+
const out2 = [];
|
|
25377
|
+
const lines = code.split("\n");
|
|
25378
|
+
const subscriptHeaderRe = /\.headers\s*\[\s*['"]([^'"]+)['"]\s*\]\s*=\s*(.+)$/;
|
|
25379
|
+
const xfoHits = [];
|
|
25380
|
+
const cspHits = [];
|
|
25381
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25382
|
+
const raw = lines[i2];
|
|
25383
|
+
const trimmed = raw.trim();
|
|
25384
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
25385
|
+
const lineNumber = i2 + 1;
|
|
25386
|
+
const sm = trimmed.match(subscriptHeaderRe);
|
|
25387
|
+
if (sm) {
|
|
25388
|
+
const headerName = sm[1];
|
|
25389
|
+
const rhs = sm[2].trim();
|
|
25390
|
+
const headerLower = headerName.toLowerCase();
|
|
25391
|
+
if (headerLower === "access-control-allow-origin") {
|
|
25392
|
+
const valMatch = rhs.match(/^['"]\*['"]/);
|
|
25393
|
+
if (valMatch) {
|
|
25394
|
+
out2.push({
|
|
25395
|
+
id: `cors-wildcard-origin-${file}-${lineNumber}`,
|
|
25396
|
+
pass: "language-sources",
|
|
25397
|
+
category: "security",
|
|
25398
|
+
rule_id: "cors-wildcard-origin",
|
|
25399
|
+
cwe: "CWE-942",
|
|
25400
|
+
severity: "medium",
|
|
25401
|
+
level: "warning",
|
|
25402
|
+
message: "CORS Access-Control-Allow-Origin set to wildcard '*': any origin may read responses",
|
|
25403
|
+
file,
|
|
25404
|
+
line: lineNumber,
|
|
25405
|
+
snippet: trimmed.substring(0, 100)
|
|
25406
|
+
});
|
|
25407
|
+
}
|
|
25408
|
+
}
|
|
25409
|
+
if (headerLower === "x-frame-options") {
|
|
25410
|
+
xfoHits.push({ line: lineNumber, value: rhs });
|
|
25411
|
+
} else if (headerLower === "content-security-policy") {
|
|
25412
|
+
cspHits.push({ line: lineNumber, value: rhs });
|
|
25413
|
+
}
|
|
25414
|
+
}
|
|
25415
|
+
if (/\bverify_mode\s*=\s*ssl\.CERT_NONE\b/.test(trimmed)) {
|
|
25416
|
+
out2.push({
|
|
25417
|
+
id: `tls-verify-disabled-${file}-${lineNumber}`,
|
|
25418
|
+
pass: "language-sources",
|
|
25419
|
+
category: "security",
|
|
25420
|
+
rule_id: "tls-verify-disabled",
|
|
25421
|
+
cwe: "CWE-295",
|
|
25422
|
+
severity: "high",
|
|
25423
|
+
level: "error",
|
|
25424
|
+
message: "TLS certificate verification disabled: ssl context verify_mode set to CERT_NONE",
|
|
25425
|
+
file,
|
|
25426
|
+
line: lineNumber,
|
|
25427
|
+
snippet: trimmed.substring(0, 100)
|
|
25428
|
+
});
|
|
25429
|
+
} else if (/\bcheck_hostname\s*=\s*False\b/.test(trimmed)) {
|
|
25430
|
+
out2.push({
|
|
25431
|
+
id: `tls-verify-disabled-${file}-${lineNumber}`,
|
|
25432
|
+
pass: "language-sources",
|
|
25433
|
+
category: "security",
|
|
25434
|
+
rule_id: "tls-verify-disabled",
|
|
25435
|
+
cwe: "CWE-295",
|
|
25436
|
+
severity: "high",
|
|
25437
|
+
level: "error",
|
|
25438
|
+
message: "TLS hostname verification disabled: ssl context check_hostname set to False",
|
|
25439
|
+
file,
|
|
25440
|
+
line: lineNumber,
|
|
25441
|
+
snippet: trimmed.substring(0, 100)
|
|
25442
|
+
});
|
|
25443
|
+
}
|
|
25444
|
+
}
|
|
25445
|
+
const restrictiveXfo = xfoHits.find(
|
|
25446
|
+
(h) => /['"](?:DENY|SAMEORIGIN)['"]/i.test(h.value)
|
|
25447
|
+
);
|
|
25448
|
+
const permissiveCsp = cspHits.find((h) => {
|
|
25449
|
+
const pm = h.value.match(/['"]([^'"]+)['"]/);
|
|
25450
|
+
if (!pm) return false;
|
|
25451
|
+
const policy = pm[1];
|
|
25452
|
+
const faMatch = policy.match(/frame-ancestors\s+([^;]+)/i);
|
|
25453
|
+
if (!faMatch) return false;
|
|
25454
|
+
const directive = faMatch[1].trim();
|
|
25455
|
+
return /(^|\s)\*(\s|$)/.test(directive) || /\bhttps?:\/\//i.test(directive);
|
|
25456
|
+
});
|
|
25457
|
+
if (restrictiveXfo && permissiveCsp) {
|
|
25458
|
+
out2.push({
|
|
25459
|
+
id: `xfo-csp-mismatch-${file}-${restrictiveXfo.line}`,
|
|
25460
|
+
pass: "language-sources",
|
|
25461
|
+
category: "security",
|
|
25462
|
+
rule_id: "xfo-csp-mismatch",
|
|
25463
|
+
cwe: "CWE-1021",
|
|
25464
|
+
severity: "medium",
|
|
25465
|
+
level: "warning",
|
|
25466
|
+
message: "X-Frame-Options/CSP frame-ancestors mismatch: XFO restricts framing but CSP frame-ancestors is permissive (CSP overrides on modern browsers)",
|
|
25467
|
+
file,
|
|
25468
|
+
line: restrictiveXfo.line,
|
|
25469
|
+
snippet: lines[restrictiveXfo.line - 1].trim().substring(0, 100)
|
|
25470
|
+
});
|
|
25471
|
+
}
|
|
25472
|
+
return out2;
|
|
25473
|
+
}
|
|
25474
|
+
function findRustPatternFindings(code, file) {
|
|
25475
|
+
const out2 = [];
|
|
25476
|
+
const lines = code.split("\n");
|
|
25477
|
+
const re = /\.\s*(danger_accept_invalid_certs|danger_accept_invalid_hostnames)\s*\(\s*true\s*\)/;
|
|
25478
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
25479
|
+
const raw = lines[i2];
|
|
25480
|
+
const trimmed = raw.trim();
|
|
25481
|
+
if (!trimmed || trimmed.startsWith("//")) continue;
|
|
25482
|
+
const m = trimmed.match(re);
|
|
25483
|
+
if (!m) continue;
|
|
25484
|
+
const method = m[1];
|
|
25485
|
+
const what = method === "danger_accept_invalid_certs" ? "certificate" : "hostname";
|
|
25486
|
+
out2.push({
|
|
25487
|
+
id: `tls-verify-disabled-${file}-${i2 + 1}`,
|
|
25488
|
+
pass: "language-sources",
|
|
25489
|
+
category: "security",
|
|
25490
|
+
rule_id: "tls-verify-disabled",
|
|
25491
|
+
cwe: "CWE-295",
|
|
25492
|
+
severity: "high",
|
|
25493
|
+
level: "error",
|
|
25494
|
+
message: `TLS ${what} verification disabled: reqwest builder ${method}(true)`,
|
|
25495
|
+
file,
|
|
25496
|
+
line: i2 + 1,
|
|
25497
|
+
snippet: trimmed.substring(0, 100)
|
|
25498
|
+
});
|
|
25499
|
+
}
|
|
25500
|
+
return out2;
|
|
25501
|
+
}
|
|
24833
25502
|
|
|
24834
25503
|
// src/analysis/passes/sink-filter-pass.ts
|
|
24835
25504
|
var JS_XSS_SANITIZERS = [
|