cognium-dev 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/dist/cli.js +668 -6
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -10660,7 +10660,6 @@ var DEFAULT_SINKS = [
|
|
|
10660
10660
|
{ method: "addObject", class: "ModelAndView", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
10661
10661
|
{ method: "println", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10662
10662
|
{ method: "print", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10663
|
-
{ method: "write", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10664
10663
|
{ method: "append", class: "StringBuilder", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10665
10664
|
{ method: "append", class: "StringBuffer", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10666
10665
|
{ method: "handleHyperlinks", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
@@ -10916,8 +10915,10 @@ var DEFAULT_SINKS = [
|
|
|
10916
10915
|
{ method: "exchange", class: "RestTemplate", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
10917
10916
|
{ method: "get", class: "WebClient", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [] },
|
|
10918
10917
|
{ method: "post", class: "WebClient", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [] },
|
|
10919
|
-
{ method: "setAttribute", class: "HttpSession", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [0] },
|
|
10920
|
-
{ method: "putValue", class: "HttpSession", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [0] },
|
|
10918
|
+
{ method: "setAttribute", class: "HttpSession", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [0, 1] },
|
|
10919
|
+
{ method: "putValue", class: "HttpSession", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [0, 1] },
|
|
10920
|
+
{ method: "setAttribute", class: "ServletContext", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [0, 1] },
|
|
10921
|
+
{ method: "setAttribute", class: "HttpServletRequest", type: "trust_boundary", cwe: "CWE-501", severity: "low", arg_positions: [0, 1] },
|
|
10921
10922
|
{ method: "outputElementContent", class: "XMLOutputter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
10922
10923
|
{ method: "output", class: "XMLOutputter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
10923
10924
|
{ method: "outputString", class: "XMLOutputter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
@@ -28793,6 +28794,30 @@ var PROVIDER_PATTERNS = [
|
|
|
28793
28794
|
fix: "Revoke the npm token at https://www.npmjs.com/settings/<user>/tokens and load from environment."
|
|
28794
28795
|
}
|
|
28795
28796
|
];
|
|
28797
|
+
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;
|
|
28798
|
+
var CRED_DYNAMIC_VALUE_RE = /\$\{|process\.env|os\.environ|os\.Getenv|System\.getenv/;
|
|
28799
|
+
var CRED_FUNCTION_DECL_RE = /\b(?:function|func|def|fn)\s+\w+\s*\(/;
|
|
28800
|
+
var CRED_COMPARISON_RE = /(?:===?|!==?|>=|<=|<>)\s*["'`]/;
|
|
28801
|
+
function isLikelyCredentialAssignment(line) {
|
|
28802
|
+
if (CRED_FUNCTION_DECL_RE.test(line))
|
|
28803
|
+
return null;
|
|
28804
|
+
if (CRED_COMPARISON_RE.test(line))
|
|
28805
|
+
return null;
|
|
28806
|
+
const m = line.match(CRED_KEYWORD_RE);
|
|
28807
|
+
if (!m)
|
|
28808
|
+
return null;
|
|
28809
|
+
const name2 = m[1];
|
|
28810
|
+
const value = m[2];
|
|
28811
|
+
if (PLACEHOLDER_RE.test(value))
|
|
28812
|
+
return null;
|
|
28813
|
+
if (CRED_DYNAMIC_VALUE_RE.test(value))
|
|
28814
|
+
return null;
|
|
28815
|
+
if (value.length < 3)
|
|
28816
|
+
return null;
|
|
28817
|
+
if (isAllSameChar(value))
|
|
28818
|
+
return null;
|
|
28819
|
+
return { name: name2, value };
|
|
28820
|
+
}
|
|
28796
28821
|
var STRING_LITERAL_RE = /(["'`])((?:\\.|(?!\1).){8,200})\1/g;
|
|
28797
28822
|
var BASE64ISH_RE = /^[A-Za-z0-9+/=_-]+$/;
|
|
28798
28823
|
var HEXISH_RE = /^[a-fA-F0-9]+$/;
|
|
@@ -28896,6 +28921,33 @@ class ScanSecretsPass {
|
|
|
28896
28921
|
break;
|
|
28897
28922
|
}
|
|
28898
28923
|
}
|
|
28924
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
28925
|
+
const lineText = lines[i2];
|
|
28926
|
+
const lineNum = i2 + 1;
|
|
28927
|
+
const hit = isLikelyCredentialAssignment(lineText);
|
|
28928
|
+
if (!hit)
|
|
28929
|
+
continue;
|
|
28930
|
+
const key = `${lineNum}:hardcoded-credential`;
|
|
28931
|
+
if (seen.has(key))
|
|
28932
|
+
continue;
|
|
28933
|
+
seen.add(key);
|
|
28934
|
+
ctx.addFinding({
|
|
28935
|
+
id: `hardcoded-credential-${file}-${lineNum}`,
|
|
28936
|
+
pass: this.name,
|
|
28937
|
+
category: this.category,
|
|
28938
|
+
rule_id: "hardcoded-credential",
|
|
28939
|
+
cwe: "CWE-798",
|
|
28940
|
+
severity: "high",
|
|
28941
|
+
level: "error",
|
|
28942
|
+
message: `Hardcoded credential: \`${hit.name}\` assigned a literal value`,
|
|
28943
|
+
file,
|
|
28944
|
+
line: lineNum,
|
|
28945
|
+
snippet: lineText.trim().substring(0, 120),
|
|
28946
|
+
fix: "Move the credential to an environment variable or secrets manager; never commit live secrets to source control.",
|
|
28947
|
+
evidence: { kind: "named-credential", name: hit.name }
|
|
28948
|
+
});
|
|
28949
|
+
providerFindings += 1;
|
|
28950
|
+
}
|
|
28899
28951
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
28900
28952
|
const lineText = lines[i2];
|
|
28901
28953
|
const lineNum = i2 + 1;
|
|
@@ -29268,9 +29320,11 @@ class InsecureCookiePass {
|
|
|
29268
29320
|
};
|
|
29269
29321
|
}
|
|
29270
29322
|
detectJavaCookieCtor(call, hasSetSecureTrue, hasSetHttpOnlyTrue) {
|
|
29271
|
-
|
|
29323
|
+
const method = call.method_name ?? "";
|
|
29324
|
+
const isCookieCtor = method === "Cookie" || method.endsWith(".Cookie");
|
|
29325
|
+
if (!isCookieCtor)
|
|
29272
29326
|
return null;
|
|
29273
|
-
const looksLikeCtor = call.is_constructor || !call.receiver && call.receiver_type === "Cookie" || (call.resolution?.target ?? "").endsWith(".<init>");
|
|
29327
|
+
const looksLikeCtor = call.is_constructor || !call.receiver && (call.receiver_type === "Cookie" || (call.receiver_type ?? "").endsWith(".Cookie")) || (call.resolution?.target ?? "").endsWith(".<init>");
|
|
29274
29328
|
if (!looksLikeCtor)
|
|
29275
29329
|
return null;
|
|
29276
29330
|
if (call.arguments.length < 2)
|
|
@@ -30107,6 +30161,606 @@ class WeakRandomPass {
|
|
|
30107
30161
|
}
|
|
30108
30162
|
}
|
|
30109
30163
|
|
|
30164
|
+
// ../circle-ir/dist/analysis/passes/_credential-helpers.js
|
|
30165
|
+
var CRED_KEYWORD_RE2 = /(?:password|passwd|pwd|secret|api[_-]?key|auth[_-]?token|private[_-]?key|access[_-]?key|credential)/i;
|
|
30166
|
+
function isCredentialIdentifier(name2) {
|
|
30167
|
+
if (!name2)
|
|
30168
|
+
return false;
|
|
30169
|
+
if (name2.length < 3)
|
|
30170
|
+
return false;
|
|
30171
|
+
return CRED_KEYWORD_RE2.test(name2);
|
|
30172
|
+
}
|
|
30173
|
+
function argLooksLikeCredential(arg) {
|
|
30174
|
+
if (!arg)
|
|
30175
|
+
return false;
|
|
30176
|
+
if (arg.variable && isCredentialIdentifier(arg.variable))
|
|
30177
|
+
return true;
|
|
30178
|
+
const expr = (arg.expression ?? "").trim();
|
|
30179
|
+
if (!expr)
|
|
30180
|
+
return false;
|
|
30181
|
+
const head = expr.split(/[.\s(]/, 1)[0] ?? "";
|
|
30182
|
+
return isCredentialIdentifier(head);
|
|
30183
|
+
}
|
|
30184
|
+
function stripQuotes6(s) {
|
|
30185
|
+
const t = s.trim();
|
|
30186
|
+
if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'") || t.startsWith("`") && t.endsWith("`")) {
|
|
30187
|
+
return t.slice(1, -1);
|
|
30188
|
+
}
|
|
30189
|
+
return t;
|
|
30190
|
+
}
|
|
30191
|
+
function literalAt(call, position) {
|
|
30192
|
+
const arg = call.arguments.find((a) => a.position === position);
|
|
30193
|
+
if (!arg)
|
|
30194
|
+
return null;
|
|
30195
|
+
const raw = arg.literal ?? arg.expression ?? "";
|
|
30196
|
+
const trimmed = raw.trim();
|
|
30197
|
+
if (trimmed.startsWith('"') || trimmed.startsWith("'") || trimmed.startsWith("`")) {
|
|
30198
|
+
return stripQuotes6(trimmed);
|
|
30199
|
+
}
|
|
30200
|
+
if (arg.literal)
|
|
30201
|
+
return stripQuotes6(arg.literal);
|
|
30202
|
+
return null;
|
|
30203
|
+
}
|
|
30204
|
+
function isHashFunctionCall(call) {
|
|
30205
|
+
const method = call.method_name ?? "";
|
|
30206
|
+
const receiver = call.receiver ?? "";
|
|
30207
|
+
const recvLower = receiver.toLowerCase();
|
|
30208
|
+
if (recvLower === "bcrypt" || recvLower.endsWith(".bcrypt")) {
|
|
30209
|
+
return method === "hashpw" || method === "hash" || method === "hashSync" || method === "GenerateFromPassword" || method === "generate_password_hash";
|
|
30210
|
+
}
|
|
30211
|
+
if (recvLower === "argon2" || recvLower.endsWith(".argon2")) {
|
|
30212
|
+
return method === "hash" || method === "Hash" || method === "PasswordHash";
|
|
30213
|
+
}
|
|
30214
|
+
if (recvLower === "hashlib")
|
|
30215
|
+
return true;
|
|
30216
|
+
if (recvLower === "passlib" || recvLower.includes("passlib.hash"))
|
|
30217
|
+
return true;
|
|
30218
|
+
if (method === "PBKDF2HMAC" || method === "derive")
|
|
30219
|
+
return true;
|
|
30220
|
+
if (recvLower === "crypto") {
|
|
30221
|
+
return method === "createHash" || method === "createHmac" || method === "pbkdf2" || method === "pbkdf2Sync" || method === "scrypt" || method === "scryptSync";
|
|
30222
|
+
}
|
|
30223
|
+
if (receiver === "MessageDigest" || receiver.endsWith(".MessageDigest")) {
|
|
30224
|
+
return method === "getInstance" || method === "update" || method === "digest";
|
|
30225
|
+
}
|
|
30226
|
+
if (receiver === "DigestUtils" || receiver.endsWith(".DigestUtils")) {
|
|
30227
|
+
return true;
|
|
30228
|
+
}
|
|
30229
|
+
if (receiver === "SecretKeyFactory" || receiver.endsWith(".SecretKeyFactory")) {
|
|
30230
|
+
return method === "getInstance" || method === "generateSecret";
|
|
30231
|
+
}
|
|
30232
|
+
if (method === "PBEKeySpec")
|
|
30233
|
+
return true;
|
|
30234
|
+
if (receiver === "md5" || receiver === "sha1" || receiver === "sha256" || receiver === "sha512" || receiver === "sha3" || receiver.endsWith("/md5") || receiver.endsWith("/sha1") || receiver.endsWith("/sha256") || receiver.endsWith("/sha512")) {
|
|
30235
|
+
return method === "New" || method === "Sum" || method === "New224" || method === "New384";
|
|
30236
|
+
}
|
|
30237
|
+
const m = method.toLowerCase();
|
|
30238
|
+
if (m === "hash" || m === "hashpw" || m === "hashsync" || m === "pbkdf2" || m === "pbkdf2sync" || m === "scrypt" || m === "scryptsync")
|
|
30239
|
+
return true;
|
|
30240
|
+
return false;
|
|
30241
|
+
}
|
|
30242
|
+
function priorHashOf(varName, priorCalls) {
|
|
30243
|
+
for (const c of priorCalls) {
|
|
30244
|
+
if (!isHashFunctionCall(c))
|
|
30245
|
+
continue;
|
|
30246
|
+
for (const a of c.arguments) {
|
|
30247
|
+
if (a.variable === varName)
|
|
30248
|
+
return true;
|
|
30249
|
+
const head = (a.expression ?? "").trim().split(/[.\s(]/, 1)[0];
|
|
30250
|
+
if (head === varName)
|
|
30251
|
+
return true;
|
|
30252
|
+
}
|
|
30253
|
+
}
|
|
30254
|
+
return false;
|
|
30255
|
+
}
|
|
30256
|
+
|
|
30257
|
+
// ../circle-ir/dist/analysis/passes/weak-password-hash-pass.js
|
|
30258
|
+
var FAST_HASH_NAMES = new Set([
|
|
30259
|
+
"sha224",
|
|
30260
|
+
"sha-224",
|
|
30261
|
+
"sha256",
|
|
30262
|
+
"sha-256",
|
|
30263
|
+
"sha384",
|
|
30264
|
+
"sha-384",
|
|
30265
|
+
"sha512",
|
|
30266
|
+
"sha-512",
|
|
30267
|
+
"sha3",
|
|
30268
|
+
"sha-3",
|
|
30269
|
+
"sha3-256",
|
|
30270
|
+
"sha3-512"
|
|
30271
|
+
]);
|
|
30272
|
+
var BCRYPT_MIN_COST = 10;
|
|
30273
|
+
var PBKDF2_MIN_ITERATIONS = 1e5;
|
|
30274
|
+
function intLiteral(s) {
|
|
30275
|
+
if (s == null)
|
|
30276
|
+
return null;
|
|
30277
|
+
const t = s.trim();
|
|
30278
|
+
if (!/^-?\d+$/.test(t))
|
|
30279
|
+
return null;
|
|
30280
|
+
const n = parseInt(t, 10);
|
|
30281
|
+
return Number.isFinite(n) ? n : null;
|
|
30282
|
+
}
|
|
30283
|
+
function pyKwargInt(call, name2) {
|
|
30284
|
+
for (const a of call.arguments) {
|
|
30285
|
+
const expr = (a.expression ?? "").trim();
|
|
30286
|
+
const m = expr.match(new RegExp(`^${name2}\\s*=\\s*(-?\\d+)$`));
|
|
30287
|
+
if (m)
|
|
30288
|
+
return parseInt(m[1], 10);
|
|
30289
|
+
}
|
|
30290
|
+
return null;
|
|
30291
|
+
}
|
|
30292
|
+
|
|
30293
|
+
class WeakPasswordHashPass {
|
|
30294
|
+
name = "weak-password-hash";
|
|
30295
|
+
category = "security";
|
|
30296
|
+
run(ctx) {
|
|
30297
|
+
const { graph, language } = ctx;
|
|
30298
|
+
const file = graph.ir.meta.file;
|
|
30299
|
+
const findings = [];
|
|
30300
|
+
for (const call of graph.ir.calls) {
|
|
30301
|
+
const detection = this.detect(call, language);
|
|
30302
|
+
if (!detection)
|
|
30303
|
+
continue;
|
|
30304
|
+
const { kind, api } = detection;
|
|
30305
|
+
const line = call.location.line;
|
|
30306
|
+
findings.push({ line, language, kind, api });
|
|
30307
|
+
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}).`;
|
|
30308
|
+
ctx.addFinding({
|
|
30309
|
+
id: `${this.name}-${file}-${line}`,
|
|
30310
|
+
pass: this.name,
|
|
30311
|
+
category: this.category,
|
|
30312
|
+
rule_id: this.name,
|
|
30313
|
+
cwe: "CWE-916",
|
|
30314
|
+
severity: "high",
|
|
30315
|
+
level: "warning",
|
|
30316
|
+
message,
|
|
30317
|
+
file,
|
|
30318
|
+
line,
|
|
30319
|
+
fix: "Use a memory-hard password-hashing function with appropriate cost: " + "Argon2id (recommended), bcrypt (cost ≥ 12), scrypt, or PBKDF2 with ≥ 600k iterations.",
|
|
30320
|
+
evidence: { kind, api, language }
|
|
30321
|
+
});
|
|
30322
|
+
}
|
|
30323
|
+
return { findings };
|
|
30324
|
+
}
|
|
30325
|
+
detect(call, language) {
|
|
30326
|
+
const method = call.method_name ?? "";
|
|
30327
|
+
const receiver = call.receiver ?? "";
|
|
30328
|
+
const recvLower = receiver.toLowerCase();
|
|
30329
|
+
if (recvLower === "bcrypt" || recvLower.endsWith(".bcrypt")) {
|
|
30330
|
+
if (method === "gensalt") {
|
|
30331
|
+
const rounds = pyKwargInt(call, "rounds");
|
|
30332
|
+
if (rounds !== null && rounds < BCRYPT_MIN_COST) {
|
|
30333
|
+
return { kind: "low-bcrypt-cost", api: "bcrypt.gensalt" };
|
|
30334
|
+
}
|
|
30335
|
+
}
|
|
30336
|
+
if (method === "hash" || method === "hashSync") {
|
|
30337
|
+
const cost = intLiteral(literalAt(call, 1));
|
|
30338
|
+
if (cost !== null && cost < BCRYPT_MIN_COST) {
|
|
30339
|
+
return { kind: "low-bcrypt-cost", api: `bcrypt.${method}` };
|
|
30340
|
+
}
|
|
30341
|
+
}
|
|
30342
|
+
if (method === "GenerateFromPassword") {
|
|
30343
|
+
const arg1 = call.arguments.find((a) => a.position === 1);
|
|
30344
|
+
const expr = (arg1?.expression ?? "").trim();
|
|
30345
|
+
const n = intLiteral(expr);
|
|
30346
|
+
if (n !== null && n < BCRYPT_MIN_COST) {
|
|
30347
|
+
return { kind: "low-bcrypt-cost", api: "bcrypt.GenerateFromPassword" };
|
|
30348
|
+
}
|
|
30349
|
+
if (expr === "bcrypt.MinCost") {
|
|
30350
|
+
return { kind: "low-bcrypt-cost", api: "bcrypt.GenerateFromPassword" };
|
|
30351
|
+
}
|
|
30352
|
+
}
|
|
30353
|
+
}
|
|
30354
|
+
if (method === "PBKDF2HMAC") {
|
|
30355
|
+
const iters = pyKwargInt(call, "iterations");
|
|
30356
|
+
if (iters !== null && iters < PBKDF2_MIN_ITERATIONS) {
|
|
30357
|
+
return { kind: "low-pbkdf2-iterations", api: "PBKDF2HMAC" };
|
|
30358
|
+
}
|
|
30359
|
+
}
|
|
30360
|
+
if ((method === "pbkdf2" || method === "pbkdf2Sync") && (recvLower === "crypto" || recvLower.endsWith(".crypto"))) {
|
|
30361
|
+
const iters = intLiteral(literalAt(call, 2));
|
|
30362
|
+
if (iters !== null && iters < PBKDF2_MIN_ITERATIONS) {
|
|
30363
|
+
return { kind: "low-pbkdf2-iterations", api: `crypto.${method}` };
|
|
30364
|
+
}
|
|
30365
|
+
}
|
|
30366
|
+
if (method === "PBEKeySpec" && language === "java") {
|
|
30367
|
+
const iters = intLiteral(literalAt(call, 2));
|
|
30368
|
+
if (iters !== null && iters < PBKDF2_MIN_ITERATIONS) {
|
|
30369
|
+
return { kind: "low-pbkdf2-iterations", api: "PBEKeySpec" };
|
|
30370
|
+
}
|
|
30371
|
+
}
|
|
30372
|
+
if (language === "python" && (recvLower === "hashlib" || recvLower.endsWith(".hashlib"))) {
|
|
30373
|
+
if (FAST_HASH_NAMES.has(method.toLowerCase())) {
|
|
30374
|
+
if (argLooksLikeCredential(call.arguments.find((a) => a.position === 0))) {
|
|
30375
|
+
return { kind: "fast-unsalted-hash", api: `hashlib.${method}` };
|
|
30376
|
+
}
|
|
30377
|
+
}
|
|
30378
|
+
if (method === "new") {
|
|
30379
|
+
const algo = literalAt(call, 0)?.toLowerCase() ?? "";
|
|
30380
|
+
if (FAST_HASH_NAMES.has(algo) && argLooksLikeCredential(call.arguments.find((a) => a.position === 1))) {
|
|
30381
|
+
return { kind: "fast-unsalted-hash", api: `hashlib.new(${algo})` };
|
|
30382
|
+
}
|
|
30383
|
+
}
|
|
30384
|
+
}
|
|
30385
|
+
if ((language === "javascript" || language === "typescript") && method === "update") {
|
|
30386
|
+
const recvExpr = (receiver ?? "").toLowerCase();
|
|
30387
|
+
const hashLike = recvExpr.includes("hash") || recvExpr.includes("createhash") || recvExpr.includes("sha") || recvExpr.includes("md");
|
|
30388
|
+
if (hashLike && argLooksLikeCredential(call.arguments.find((a) => a.position === 0))) {
|
|
30389
|
+
return { kind: "fast-unsalted-hash", api: "crypto.createHash().update" };
|
|
30390
|
+
}
|
|
30391
|
+
}
|
|
30392
|
+
if (language === "java" && method === "update") {
|
|
30393
|
+
const recvName = (receiver ?? "").toLowerCase();
|
|
30394
|
+
const looksLikeDigest = recvName.includes("digest") || recvName.includes("md") || recvName.includes("hash");
|
|
30395
|
+
if (looksLikeDigest && argLooksLikeCredential(call.arguments.find((a) => a.position === 0))) {
|
|
30396
|
+
return { kind: "fast-unsalted-hash", api: "MessageDigest.update" };
|
|
30397
|
+
}
|
|
30398
|
+
}
|
|
30399
|
+
if (language === "go") {
|
|
30400
|
+
const isFastPkg = receiver === "sha256" || receiver === "sha512" || receiver === "sha3" || receiver === "sha224";
|
|
30401
|
+
if (isFastPkg && (method === "Sum256" || method === "Sum512" || method === "Sum224" || method === "Sum384" || method === "Sum")) {
|
|
30402
|
+
const expr = (call.arguments.find((a) => a.position === 0)?.expression ?? "").trim();
|
|
30403
|
+
const inner = expr.replace(/^\[\]byte\s*\(\s*/, "").replace(/\s*\)\s*$/, "");
|
|
30404
|
+
if (argLooksLikeCredential({ position: 0, expression: inner, variable: inner })) {
|
|
30405
|
+
return { kind: "fast-unsalted-hash", api: `${receiver}.${method}` };
|
|
30406
|
+
}
|
|
30407
|
+
}
|
|
30408
|
+
}
|
|
30409
|
+
return null;
|
|
30410
|
+
}
|
|
30411
|
+
}
|
|
30412
|
+
|
|
30413
|
+
// ../circle-ir/dist/analysis/passes/weak-password-encoding-pass.js
|
|
30414
|
+
function isBasicAuthContext(call, code) {
|
|
30415
|
+
const line = call.location.line;
|
|
30416
|
+
if (line < 1)
|
|
30417
|
+
return false;
|
|
30418
|
+
const lines = code.split(`
|
|
30419
|
+
`);
|
|
30420
|
+
const start2 = Math.max(0, line - 2);
|
|
30421
|
+
const end = Math.min(lines.length, line + 1);
|
|
30422
|
+
const window2 = lines.slice(start2, end).join(`
|
|
30423
|
+
`);
|
|
30424
|
+
return /["'`]Basic\s/i.test(window2);
|
|
30425
|
+
}
|
|
30426
|
+
|
|
30427
|
+
class WeakPasswordEncodingPass {
|
|
30428
|
+
name = "weak-password-encoding";
|
|
30429
|
+
category = "security";
|
|
30430
|
+
run(ctx) {
|
|
30431
|
+
const { graph, language, code } = ctx;
|
|
30432
|
+
const file = graph.ir.meta.file;
|
|
30433
|
+
const findings = [];
|
|
30434
|
+
for (const call of graph.ir.calls) {
|
|
30435
|
+
const api = this.detect(call, language);
|
|
30436
|
+
if (!api)
|
|
30437
|
+
continue;
|
|
30438
|
+
if (isBasicAuthContext(call, code))
|
|
30439
|
+
continue;
|
|
30440
|
+
const line = call.location.line;
|
|
30441
|
+
findings.push({ line, language, api });
|
|
30442
|
+
ctx.addFinding({
|
|
30443
|
+
id: `${this.name}-${file}-${line}`,
|
|
30444
|
+
pass: this.name,
|
|
30445
|
+
category: this.category,
|
|
30446
|
+
rule_id: this.name,
|
|
30447
|
+
cwe: "CWE-261",
|
|
30448
|
+
severity: "medium",
|
|
30449
|
+
level: "warning",
|
|
30450
|
+
message: `Credential encoded via \`${api}\` — encoding is NOT encryption. ` + "Base64/hex provide no confidentiality; anyone with the encoded value can decode it.",
|
|
30451
|
+
file,
|
|
30452
|
+
line,
|
|
30453
|
+
fix: "For storage, use a password hash (Argon2id / bcrypt). " + "For transport, use TLS. For symmetric secrecy, use authenticated encryption (AES-GCM).",
|
|
30454
|
+
evidence: { api, language }
|
|
30455
|
+
});
|
|
30456
|
+
}
|
|
30457
|
+
return { findings };
|
|
30458
|
+
}
|
|
30459
|
+
detect(call, language) {
|
|
30460
|
+
const method = call.method_name ?? "";
|
|
30461
|
+
const receiver = call.receiver ?? "";
|
|
30462
|
+
const recvLower = receiver.toLowerCase();
|
|
30463
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
30464
|
+
if (language === "python") {
|
|
30465
|
+
if (recvLower === "base64" && (method === "b64encode" || method === "urlsafe_b64encode" || method === "standard_b64encode")) {
|
|
30466
|
+
if (argLooksLikeCredential(arg0))
|
|
30467
|
+
return `base64.${method}`;
|
|
30468
|
+
}
|
|
30469
|
+
if (recvLower === "binascii" && method === "hexlify") {
|
|
30470
|
+
if (argLooksLikeCredential(arg0))
|
|
30471
|
+
return "binascii.hexlify";
|
|
30472
|
+
}
|
|
30473
|
+
}
|
|
30474
|
+
if (language === "javascript" || language === "typescript") {
|
|
30475
|
+
if (method === "toString") {
|
|
30476
|
+
const encoding = literalAt(call, 0);
|
|
30477
|
+
if (encoding === "base64" || encoding === "hex" || encoding === "base64url") {
|
|
30478
|
+
const recv = (receiver ?? "").toLowerCase();
|
|
30479
|
+
if (recv.includes("buffer.from") && /(?:password|passwd|pwd|secret|api[_-]?key|auth[_-]?token|private[_-]?key|access[_-]?key|credential)/i.test(receiver ?? "")) {
|
|
30480
|
+
return `Buffer.from().toString('${encoding}')`;
|
|
30481
|
+
}
|
|
30482
|
+
}
|
|
30483
|
+
}
|
|
30484
|
+
if (method === "btoa" && receiver === "") {
|
|
30485
|
+
if (argLooksLikeCredential(arg0))
|
|
30486
|
+
return "btoa";
|
|
30487
|
+
}
|
|
30488
|
+
}
|
|
30489
|
+
if (language === "java") {
|
|
30490
|
+
if (method === "encodeToString") {
|
|
30491
|
+
const recv = (receiver ?? "").toLowerCase();
|
|
30492
|
+
if (recv.includes("encoder") || recv.includes("base64")) {
|
|
30493
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
30494
|
+
const head = expr.split(/[.\s(]/, 1)[0] ?? "";
|
|
30495
|
+
if (argLooksLikeCredential({ position: 0, expression: head, variable: head })) {
|
|
30496
|
+
return "Base64.encodeToString";
|
|
30497
|
+
}
|
|
30498
|
+
}
|
|
30499
|
+
}
|
|
30500
|
+
if (method === "encodeHexString" && (receiver === "Hex" || receiver.endsWith(".Hex"))) {
|
|
30501
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
30502
|
+
const head = expr.split(/[.\s(]/, 1)[0] ?? "";
|
|
30503
|
+
if (argLooksLikeCredential({ position: 0, expression: head, variable: head })) {
|
|
30504
|
+
return "Hex.encodeHexString";
|
|
30505
|
+
}
|
|
30506
|
+
}
|
|
30507
|
+
}
|
|
30508
|
+
if (language === "go") {
|
|
30509
|
+
if (method === "EncodeToString") {
|
|
30510
|
+
const recv = (receiver ?? "").toLowerCase();
|
|
30511
|
+
if (recv.includes("base64") || recv.includes("hex") || recv.includes("encoding")) {
|
|
30512
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
30513
|
+
const inner = expr.replace(/^\[\]byte\s*\(\s*/, "").replace(/\s*\)\s*$/, "");
|
|
30514
|
+
const head = inner.split(/[.\s(]/, 1)[0] ?? "";
|
|
30515
|
+
if (argLooksLikeCredential({ position: 0, expression: head, variable: head })) {
|
|
30516
|
+
return recv.includes("hex") ? "hex.EncodeToString" : "base64.EncodeToString";
|
|
30517
|
+
}
|
|
30518
|
+
}
|
|
30519
|
+
}
|
|
30520
|
+
}
|
|
30521
|
+
return null;
|
|
30522
|
+
}
|
|
30523
|
+
}
|
|
30524
|
+
|
|
30525
|
+
// ../circle-ir/dist/analysis/passes/plaintext-password-storage-pass.js
|
|
30526
|
+
function isWriteStorageCall(call, language) {
|
|
30527
|
+
const method = call.method_name ?? "";
|
|
30528
|
+
const receiver = call.receiver ?? "";
|
|
30529
|
+
const recvLower = receiver.toLowerCase();
|
|
30530
|
+
if (language === "python") {
|
|
30531
|
+
if (method === "write" || method === "writelines") {
|
|
30532
|
+
return { credPos: 0, api: `<file>.${method}` };
|
|
30533
|
+
}
|
|
30534
|
+
if ((recvLower === "pickle" || recvLower === "json" || recvLower === "yaml") && (method === "dump" || method === "dumps")) {
|
|
30535
|
+
return { credPos: 0, api: `${receiver}.${method}` };
|
|
30536
|
+
}
|
|
30537
|
+
if (recvLower === "redis" && (method === "set" || method === "setex" || method === "hset")) {
|
|
30538
|
+
return { credPos: 1, api: `redis.${method}` };
|
|
30539
|
+
}
|
|
30540
|
+
}
|
|
30541
|
+
if (language === "javascript" || language === "typescript") {
|
|
30542
|
+
if ((recvLower === "fs" || recvLower.endsWith(".fs")) && (method === "writeFile" || method === "writeFileSync" || method === "appendFile" || method === "appendFileSync")) {
|
|
30543
|
+
return { credPos: 1, api: `fs.${method}` };
|
|
30544
|
+
}
|
|
30545
|
+
if ((recvLower === "localstorage" || recvLower === "sessionstorage") && method === "setItem") {
|
|
30546
|
+
return { credPos: 1, api: `${receiver}.setItem` };
|
|
30547
|
+
}
|
|
30548
|
+
if (recvLower === "redis" && (method === "set" || method === "setex" || method === "hset")) {
|
|
30549
|
+
return { credPos: 1, api: `redis.${method}` };
|
|
30550
|
+
}
|
|
30551
|
+
}
|
|
30552
|
+
if (language === "java") {
|
|
30553
|
+
if ((receiver === "Files" || receiver.endsWith(".Files")) && (method === "write" || method === "writeString")) {
|
|
30554
|
+
return { credPos: 1, api: `Files.${method}` };
|
|
30555
|
+
}
|
|
30556
|
+
if (method === "write") {
|
|
30557
|
+
const lc = (receiver ?? "").toLowerCase();
|
|
30558
|
+
if (lc.includes("writer") || lc.includes("file") || lc.includes("stream")) {
|
|
30559
|
+
return { credPos: 0, api: `${receiver}.write` };
|
|
30560
|
+
}
|
|
30561
|
+
}
|
|
30562
|
+
}
|
|
30563
|
+
if (language === "go") {
|
|
30564
|
+
if (receiver === "os" || receiver.endsWith("/os")) {
|
|
30565
|
+
if (method === "WriteFile")
|
|
30566
|
+
return { credPos: 1, api: "os.WriteFile" };
|
|
30567
|
+
}
|
|
30568
|
+
if (receiver === "ioutil" || receiver.endsWith("/ioutil")) {
|
|
30569
|
+
if (method === "WriteFile")
|
|
30570
|
+
return { credPos: 1, api: "ioutil.WriteFile" };
|
|
30571
|
+
}
|
|
30572
|
+
if (method === "WriteString" || method === "Write") {
|
|
30573
|
+
return { credPos: 0, api: `<file>.${method}` };
|
|
30574
|
+
}
|
|
30575
|
+
}
|
|
30576
|
+
return null;
|
|
30577
|
+
}
|
|
30578
|
+
|
|
30579
|
+
class PlaintextPasswordStoragePass {
|
|
30580
|
+
name = "plaintext-password-storage";
|
|
30581
|
+
category = "security";
|
|
30582
|
+
run(ctx) {
|
|
30583
|
+
const { graph, language } = ctx;
|
|
30584
|
+
const file = graph.ir.meta.file;
|
|
30585
|
+
const findings = [];
|
|
30586
|
+
const callsByScope = new Map;
|
|
30587
|
+
for (const call of graph.ir.calls) {
|
|
30588
|
+
const scope = call.in_method ?? "<top>";
|
|
30589
|
+
const arr = callsByScope.get(scope) ?? [];
|
|
30590
|
+
arr.push(call);
|
|
30591
|
+
callsByScope.set(scope, arr);
|
|
30592
|
+
}
|
|
30593
|
+
for (const call of graph.ir.calls) {
|
|
30594
|
+
const spec = isWriteStorageCall(call, language);
|
|
30595
|
+
if (!spec)
|
|
30596
|
+
continue;
|
|
30597
|
+
const credArg = call.arguments.find((a) => a.position === spec.credPos);
|
|
30598
|
+
if (!credArg)
|
|
30599
|
+
continue;
|
|
30600
|
+
if (!argLooksLikeCredential(credArg))
|
|
30601
|
+
continue;
|
|
30602
|
+
const identExpr = (credArg.expression ?? "").trim();
|
|
30603
|
+
const head = identExpr.split(/[.\s(]/, 1)[0] ?? "";
|
|
30604
|
+
const identifier = credArg.variable ?? head;
|
|
30605
|
+
if (!identifier)
|
|
30606
|
+
continue;
|
|
30607
|
+
const scope = call.in_method ?? "<top>";
|
|
30608
|
+
const scopeCalls = callsByScope.get(scope) ?? [];
|
|
30609
|
+
const prior = scopeCalls.filter((c) => c.location.line < call.location.line);
|
|
30610
|
+
if (priorHashOf(identifier, prior))
|
|
30611
|
+
continue;
|
|
30612
|
+
if (/\b(?:hashpw|hash|sha\d+|md5|bcrypt|argon2|pbkdf2|digest)\b/i.test(credArg.expression ?? "")) {
|
|
30613
|
+
continue;
|
|
30614
|
+
}
|
|
30615
|
+
const line = call.location.line;
|
|
30616
|
+
findings.push({ line, language, api: spec.api, identifier });
|
|
30617
|
+
ctx.addFinding({
|
|
30618
|
+
id: `${this.name}-${file}-${line}`,
|
|
30619
|
+
pass: this.name,
|
|
30620
|
+
category: this.category,
|
|
30621
|
+
rule_id: this.name,
|
|
30622
|
+
cwe: "CWE-256",
|
|
30623
|
+
severity: "high",
|
|
30624
|
+
level: "warning",
|
|
30625
|
+
message: `Credential \`${identifier}\` written in plaintext via \`${spec.api}\`. ` + "Passwords / secrets must be hashed (Argon2id, bcrypt) before storage.",
|
|
30626
|
+
file,
|
|
30627
|
+
line,
|
|
30628
|
+
fix: "Hash the credential with Argon2id / bcrypt before writing it to " + "disk, cookie, KV store, or database.",
|
|
30629
|
+
evidence: { identifier, api: spec.api, language }
|
|
30630
|
+
});
|
|
30631
|
+
}
|
|
30632
|
+
return { findings };
|
|
30633
|
+
}
|
|
30634
|
+
}
|
|
30635
|
+
|
|
30636
|
+
// ../circle-ir/dist/analysis/passes/cleartext-credential-transport-pass.js
|
|
30637
|
+
var LOCALHOST_RE = /^(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::\d+)?$/i;
|
|
30638
|
+
function isInsecureHttpUrl(urlLiteral) {
|
|
30639
|
+
if (!urlLiteral)
|
|
30640
|
+
return false;
|
|
30641
|
+
if (!/^http:\/\//i.test(urlLiteral))
|
|
30642
|
+
return false;
|
|
30643
|
+
const rest = urlLiteral.slice("http://".length);
|
|
30644
|
+
const host = rest.split("/", 1)[0] ?? "";
|
|
30645
|
+
if (LOCALHOST_RE.test(host))
|
|
30646
|
+
return false;
|
|
30647
|
+
return true;
|
|
30648
|
+
}
|
|
30649
|
+
function anyArgCarriesCredential(call, startPos) {
|
|
30650
|
+
for (const a of call.arguments) {
|
|
30651
|
+
if (a.position < startPos)
|
|
30652
|
+
continue;
|
|
30653
|
+
if (argLooksLikeCredential(a))
|
|
30654
|
+
return true;
|
|
30655
|
+
const expr = (a.expression ?? "").trim();
|
|
30656
|
+
if (!expr)
|
|
30657
|
+
continue;
|
|
30658
|
+
if (/(?:["'`]?(?:password|passwd|pwd|secret|api[_-]?key|auth[_-]?token|credential)["'`]?\s*[:=])/i.test(expr)) {
|
|
30659
|
+
return true;
|
|
30660
|
+
}
|
|
30661
|
+
if (/\b(?:password|passwd|pwd|secret|api_key|api-key|apiKey|auth_token|authToken|credential)\w*\b/i.test(expr)) {
|
|
30662
|
+
return true;
|
|
30663
|
+
}
|
|
30664
|
+
}
|
|
30665
|
+
return false;
|
|
30666
|
+
}
|
|
30667
|
+
|
|
30668
|
+
class CleartextCredentialTransportPass {
|
|
30669
|
+
name = "cleartext-credential-transport";
|
|
30670
|
+
category = "security";
|
|
30671
|
+
run(ctx) {
|
|
30672
|
+
const { graph, language } = ctx;
|
|
30673
|
+
const file = graph.ir.meta.file;
|
|
30674
|
+
const findings = [];
|
|
30675
|
+
for (const call of graph.ir.calls) {
|
|
30676
|
+
const detection = this.detect(call, language);
|
|
30677
|
+
if (!detection)
|
|
30678
|
+
continue;
|
|
30679
|
+
const { api, url } = detection;
|
|
30680
|
+
const line = call.location.line;
|
|
30681
|
+
findings.push({ line, language, api, url });
|
|
30682
|
+
ctx.addFinding({
|
|
30683
|
+
id: `${this.name}-${file}-${line}`,
|
|
30684
|
+
pass: this.name,
|
|
30685
|
+
category: this.category,
|
|
30686
|
+
rule_id: this.name,
|
|
30687
|
+
cwe: "CWE-523",
|
|
30688
|
+
severity: "high",
|
|
30689
|
+
level: "error",
|
|
30690
|
+
message: `Credentials transmitted to \`${url}\` over HTTP via \`${api}\`. ` + "Cleartext transport exposes credentials to network observers.",
|
|
30691
|
+
file,
|
|
30692
|
+
line,
|
|
30693
|
+
fix: "Use HTTPS (https://) for all endpoints that receive credentials. " + "For internal traffic, terminate TLS at the service boundary.",
|
|
30694
|
+
evidence: { api, url, language }
|
|
30695
|
+
});
|
|
30696
|
+
}
|
|
30697
|
+
return { findings };
|
|
30698
|
+
}
|
|
30699
|
+
detect(call, language) {
|
|
30700
|
+
const method = call.method_name ?? "";
|
|
30701
|
+
const receiver = call.receiver ?? "";
|
|
30702
|
+
const recvLower = receiver.toLowerCase();
|
|
30703
|
+
if (language === "python") {
|
|
30704
|
+
if ((recvLower === "requests" || recvLower === "httpx" || recvLower.endsWith(".requests") || recvLower.endsWith(".httpx")) && (method === "post" || method === "put" || method === "patch" || method === "request")) {
|
|
30705
|
+
const url = literalAt(call, method === "request" ? 1 : 0);
|
|
30706
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30707
|
+
return { api: `${receiver}.${method}`, url };
|
|
30708
|
+
}
|
|
30709
|
+
}
|
|
30710
|
+
if (method === "urlopen" && recvLower.includes("urllib")) {
|
|
30711
|
+
const url = literalAt(call, 0);
|
|
30712
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30713
|
+
return { api: "urllib.request.urlopen", url };
|
|
30714
|
+
}
|
|
30715
|
+
}
|
|
30716
|
+
}
|
|
30717
|
+
if (language === "javascript" || language === "typescript") {
|
|
30718
|
+
if ((recvLower === "axios" || recvLower.endsWith(".axios")) && (method === "post" || method === "put" || method === "patch" || method === "request")) {
|
|
30719
|
+
const url = literalAt(call, 0);
|
|
30720
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30721
|
+
return { api: `axios.${method}`, url };
|
|
30722
|
+
}
|
|
30723
|
+
}
|
|
30724
|
+
if (method === "fetch" && receiver === "") {
|
|
30725
|
+
const url = literalAt(call, 0);
|
|
30726
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30727
|
+
return { api: "fetch", url };
|
|
30728
|
+
}
|
|
30729
|
+
}
|
|
30730
|
+
if (method === "request" && (recvLower === "http" || recvLower.endsWith(".http"))) {
|
|
30731
|
+
const url = literalAt(call, 0);
|
|
30732
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30733
|
+
return { api: "http.request", url };
|
|
30734
|
+
}
|
|
30735
|
+
}
|
|
30736
|
+
}
|
|
30737
|
+
if (language === "java" && method === "URL" && receiver === "") {
|
|
30738
|
+
const url = literalAt(call, 0);
|
|
30739
|
+
if (!isInsecureHttpUrl(url))
|
|
30740
|
+
return null;
|
|
30741
|
+
const scope = call.in_method ?? null;
|
|
30742
|
+
if (!scope)
|
|
30743
|
+
return null;
|
|
30744
|
+
return null;
|
|
30745
|
+
}
|
|
30746
|
+
if (language === "go") {
|
|
30747
|
+
if (method === "Post" && (receiver === "http" || receiver.endsWith("/http"))) {
|
|
30748
|
+
const url = literalAt(call, 0);
|
|
30749
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 1)) {
|
|
30750
|
+
return { api: "http.Post", url };
|
|
30751
|
+
}
|
|
30752
|
+
}
|
|
30753
|
+
if (method === "NewRequest" && (receiver === "http" || receiver.endsWith("/http"))) {
|
|
30754
|
+
const url = literalAt(call, 1);
|
|
30755
|
+
if (isInsecureHttpUrl(url) && anyArgCarriesCredential(call, 2)) {
|
|
30756
|
+
return { api: "http.NewRequest", url };
|
|
30757
|
+
}
|
|
30758
|
+
}
|
|
30759
|
+
}
|
|
30760
|
+
return null;
|
|
30761
|
+
}
|
|
30762
|
+
}
|
|
30763
|
+
|
|
30110
30764
|
// ../circle-ir/dist/analysis/passes/tls-verify-disabled-pass.js
|
|
30111
30765
|
var PY_HTTP_METHODS = new Set([
|
|
30112
30766
|
"get",
|
|
@@ -32408,6 +33062,14 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
32408
33062
|
pipeline.add(new WeakCryptoPass);
|
|
32409
33063
|
if (!disabledPasses.has("weak-random"))
|
|
32410
33064
|
pipeline.add(new WeakRandomPass);
|
|
33065
|
+
if (!disabledPasses.has("weak-password-hash"))
|
|
33066
|
+
pipeline.add(new WeakPasswordHashPass);
|
|
33067
|
+
if (!disabledPasses.has("weak-password-encoding"))
|
|
33068
|
+
pipeline.add(new WeakPasswordEncodingPass);
|
|
33069
|
+
if (!disabledPasses.has("plaintext-password-storage"))
|
|
33070
|
+
pipeline.add(new PlaintextPasswordStoragePass);
|
|
33071
|
+
if (!disabledPasses.has("cleartext-credential-transport"))
|
|
33072
|
+
pipeline.add(new CleartextCredentialTransportPass);
|
|
32411
33073
|
if (!disabledPasses.has("tls-verify-disabled"))
|
|
32412
33074
|
pipeline.add(new TlsVerifyDisabledPass);
|
|
32413
33075
|
if (!disabledPasses.has("module-side-effect"))
|
|
@@ -32615,7 +33277,7 @@ var colors = {
|
|
|
32615
33277
|
};
|
|
32616
33278
|
|
|
32617
33279
|
// src/version.ts
|
|
32618
|
-
var version = "3.
|
|
33280
|
+
var version = "3.81.0";
|
|
32619
33281
|
|
|
32620
33282
|
// src/formatters.ts
|
|
32621
33283
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.81.0",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"registry": "https://registry.npmjs.org/"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"circle-ir": "^3.
|
|
68
|
+
"circle-ir": "^3.81.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|