circle-ir 3.79.0 → 3.81.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/configs/sinks/xss.yaml +2 -1
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +17 -5
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/passes/_credential-helpers.d.ts +40 -0
- package/dist/analysis/passes/_credential-helpers.d.ts.map +1 -0
- package/dist/analysis/passes/_credential-helpers.js +152 -0
- package/dist/analysis/passes/_credential-helpers.js.map +1 -0
- package/dist/analysis/passes/cleartext-credential-transport-pass.d.ts +42 -0
- package/dist/analysis/passes/cleartext-credential-transport-pass.d.ts.map +1 -0
- package/dist/analysis/passes/cleartext-credential-transport-pass.js +196 -0
- package/dist/analysis/passes/cleartext-credential-transport-pass.js.map +1 -0
- package/dist/analysis/passes/insecure-cookie-pass.d.ts.map +1 -1
- package/dist/analysis/passes/insecure-cookie-pass.js +10 -5
- package/dist/analysis/passes/insecure-cookie-pass.js.map +1 -1
- package/dist/analysis/passes/plaintext-password-storage-pass.d.ts +47 -0
- package/dist/analysis/passes/plaintext-password-storage-pass.d.ts.map +1 -0
- package/dist/analysis/passes/plaintext-password-storage-pass.js +159 -0
- package/dist/analysis/passes/plaintext-password-storage-pass.js.map +1 -0
- package/dist/analysis/passes/scan-secrets-pass.d.ts.map +1 -1
- package/dist/analysis/passes/scan-secrets-pass.js +88 -0
- package/dist/analysis/passes/scan-secrets-pass.js.map +1 -1
- package/dist/analysis/passes/weak-password-encoding-pass.d.ts +40 -0
- package/dist/analysis/passes/weak-password-encoding-pass.d.ts.map +1 -0
- package/dist/analysis/passes/weak-password-encoding-pass.js +157 -0
- package/dist/analysis/passes/weak-password-encoding-pass.js.map +1 -0
- package/dist/analysis/passes/weak-password-hash-pass.d.ts +49 -0
- package/dist/analysis/passes/weak-password-hash-pass.d.ts.map +1 -0
- package/dist/analysis/passes/weak-password-hash-pass.js +225 -0
- package/dist/analysis/passes/weak-password-hash-pass.js.map +1 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +12 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +621 -7
- package/dist/core/circle-ir-core.cjs +17 -5
- package/dist/core/circle-ir-core.js +17 -5
- package/package.json +1 -1
|
@@ -11377,7 +11377,12 @@ var DEFAULT_SINKS = [
|
|
|
11377
11377
|
// Class-less XSS patterns for cases where receiver type is inferred
|
|
11378
11378
|
{ method: "println", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
11379
11379
|
{ method: "print", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
11380
|
-
{ method:
|
|
11380
|
+
// NOTE: the unscoped { method: 'write', type: 'xss' } entry was removed in
|
|
11381
|
+
// Sprint 28 (#110). It mistyped every non-XSS .write() across all languages
|
|
11382
|
+
// (fs.writeFile, open().write, bcrypt callbacks, credential file writes,
|
|
11383
|
+
// node ClientRequest.write, etc.) as xss. Real HTML writers are covered
|
|
11384
|
+
// by class-scoped entries: PrintWriter.write (line 843), ServletOutputStream.write
|
|
11385
|
+
// (line 849), JspWriter.write (xss.yaml), Response.write (nodejs.json).
|
|
11381
11386
|
{ method: "append", class: "StringBuilder", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
11382
11387
|
{ method: "append", class: "StringBuffer", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
11383
11388
|
// Wiki/CMS XSS sinks (JSPWiki, Confluence, etc.)
|
|
@@ -11736,10 +11741,17 @@ var DEFAULT_SINKS = [
|
|
|
11736
11741
|
// These patterns are detected by call-site literal inspection, not taint flow,
|
|
11737
11742
|
// so they are NOT registered here as sinks (they could never match a "tainted
|
|
11738
11743
|
// value flowing into a sink" because the bad value is a hard-coded constant).
|
|
11739
|
-
// Trust Boundary (CWE-501)
|
|
11740
|
-
//
|
|
11741
|
-
|
|
11742
|
-
|
|
11744
|
+
// Trust Boundary (CWE-501) — tainted VALUE crossing into shared session
|
|
11745
|
+
// state. OWASP/CWE-501 treats `session.setAttribute("k", taintedValue)` as
|
|
11746
|
+
// the violation: untrusted data enters server-side state where downstream
|
|
11747
|
+
// code reads it as if trusted. Both arg positions are flagged so either a
|
|
11748
|
+
// tainted key (rare) or tainted value (the OWASP shape, 83 cases) trips
|
|
11749
|
+
// the sink. (cognium-dev #117)
|
|
11750
|
+
{ method: "setAttribute", class: "HttpSession", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [0, 1] },
|
|
11751
|
+
{ method: "putValue", class: "HttpSession", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [0, 1] },
|
|
11752
|
+
// ServletContext + request scopes — same trust-boundary semantics.
|
|
11753
|
+
{ method: "setAttribute", class: "ServletContext", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [0, 1] },
|
|
11754
|
+
{ method: "setAttribute", class: "HttpServletRequest", type: "trust_boundary", cwe: "CWE-501", severity: "low", arg_positions: [0, 1] },
|
|
11743
11755
|
// Additional XSS patterns (JDOM/XML output)
|
|
11744
11756
|
{ method: "outputElementContent", class: "XMLOutputter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11745
11757
|
{ method: "output", class: "XMLOutputter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
@@ -28683,6 +28695,23 @@ var PROVIDER_PATTERNS = [
|
|
|
28683
28695
|
fix: "Revoke the npm token at https://www.npmjs.com/settings/<user>/tokens and load from environment."
|
|
28684
28696
|
}
|
|
28685
28697
|
];
|
|
28698
|
+
var CRED_KEYWORD_RE = /\b([A-Za-z_$][\w$]*?(?:password|passwd|secret|api[_-]?key|auth[_-]?token|private[_-]?key|access[_-]?key)[\w$]*?)\s*[:=]\s*["'`]([^"'`\s$][^"'`\n]{2,})["'`]/i;
|
|
28699
|
+
var CRED_DYNAMIC_VALUE_RE = /\$\{|process\.env|os\.environ|os\.Getenv|System\.getenv/;
|
|
28700
|
+
var CRED_FUNCTION_DECL_RE = /\b(?:function|func|def|fn)\s+\w+\s*\(/;
|
|
28701
|
+
var CRED_COMPARISON_RE = /(?:===?|!==?|>=|<=|<>)\s*["'`]/;
|
|
28702
|
+
function isLikelyCredentialAssignment(line) {
|
|
28703
|
+
if (CRED_FUNCTION_DECL_RE.test(line)) return null;
|
|
28704
|
+
if (CRED_COMPARISON_RE.test(line)) return null;
|
|
28705
|
+
const m = line.match(CRED_KEYWORD_RE);
|
|
28706
|
+
if (!m) return null;
|
|
28707
|
+
const name2 = m[1];
|
|
28708
|
+
const value = m[2];
|
|
28709
|
+
if (PLACEHOLDER_RE.test(value)) return null;
|
|
28710
|
+
if (CRED_DYNAMIC_VALUE_RE.test(value)) return null;
|
|
28711
|
+
if (value.length < 3) return null;
|
|
28712
|
+
if (isAllSameChar(value)) return null;
|
|
28713
|
+
return { name: name2, value };
|
|
28714
|
+
}
|
|
28686
28715
|
var STRING_LITERAL_RE = /(["'`])((?:\\.|(?!\1).){8,200})\1/g;
|
|
28687
28716
|
var BASE64ISH_RE = /^[A-Za-z0-9+/=_-]+$/;
|
|
28688
28717
|
var HEXISH_RE = /^[a-fA-F0-9]+$/;
|
|
@@ -28774,6 +28803,31 @@ var ScanSecretsPass = class {
|
|
|
28774
28803
|
break;
|
|
28775
28804
|
}
|
|
28776
28805
|
}
|
|
28806
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28807
|
+
const lineText = lines[i2];
|
|
28808
|
+
const lineNum = i2 + 1;
|
|
28809
|
+
const hit = isLikelyCredentialAssignment(lineText);
|
|
28810
|
+
if (!hit) continue;
|
|
28811
|
+
const key = `${lineNum}:hardcoded-credential`;
|
|
28812
|
+
if (seen.has(key)) continue;
|
|
28813
|
+
seen.add(key);
|
|
28814
|
+
ctx.addFinding({
|
|
28815
|
+
id: `hardcoded-credential-${file}-${lineNum}`,
|
|
28816
|
+
pass: this.name,
|
|
28817
|
+
category: this.category,
|
|
28818
|
+
rule_id: "hardcoded-credential",
|
|
28819
|
+
cwe: "CWE-798",
|
|
28820
|
+
severity: "high",
|
|
28821
|
+
level: "error",
|
|
28822
|
+
message: `Hardcoded credential: \`${hit.name}\` assigned a literal value`,
|
|
28823
|
+
file,
|
|
28824
|
+
line: lineNum,
|
|
28825
|
+
snippet: lineText.trim().substring(0, 120),
|
|
28826
|
+
fix: "Move the credential to an environment variable or secrets manager; never commit live secrets to source control.",
|
|
28827
|
+
evidence: { kind: "named-credential", name: hit.name }
|
|
28828
|
+
});
|
|
28829
|
+
providerFindings += 1;
|
|
28830
|
+
}
|
|
28777
28831
|
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28778
28832
|
const lineText = lines[i2];
|
|
28779
28833
|
const lineNum = i2 + 1;
|
|
@@ -29141,8 +29195,10 @@ var InsecureCookiePass = class {
|
|
|
29141
29195
|
}
|
|
29142
29196
|
// ---------------- Java ----------------
|
|
29143
29197
|
detectJavaCookieCtor(call, hasSetSecureTrue, hasSetHttpOnlyTrue) {
|
|
29144
|
-
|
|
29145
|
-
const
|
|
29198
|
+
const method = call.method_name ?? "";
|
|
29199
|
+
const isCookieCtor = method === "Cookie" || method.endsWith(".Cookie");
|
|
29200
|
+
if (!isCookieCtor) return null;
|
|
29201
|
+
const looksLikeCtor = call.is_constructor || !call.receiver && (call.receiver_type === "Cookie" || (call.receiver_type ?? "").endsWith(".Cookie")) || (call.resolution?.target ?? "").endsWith(".<init>");
|
|
29146
29202
|
if (!looksLikeCtor) return null;
|
|
29147
29203
|
if (call.arguments.length < 2) return null;
|
|
29148
29204
|
const missingSecure = !hasSetSecureTrue;
|
|
@@ -29930,6 +29986,560 @@ var WeakRandomPass = class {
|
|
|
29930
29986
|
}
|
|
29931
29987
|
};
|
|
29932
29988
|
|
|
29989
|
+
// src/analysis/passes/_credential-helpers.ts
|
|
29990
|
+
var CRED_KEYWORD_RE2 = /(?:password|passwd|pwd|secret|api[_-]?key|auth[_-]?token|private[_-]?key|access[_-]?key|credential)/i;
|
|
29991
|
+
function isCredentialIdentifier(name2) {
|
|
29992
|
+
if (!name2) return false;
|
|
29993
|
+
if (name2.length < 3) return false;
|
|
29994
|
+
return CRED_KEYWORD_RE2.test(name2);
|
|
29995
|
+
}
|
|
29996
|
+
function argLooksLikeCredential(arg) {
|
|
29997
|
+
if (!arg) return false;
|
|
29998
|
+
if (arg.variable && isCredentialIdentifier(arg.variable)) return true;
|
|
29999
|
+
const expr = (arg.expression ?? "").trim();
|
|
30000
|
+
if (!expr) return false;
|
|
30001
|
+
const head = expr.split(/[.\s(]/, 1)[0] ?? "";
|
|
30002
|
+
return isCredentialIdentifier(head);
|
|
30003
|
+
}
|
|
30004
|
+
function stripQuotes6(s) {
|
|
30005
|
+
const t = s.trim();
|
|
30006
|
+
if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'") || t.startsWith("`") && t.endsWith("`")) {
|
|
30007
|
+
return t.slice(1, -1);
|
|
30008
|
+
}
|
|
30009
|
+
return t;
|
|
30010
|
+
}
|
|
30011
|
+
function literalAt(call, position) {
|
|
30012
|
+
const arg = call.arguments.find((a) => a.position === position);
|
|
30013
|
+
if (!arg) return null;
|
|
30014
|
+
const raw = arg.literal ?? arg.expression ?? "";
|
|
30015
|
+
const trimmed = raw.trim();
|
|
30016
|
+
if (trimmed.startsWith('"') || trimmed.startsWith("'") || trimmed.startsWith("`")) {
|
|
30017
|
+
return stripQuotes6(trimmed);
|
|
30018
|
+
}
|
|
30019
|
+
if (arg.literal) return stripQuotes6(arg.literal);
|
|
30020
|
+
return null;
|
|
30021
|
+
}
|
|
30022
|
+
function isHashFunctionCall(call) {
|
|
30023
|
+
const method = call.method_name ?? "";
|
|
30024
|
+
const receiver = call.receiver ?? "";
|
|
30025
|
+
const recvLower = receiver.toLowerCase();
|
|
30026
|
+
if (recvLower === "bcrypt" || recvLower.endsWith(".bcrypt")) {
|
|
30027
|
+
return method === "hashpw" || method === "hash" || method === "hashSync" || method === "GenerateFromPassword" || method === "generate_password_hash";
|
|
30028
|
+
}
|
|
30029
|
+
if (recvLower === "argon2" || recvLower.endsWith(".argon2")) {
|
|
30030
|
+
return method === "hash" || method === "Hash" || method === "PasswordHash";
|
|
30031
|
+
}
|
|
30032
|
+
if (recvLower === "hashlib") return true;
|
|
30033
|
+
if (recvLower === "passlib" || recvLower.includes("passlib.hash")) return true;
|
|
30034
|
+
if (method === "PBKDF2HMAC" || method === "derive") return true;
|
|
30035
|
+
if (recvLower === "crypto") {
|
|
30036
|
+
return method === "createHash" || method === "createHmac" || method === "pbkdf2" || method === "pbkdf2Sync" || method === "scrypt" || method === "scryptSync";
|
|
30037
|
+
}
|
|
30038
|
+
if (receiver === "MessageDigest" || receiver.endsWith(".MessageDigest")) {
|
|
30039
|
+
return method === "getInstance" || method === "update" || method === "digest";
|
|
30040
|
+
}
|
|
30041
|
+
if (receiver === "DigestUtils" || receiver.endsWith(".DigestUtils")) {
|
|
30042
|
+
return true;
|
|
30043
|
+
}
|
|
30044
|
+
if (receiver === "SecretKeyFactory" || receiver.endsWith(".SecretKeyFactory")) {
|
|
30045
|
+
return method === "getInstance" || method === "generateSecret";
|
|
30046
|
+
}
|
|
30047
|
+
if (method === "PBEKeySpec") return true;
|
|
30048
|
+
if (receiver === "md5" || receiver === "sha1" || receiver === "sha256" || receiver === "sha512" || receiver === "sha3" || receiver.endsWith("/md5") || receiver.endsWith("/sha1") || receiver.endsWith("/sha256") || receiver.endsWith("/sha512")) {
|
|
30049
|
+
return method === "New" || method === "Sum" || method === "New224" || method === "New384";
|
|
30050
|
+
}
|
|
30051
|
+
const m = method.toLowerCase();
|
|
30052
|
+
if (m === "hash" || m === "hashpw" || m === "hashsync" || m === "pbkdf2" || m === "pbkdf2sync" || m === "scrypt" || m === "scryptsync") return true;
|
|
30053
|
+
return false;
|
|
30054
|
+
}
|
|
30055
|
+
function priorHashOf(varName, priorCalls) {
|
|
30056
|
+
for (const c of priorCalls) {
|
|
30057
|
+
if (!isHashFunctionCall(c)) continue;
|
|
30058
|
+
for (const a of c.arguments) {
|
|
30059
|
+
if (a.variable === varName) return true;
|
|
30060
|
+
const head = (a.expression ?? "").trim().split(/[.\s(]/, 1)[0];
|
|
30061
|
+
if (head === varName) return true;
|
|
30062
|
+
}
|
|
30063
|
+
}
|
|
30064
|
+
return false;
|
|
30065
|
+
}
|
|
30066
|
+
|
|
30067
|
+
// src/analysis/passes/weak-password-hash-pass.ts
|
|
30068
|
+
var FAST_HASH_NAMES = /* @__PURE__ */ new Set([
|
|
30069
|
+
"sha224",
|
|
30070
|
+
"sha-224",
|
|
30071
|
+
"sha256",
|
|
30072
|
+
"sha-256",
|
|
30073
|
+
"sha384",
|
|
30074
|
+
"sha-384",
|
|
30075
|
+
"sha512",
|
|
30076
|
+
"sha-512",
|
|
30077
|
+
"sha3",
|
|
30078
|
+
"sha-3",
|
|
30079
|
+
"sha3-256",
|
|
30080
|
+
"sha3-512"
|
|
30081
|
+
// MD/SHA1 are covered by weak-hash; not duplicating here.
|
|
30082
|
+
]);
|
|
30083
|
+
var BCRYPT_MIN_COST = 10;
|
|
30084
|
+
var PBKDF2_MIN_ITERATIONS = 1e5;
|
|
30085
|
+
function intLiteral(s) {
|
|
30086
|
+
if (s == null) return null;
|
|
30087
|
+
const t = s.trim();
|
|
30088
|
+
if (!/^-?\d+$/.test(t)) return null;
|
|
30089
|
+
const n = parseInt(t, 10);
|
|
30090
|
+
return Number.isFinite(n) ? n : null;
|
|
30091
|
+
}
|
|
30092
|
+
function pyKwargInt(call, name2) {
|
|
30093
|
+
for (const a of call.arguments) {
|
|
30094
|
+
const expr = (a.expression ?? "").trim();
|
|
30095
|
+
const m = expr.match(new RegExp(`^${name2}\\s*=\\s*(-?\\d+)$`));
|
|
30096
|
+
if (m) return parseInt(m[1], 10);
|
|
30097
|
+
}
|
|
30098
|
+
return null;
|
|
30099
|
+
}
|
|
30100
|
+
var WeakPasswordHashPass = class {
|
|
30101
|
+
name = "weak-password-hash";
|
|
30102
|
+
category = "security";
|
|
30103
|
+
run(ctx) {
|
|
30104
|
+
const { graph, language } = ctx;
|
|
30105
|
+
const file = graph.ir.meta.file;
|
|
30106
|
+
const findings = [];
|
|
30107
|
+
for (const call of graph.ir.calls) {
|
|
30108
|
+
const detection = this.detect(call, language);
|
|
30109
|
+
if (!detection) continue;
|
|
30110
|
+
const { kind, api } = detection;
|
|
30111
|
+
const line = call.location.line;
|
|
30112
|
+
findings.push({ line, language, kind, api });
|
|
30113
|
+
const message = kind === "fast-unsalted-hash" ? `Fast/unsalted hash \`${api}\` applied to a password. General-purpose hashes (SHA-256/512) are unsuitable for password storage.` : kind === "low-bcrypt-cost" ? `bcrypt called with insufficient cost factor (< ${BCRYPT_MIN_COST}).` : `PBKDF2 called with insufficient iteration count (< ${PBKDF2_MIN_ITERATIONS}).`;
|
|
30114
|
+
ctx.addFinding({
|
|
30115
|
+
id: `${this.name}-${file}-${line}`,
|
|
30116
|
+
pass: this.name,
|
|
30117
|
+
category: this.category,
|
|
30118
|
+
rule_id: this.name,
|
|
30119
|
+
cwe: "CWE-916",
|
|
30120
|
+
severity: "high",
|
|
30121
|
+
level: "warning",
|
|
30122
|
+
message,
|
|
30123
|
+
file,
|
|
30124
|
+
line,
|
|
30125
|
+
fix: "Use a memory-hard password-hashing function with appropriate cost: Argon2id (recommended), bcrypt (cost \u2265 12), scrypt, or PBKDF2 with \u2265 600k iterations.",
|
|
30126
|
+
evidence: { kind, api, language }
|
|
30127
|
+
});
|
|
30128
|
+
}
|
|
30129
|
+
return { findings };
|
|
30130
|
+
}
|
|
30131
|
+
detect(call, language) {
|
|
30132
|
+
const method = call.method_name ?? "";
|
|
30133
|
+
const receiver = call.receiver ?? "";
|
|
30134
|
+
const recvLower = receiver.toLowerCase();
|
|
30135
|
+
if (recvLower === "bcrypt" || recvLower.endsWith(".bcrypt")) {
|
|
30136
|
+
if (method === "gensalt") {
|
|
30137
|
+
const rounds = pyKwargInt(call, "rounds");
|
|
30138
|
+
if (rounds !== null && rounds < BCRYPT_MIN_COST) {
|
|
30139
|
+
return { kind: "low-bcrypt-cost", api: "bcrypt.gensalt" };
|
|
30140
|
+
}
|
|
30141
|
+
}
|
|
30142
|
+
if (method === "hash" || method === "hashSync") {
|
|
30143
|
+
const cost = intLiteral(literalAt(call, 1));
|
|
30144
|
+
if (cost !== null && cost < BCRYPT_MIN_COST) {
|
|
30145
|
+
return { kind: "low-bcrypt-cost", api: `bcrypt.${method}` };
|
|
30146
|
+
}
|
|
30147
|
+
}
|
|
30148
|
+
if (method === "GenerateFromPassword") {
|
|
30149
|
+
const arg1 = call.arguments.find((a) => a.position === 1);
|
|
30150
|
+
const expr = (arg1?.expression ?? "").trim();
|
|
30151
|
+
const n = intLiteral(expr);
|
|
30152
|
+
if (n !== null && n < BCRYPT_MIN_COST) {
|
|
30153
|
+
return { kind: "low-bcrypt-cost", api: "bcrypt.GenerateFromPassword" };
|
|
30154
|
+
}
|
|
30155
|
+
if (expr === "bcrypt.MinCost") {
|
|
30156
|
+
return { kind: "low-bcrypt-cost", api: "bcrypt.GenerateFromPassword" };
|
|
30157
|
+
}
|
|
30158
|
+
}
|
|
30159
|
+
}
|
|
30160
|
+
if (method === "PBKDF2HMAC") {
|
|
30161
|
+
const iters = pyKwargInt(call, "iterations");
|
|
30162
|
+
if (iters !== null && iters < PBKDF2_MIN_ITERATIONS) {
|
|
30163
|
+
return { kind: "low-pbkdf2-iterations", api: "PBKDF2HMAC" };
|
|
30164
|
+
}
|
|
30165
|
+
}
|
|
30166
|
+
if ((method === "pbkdf2" || method === "pbkdf2Sync") && (recvLower === "crypto" || recvLower.endsWith(".crypto"))) {
|
|
30167
|
+
const iters = intLiteral(literalAt(call, 2));
|
|
30168
|
+
if (iters !== null && iters < PBKDF2_MIN_ITERATIONS) {
|
|
30169
|
+
return { kind: "low-pbkdf2-iterations", api: `crypto.${method}` };
|
|
30170
|
+
}
|
|
30171
|
+
}
|
|
30172
|
+
if (method === "PBEKeySpec" && language === "java") {
|
|
30173
|
+
const iters = intLiteral(literalAt(call, 2));
|
|
30174
|
+
if (iters !== null && iters < PBKDF2_MIN_ITERATIONS) {
|
|
30175
|
+
return { kind: "low-pbkdf2-iterations", api: "PBEKeySpec" };
|
|
30176
|
+
}
|
|
30177
|
+
}
|
|
30178
|
+
if (language === "python" && (recvLower === "hashlib" || recvLower.endsWith(".hashlib"))) {
|
|
30179
|
+
if (FAST_HASH_NAMES.has(method.toLowerCase())) {
|
|
30180
|
+
if (argLooksLikeCredential(call.arguments.find((a) => a.position === 0))) {
|
|
30181
|
+
return { kind: "fast-unsalted-hash", api: `hashlib.${method}` };
|
|
30182
|
+
}
|
|
30183
|
+
}
|
|
30184
|
+
if (method === "new") {
|
|
30185
|
+
const algo = literalAt(call, 0)?.toLowerCase() ?? "";
|
|
30186
|
+
if (FAST_HASH_NAMES.has(algo) && argLooksLikeCredential(call.arguments.find((a) => a.position === 1))) {
|
|
30187
|
+
return { kind: "fast-unsalted-hash", api: `hashlib.new(${algo})` };
|
|
30188
|
+
}
|
|
30189
|
+
}
|
|
30190
|
+
}
|
|
30191
|
+
if ((language === "javascript" || language === "typescript") && method === "update") {
|
|
30192
|
+
const recvExpr = (receiver ?? "").toLowerCase();
|
|
30193
|
+
const hashLike = recvExpr.includes("hash") || recvExpr.includes("createhash") || recvExpr.includes("sha") || recvExpr.includes("md");
|
|
30194
|
+
if (hashLike && argLooksLikeCredential(call.arguments.find((a) => a.position === 0))) {
|
|
30195
|
+
return { kind: "fast-unsalted-hash", api: "crypto.createHash().update" };
|
|
30196
|
+
}
|
|
30197
|
+
}
|
|
30198
|
+
if (language === "java" && method === "update") {
|
|
30199
|
+
const recvName = (receiver ?? "").toLowerCase();
|
|
30200
|
+
const looksLikeDigest = recvName.includes("digest") || recvName.includes("md") || recvName.includes("hash");
|
|
30201
|
+
if (looksLikeDigest && argLooksLikeCredential(call.arguments.find((a) => a.position === 0))) {
|
|
30202
|
+
return { kind: "fast-unsalted-hash", api: "MessageDigest.update" };
|
|
30203
|
+
}
|
|
30204
|
+
}
|
|
30205
|
+
if (language === "go") {
|
|
30206
|
+
const isFastPkg = receiver === "sha256" || receiver === "sha512" || receiver === "sha3" || receiver === "sha224";
|
|
30207
|
+
if (isFastPkg && (method === "Sum256" || method === "Sum512" || method === "Sum224" || method === "Sum384" || method === "Sum")) {
|
|
30208
|
+
const expr = (call.arguments.find((a) => a.position === 0)?.expression ?? "").trim();
|
|
30209
|
+
const inner = expr.replace(/^\[\]byte\s*\(\s*/, "").replace(/\s*\)\s*$/, "");
|
|
30210
|
+
if (argLooksLikeCredential({ position: 0, expression: inner, variable: inner })) {
|
|
30211
|
+
return { kind: "fast-unsalted-hash", api: `${receiver}.${method}` };
|
|
30212
|
+
}
|
|
30213
|
+
}
|
|
30214
|
+
}
|
|
30215
|
+
return null;
|
|
30216
|
+
}
|
|
30217
|
+
};
|
|
30218
|
+
|
|
30219
|
+
// src/analysis/passes/weak-password-encoding-pass.ts
|
|
30220
|
+
function isBasicAuthContext(call, code) {
|
|
30221
|
+
const line = call.location.line;
|
|
30222
|
+
if (line < 1) return false;
|
|
30223
|
+
const lines = code.split("\n");
|
|
30224
|
+
const start2 = Math.max(0, line - 2);
|
|
30225
|
+
const end = Math.min(lines.length, line + 1);
|
|
30226
|
+
const window2 = lines.slice(start2, end).join("\n");
|
|
30227
|
+
return /["'`]Basic\s/i.test(window2);
|
|
30228
|
+
}
|
|
30229
|
+
var WeakPasswordEncodingPass = class {
|
|
30230
|
+
name = "weak-password-encoding";
|
|
30231
|
+
category = "security";
|
|
30232
|
+
run(ctx) {
|
|
30233
|
+
const { graph, language, code } = ctx;
|
|
30234
|
+
const file = graph.ir.meta.file;
|
|
30235
|
+
const findings = [];
|
|
30236
|
+
for (const call of graph.ir.calls) {
|
|
30237
|
+
const api = this.detect(call, language);
|
|
30238
|
+
if (!api) continue;
|
|
30239
|
+
if (isBasicAuthContext(call, code)) continue;
|
|
30240
|
+
const line = call.location.line;
|
|
30241
|
+
findings.push({ line, language, api });
|
|
30242
|
+
ctx.addFinding({
|
|
30243
|
+
id: `${this.name}-${file}-${line}`,
|
|
30244
|
+
pass: this.name,
|
|
30245
|
+
category: this.category,
|
|
30246
|
+
rule_id: this.name,
|
|
30247
|
+
cwe: "CWE-261",
|
|
30248
|
+
severity: "medium",
|
|
30249
|
+
level: "warning",
|
|
30250
|
+
message: `Credential encoded via \`${api}\` \u2014 encoding is NOT encryption. Base64/hex provide no confidentiality; anyone with the encoded value can decode it.`,
|
|
30251
|
+
file,
|
|
30252
|
+
line,
|
|
30253
|
+
fix: "For storage, use a password hash (Argon2id / bcrypt). For transport, use TLS. For symmetric secrecy, use authenticated encryption (AES-GCM).",
|
|
30254
|
+
evidence: { api, language }
|
|
30255
|
+
});
|
|
30256
|
+
}
|
|
30257
|
+
return { findings };
|
|
30258
|
+
}
|
|
30259
|
+
detect(call, language) {
|
|
30260
|
+
const method = call.method_name ?? "";
|
|
30261
|
+
const receiver = call.receiver ?? "";
|
|
30262
|
+
const recvLower = receiver.toLowerCase();
|
|
30263
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
30264
|
+
if (language === "python") {
|
|
30265
|
+
if (recvLower === "base64" && (method === "b64encode" || method === "urlsafe_b64encode" || method === "standard_b64encode")) {
|
|
30266
|
+
if (argLooksLikeCredential(arg0)) return `base64.${method}`;
|
|
30267
|
+
}
|
|
30268
|
+
if (recvLower === "binascii" && method === "hexlify") {
|
|
30269
|
+
if (argLooksLikeCredential(arg0)) return "binascii.hexlify";
|
|
30270
|
+
}
|
|
30271
|
+
}
|
|
30272
|
+
if (language === "javascript" || language === "typescript") {
|
|
30273
|
+
if (method === "toString") {
|
|
30274
|
+
const encoding = literalAt(call, 0);
|
|
30275
|
+
if (encoding === "base64" || encoding === "hex" || encoding === "base64url") {
|
|
30276
|
+
const recv = (receiver ?? "").toLowerCase();
|
|
30277
|
+
if (recv.includes("buffer.from") && /(?:password|passwd|pwd|secret|api[_-]?key|auth[_-]?token|private[_-]?key|access[_-]?key|credential)/i.test(receiver ?? "")) {
|
|
30278
|
+
return `Buffer.from().toString('${encoding}')`;
|
|
30279
|
+
}
|
|
30280
|
+
}
|
|
30281
|
+
}
|
|
30282
|
+
if (method === "btoa" && receiver === "") {
|
|
30283
|
+
if (argLooksLikeCredential(arg0)) return "btoa";
|
|
30284
|
+
}
|
|
30285
|
+
}
|
|
30286
|
+
if (language === "java") {
|
|
30287
|
+
if (method === "encodeToString") {
|
|
30288
|
+
const recv = (receiver ?? "").toLowerCase();
|
|
30289
|
+
if (recv.includes("encoder") || recv.includes("base64")) {
|
|
30290
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
30291
|
+
const head = expr.split(/[.\s(]/, 1)[0] ?? "";
|
|
30292
|
+
if (argLooksLikeCredential({ position: 0, expression: head, variable: head })) {
|
|
30293
|
+
return "Base64.encodeToString";
|
|
30294
|
+
}
|
|
30295
|
+
}
|
|
30296
|
+
}
|
|
30297
|
+
if (method === "encodeHexString" && (receiver === "Hex" || receiver.endsWith(".Hex"))) {
|
|
30298
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
30299
|
+
const head = expr.split(/[.\s(]/, 1)[0] ?? "";
|
|
30300
|
+
if (argLooksLikeCredential({ position: 0, expression: head, variable: head })) {
|
|
30301
|
+
return "Hex.encodeHexString";
|
|
30302
|
+
}
|
|
30303
|
+
}
|
|
30304
|
+
}
|
|
30305
|
+
if (language === "go") {
|
|
30306
|
+
if (method === "EncodeToString") {
|
|
30307
|
+
const recv = (receiver ?? "").toLowerCase();
|
|
30308
|
+
if (recv.includes("base64") || recv.includes("hex") || recv.includes("encoding")) {
|
|
30309
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
30310
|
+
const inner = expr.replace(/^\[\]byte\s*\(\s*/, "").replace(/\s*\)\s*$/, "");
|
|
30311
|
+
const head = inner.split(/[.\s(]/, 1)[0] ?? "";
|
|
30312
|
+
if (argLooksLikeCredential({ position: 0, expression: head, variable: head })) {
|
|
30313
|
+
return recv.includes("hex") ? "hex.EncodeToString" : "base64.EncodeToString";
|
|
30314
|
+
}
|
|
30315
|
+
}
|
|
30316
|
+
}
|
|
30317
|
+
}
|
|
30318
|
+
return null;
|
|
30319
|
+
}
|
|
30320
|
+
};
|
|
30321
|
+
|
|
30322
|
+
// src/analysis/passes/plaintext-password-storage-pass.ts
|
|
30323
|
+
function isWriteStorageCall(call, language) {
|
|
30324
|
+
const method = call.method_name ?? "";
|
|
30325
|
+
const receiver = call.receiver ?? "";
|
|
30326
|
+
const recvLower = receiver.toLowerCase();
|
|
30327
|
+
if (language === "python") {
|
|
30328
|
+
if (method === "write" || method === "writelines") {
|
|
30329
|
+
return { credPos: 0, api: `<file>.${method}` };
|
|
30330
|
+
}
|
|
30331
|
+
if ((recvLower === "pickle" || recvLower === "json" || recvLower === "yaml") && (method === "dump" || method === "dumps")) {
|
|
30332
|
+
return { credPos: 0, api: `${receiver}.${method}` };
|
|
30333
|
+
}
|
|
30334
|
+
if (recvLower === "redis" && (method === "set" || method === "setex" || method === "hset")) {
|
|
30335
|
+
return { credPos: 1, api: `redis.${method}` };
|
|
30336
|
+
}
|
|
30337
|
+
}
|
|
30338
|
+
if (language === "javascript" || language === "typescript") {
|
|
30339
|
+
if ((recvLower === "fs" || recvLower.endsWith(".fs")) && (method === "writeFile" || method === "writeFileSync" || method === "appendFile" || method === "appendFileSync")) {
|
|
30340
|
+
return { credPos: 1, api: `fs.${method}` };
|
|
30341
|
+
}
|
|
30342
|
+
if ((recvLower === "localstorage" || recvLower === "sessionstorage") && method === "setItem") {
|
|
30343
|
+
return { credPos: 1, api: `${receiver}.setItem` };
|
|
30344
|
+
}
|
|
30345
|
+
if (recvLower === "redis" && (method === "set" || method === "setex" || method === "hset")) {
|
|
30346
|
+
return { credPos: 1, api: `redis.${method}` };
|
|
30347
|
+
}
|
|
30348
|
+
}
|
|
30349
|
+
if (language === "java") {
|
|
30350
|
+
if ((receiver === "Files" || receiver.endsWith(".Files")) && (method === "write" || method === "writeString")) {
|
|
30351
|
+
return { credPos: 1, api: `Files.${method}` };
|
|
30352
|
+
}
|
|
30353
|
+
if (method === "write") {
|
|
30354
|
+
const lc = (receiver ?? "").toLowerCase();
|
|
30355
|
+
if (lc.includes("writer") || lc.includes("file") || lc.includes("stream")) {
|
|
30356
|
+
return { credPos: 0, api: `${receiver}.write` };
|
|
30357
|
+
}
|
|
30358
|
+
}
|
|
30359
|
+
}
|
|
30360
|
+
if (language === "go") {
|
|
30361
|
+
if (receiver === "os" || receiver.endsWith("/os")) {
|
|
30362
|
+
if (method === "WriteFile") return { credPos: 1, api: "os.WriteFile" };
|
|
30363
|
+
}
|
|
30364
|
+
if (receiver === "ioutil" || receiver.endsWith("/ioutil")) {
|
|
30365
|
+
if (method === "WriteFile") return { credPos: 1, api: "ioutil.WriteFile" };
|
|
30366
|
+
}
|
|
30367
|
+
if (method === "WriteString" || method === "Write") {
|
|
30368
|
+
return { credPos: 0, api: `<file>.${method}` };
|
|
30369
|
+
}
|
|
30370
|
+
}
|
|
30371
|
+
return null;
|
|
30372
|
+
}
|
|
30373
|
+
var PlaintextPasswordStoragePass = class {
|
|
30374
|
+
name = "plaintext-password-storage";
|
|
30375
|
+
category = "security";
|
|
30376
|
+
run(ctx) {
|
|
30377
|
+
const { graph, language } = ctx;
|
|
30378
|
+
const file = graph.ir.meta.file;
|
|
30379
|
+
const findings = [];
|
|
30380
|
+
const callsByScope = /* @__PURE__ */ new Map();
|
|
30381
|
+
for (const call of graph.ir.calls) {
|
|
30382
|
+
const scope = call.in_method ?? "<top>";
|
|
30383
|
+
const arr = callsByScope.get(scope) ?? [];
|
|
30384
|
+
arr.push(call);
|
|
30385
|
+
callsByScope.set(scope, arr);
|
|
30386
|
+
}
|
|
30387
|
+
for (const call of graph.ir.calls) {
|
|
30388
|
+
const spec = isWriteStorageCall(call, language);
|
|
30389
|
+
if (!spec) continue;
|
|
30390
|
+
const credArg = call.arguments.find((a) => a.position === spec.credPos);
|
|
30391
|
+
if (!credArg) continue;
|
|
30392
|
+
if (!argLooksLikeCredential(credArg)) continue;
|
|
30393
|
+
const identExpr = (credArg.expression ?? "").trim();
|
|
30394
|
+
const head = identExpr.split(/[.\s(]/, 1)[0] ?? "";
|
|
30395
|
+
const identifier = credArg.variable ?? head;
|
|
30396
|
+
if (!identifier) continue;
|
|
30397
|
+
const scope = call.in_method ?? "<top>";
|
|
30398
|
+
const scopeCalls = callsByScope.get(scope) ?? [];
|
|
30399
|
+
const prior = scopeCalls.filter((c) => c.location.line < call.location.line);
|
|
30400
|
+
if (priorHashOf(identifier, prior)) continue;
|
|
30401
|
+
if (/\b(?:hashpw|hash|sha\d+|md5|bcrypt|argon2|pbkdf2|digest)\b/i.test(credArg.expression ?? "")) {
|
|
30402
|
+
continue;
|
|
30403
|
+
}
|
|
30404
|
+
const line = call.location.line;
|
|
30405
|
+
findings.push({ line, language, api: spec.api, identifier });
|
|
30406
|
+
ctx.addFinding({
|
|
30407
|
+
id: `${this.name}-${file}-${line}`,
|
|
30408
|
+
pass: this.name,
|
|
30409
|
+
category: this.category,
|
|
30410
|
+
rule_id: this.name,
|
|
30411
|
+
cwe: "CWE-256",
|
|
30412
|
+
severity: "high",
|
|
30413
|
+
level: "warning",
|
|
30414
|
+
message: `Credential \`${identifier}\` written in plaintext via \`${spec.api}\`. Passwords / secrets must be hashed (Argon2id, bcrypt) before storage.`,
|
|
30415
|
+
file,
|
|
30416
|
+
line,
|
|
30417
|
+
fix: "Hash the credential with Argon2id / bcrypt before writing it to disk, cookie, KV store, or database.",
|
|
30418
|
+
evidence: { identifier, api: spec.api, language }
|
|
30419
|
+
});
|
|
30420
|
+
}
|
|
30421
|
+
return { findings };
|
|
30422
|
+
}
|
|
30423
|
+
};
|
|
30424
|
+
|
|
30425
|
+
// src/analysis/passes/cleartext-credential-transport-pass.ts
|
|
30426
|
+
var LOCALHOST_RE = /^(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::\d+)?$/i;
|
|
30427
|
+
function isInsecureHttpUrl(urlLiteral) {
|
|
30428
|
+
if (!urlLiteral) return false;
|
|
30429
|
+
if (!/^http:\/\//i.test(urlLiteral)) return false;
|
|
30430
|
+
const rest = urlLiteral.slice("http://".length);
|
|
30431
|
+
const host = rest.split("/", 1)[0] ?? "";
|
|
30432
|
+
if (LOCALHOST_RE.test(host)) return false;
|
|
30433
|
+
return true;
|
|
30434
|
+
}
|
|
30435
|
+
function anyArgCarriesCredential(call, startPos) {
|
|
30436
|
+
for (const a of call.arguments) {
|
|
30437
|
+
if (a.position < startPos) continue;
|
|
30438
|
+
if (argLooksLikeCredential(a)) return true;
|
|
30439
|
+
const expr = (a.expression ?? "").trim();
|
|
30440
|
+
if (!expr) continue;
|
|
30441
|
+
if (/(?:["'`]?(?:password|passwd|pwd|secret|api[_-]?key|auth[_-]?token|credential)["'`]?\s*[:=])/i.test(expr)) {
|
|
30442
|
+
return true;
|
|
30443
|
+
}
|
|
30444
|
+
if (/\b(?:password|passwd|pwd|secret|api_key|api-key|apiKey|auth_token|authToken|credential)\w*\b/i.test(expr)) {
|
|
30445
|
+
return true;
|
|
30446
|
+
}
|
|
30447
|
+
}
|
|
30448
|
+
return false;
|
|
30449
|
+
}
|
|
30450
|
+
var CleartextCredentialTransportPass = class {
|
|
30451
|
+
name = "cleartext-credential-transport";
|
|
30452
|
+
category = "security";
|
|
30453
|
+
run(ctx) {
|
|
30454
|
+
const { graph, language } = ctx;
|
|
30455
|
+
const file = graph.ir.meta.file;
|
|
30456
|
+
const findings = [];
|
|
30457
|
+
for (const call of graph.ir.calls) {
|
|
30458
|
+
const detection = this.detect(call, language);
|
|
30459
|
+
if (!detection) continue;
|
|
30460
|
+
const { api, url } = detection;
|
|
30461
|
+
const line = call.location.line;
|
|
30462
|
+
findings.push({ line, language, api, url });
|
|
30463
|
+
ctx.addFinding({
|
|
30464
|
+
id: `${this.name}-${file}-${line}`,
|
|
30465
|
+
pass: this.name,
|
|
30466
|
+
category: this.category,
|
|
30467
|
+
rule_id: this.name,
|
|
30468
|
+
cwe: "CWE-523",
|
|
30469
|
+
severity: "high",
|
|
30470
|
+
level: "error",
|
|
30471
|
+
message: `Credentials transmitted to \`${url}\` over HTTP via \`${api}\`. Cleartext transport exposes credentials to network observers.`,
|
|
30472
|
+
file,
|
|
30473
|
+
line,
|
|
30474
|
+
fix: "Use HTTPS (https://) for all endpoints that receive credentials. For internal traffic, terminate TLS at the service boundary.",
|
|
30475
|
+
evidence: { api, url, language }
|
|
30476
|
+
});
|
|
30477
|
+
}
|
|
30478
|
+
return { findings };
|
|
30479
|
+
}
|
|
30480
|
+
detect(call, language) {
|
|
30481
|
+
const method = call.method_name ?? "";
|
|
30482
|
+
const receiver = call.receiver ?? "";
|
|
30483
|
+
const recvLower = receiver.toLowerCase();
|
|
30484
|
+
if (language === "python") {
|
|
30485
|
+
if ((recvLower === "requests" || recvLower === "httpx" || recvLower.endsWith(".requests") || recvLower.endsWith(".httpx")) && (method === "post" || method === "put" || method === "patch" || method === "request")) {
|
|
30486
|
+
const url = literalAt(call, method === "request" ? 1 : 0);
|
|
30487
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30488
|
+
return { api: `${receiver}.${method}`, url };
|
|
30489
|
+
}
|
|
30490
|
+
}
|
|
30491
|
+
if (method === "urlopen" && recvLower.includes("urllib")) {
|
|
30492
|
+
const url = literalAt(call, 0);
|
|
30493
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30494
|
+
return { api: "urllib.request.urlopen", url };
|
|
30495
|
+
}
|
|
30496
|
+
}
|
|
30497
|
+
}
|
|
30498
|
+
if (language === "javascript" || language === "typescript") {
|
|
30499
|
+
if ((recvLower === "axios" || recvLower.endsWith(".axios")) && (method === "post" || method === "put" || method === "patch" || method === "request")) {
|
|
30500
|
+
const url = literalAt(call, 0);
|
|
30501
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30502
|
+
return { api: `axios.${method}`, url };
|
|
30503
|
+
}
|
|
30504
|
+
}
|
|
30505
|
+
if (method === "fetch" && receiver === "") {
|
|
30506
|
+
const url = literalAt(call, 0);
|
|
30507
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30508
|
+
return { api: "fetch", url };
|
|
30509
|
+
}
|
|
30510
|
+
}
|
|
30511
|
+
if (method === "request" && (recvLower === "http" || recvLower.endsWith(".http"))) {
|
|
30512
|
+
const url = literalAt(call, 0);
|
|
30513
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30514
|
+
return { api: "http.request", url };
|
|
30515
|
+
}
|
|
30516
|
+
}
|
|
30517
|
+
}
|
|
30518
|
+
if (language === "java" && method === "URL" && receiver === "") {
|
|
30519
|
+
const url = literalAt(call, 0);
|
|
30520
|
+
if (!isInsecureHttpUrl(url)) return null;
|
|
30521
|
+
const scope = call.in_method ?? null;
|
|
30522
|
+
if (!scope) return null;
|
|
30523
|
+
return null;
|
|
30524
|
+
}
|
|
30525
|
+
if (language === "go") {
|
|
30526
|
+
if (method === "Post" && (receiver === "http" || receiver.endsWith("/http"))) {
|
|
30527
|
+
const url = literalAt(call, 0);
|
|
30528
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30529
|
+
return { api: "http.Post", url };
|
|
30530
|
+
}
|
|
30531
|
+
}
|
|
30532
|
+
if (method === "NewRequest" && (receiver === "http" || receiver.endsWith("/http"))) {
|
|
30533
|
+
const url = literalAt(call, 1);
|
|
30534
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 2)) {
|
|
30535
|
+
return { api: "http.NewRequest", url };
|
|
30536
|
+
}
|
|
30537
|
+
}
|
|
30538
|
+
}
|
|
30539
|
+
return null;
|
|
30540
|
+
}
|
|
30541
|
+
};
|
|
30542
|
+
|
|
29933
30543
|
// src/analysis/passes/tls-verify-disabled-pass.ts
|
|
29934
30544
|
var PY_HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
29935
30545
|
"get",
|
|
@@ -31968,6 +32578,10 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
31968
32578
|
if (!disabledPasses.has("weak-hash")) pipeline.add(new WeakHashPass());
|
|
31969
32579
|
if (!disabledPasses.has("weak-crypto")) pipeline.add(new WeakCryptoPass());
|
|
31970
32580
|
if (!disabledPasses.has("weak-random")) pipeline.add(new WeakRandomPass());
|
|
32581
|
+
if (!disabledPasses.has("weak-password-hash")) pipeline.add(new WeakPasswordHashPass());
|
|
32582
|
+
if (!disabledPasses.has("weak-password-encoding")) pipeline.add(new WeakPasswordEncodingPass());
|
|
32583
|
+
if (!disabledPasses.has("plaintext-password-storage")) pipeline.add(new PlaintextPasswordStoragePass());
|
|
32584
|
+
if (!disabledPasses.has("cleartext-credential-transport")) pipeline.add(new CleartextCredentialTransportPass());
|
|
31971
32585
|
if (!disabledPasses.has("tls-verify-disabled")) pipeline.add(new TlsVerifyDisabledPass());
|
|
31972
32586
|
if (!disabledPasses.has("module-side-effect")) pipeline.add(new ModuleSideEffectPass());
|
|
31973
32587
|
if (!disabledPasses.has("cache-no-vary")) pipeline.add(new CacheNoVaryPass());
|