@profullstack/threatcrush 0.9.0 → 0.10.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/daemon.js +149 -0
- package/dist/daemon.js.map +1 -1
- package/dist/index.js +149 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10405,6 +10405,155 @@ var CODE_RULES = [
|
|
|
10405
10405
|
// constant the author wrote and is not a finding.
|
|
10406
10406
|
pattern: /\btemplate\s*\.\s*(?:HTML|JS|CSS|HTMLAttr|URL|Srcset)\s*\(\s*(?!\s*[`"])/,
|
|
10407
10407
|
needsContext: true
|
|
10408
|
+
},
|
|
10409
|
+
// ── Misconfiguration, weak crypto and the rest of the injection family ────
|
|
10410
|
+
//
|
|
10411
|
+
// Each of these is a defect the *line* shows — a debug flag left on, a
|
|
10412
|
+
// wildcard CORS origin, a key literal, a fast password hash — so the safe
|
|
10413
|
+
// counterpart in the corpus differs on something visible here, not three
|
|
10414
|
+
// functions away. The classes that genuinely need whole-function reasoning
|
|
10415
|
+
// (CSRF, IDOR, TOCTOU, missing authorization) are deliberately still absent;
|
|
10416
|
+
// see KNOWN_GAPS.
|
|
10417
|
+
{
|
|
10418
|
+
id: "py-framework-debug-enabled",
|
|
10419
|
+
title: "debug mode enabled",
|
|
10420
|
+
consequence: "Framework debug mode serves an interactive traceback console on any error \u2014 arbitrary code execution to whoever triggers it \u2014 and leaks source and configuration.",
|
|
10421
|
+
cwe: "CWE-489",
|
|
10422
|
+
severity: "high",
|
|
10423
|
+
languages: ["python"],
|
|
10424
|
+
pattern: /\bDEBUG['"\]]*\s*[=:]\s*True\b|\.\s*run\s*\([^)\n]*\bdebug\s*=\s*True\b/,
|
|
10425
|
+
inherent: true
|
|
10426
|
+
},
|
|
10427
|
+
{
|
|
10428
|
+
id: "js-cors-wildcard-credentials",
|
|
10429
|
+
title: "wildcard CORS origin with credentials",
|
|
10430
|
+
consequence: "A `*` origin combined with credentials lets any site read authenticated responses on the victim\u2019s behalf: the browser attaches their cookies and hands the result to the attacker\u2019s page.",
|
|
10431
|
+
cwe: "CWE-942",
|
|
10432
|
+
severity: "high",
|
|
10433
|
+
languages: ["javascript", "typescript"],
|
|
10434
|
+
pattern: /origin\s*:\s*['"`]\*['"`][^\n]*credentials\s*:\s*true|credentials\s*:\s*true[^\n]*origin\s*:\s*['"`]\*['"`]/,
|
|
10435
|
+
inherent: true
|
|
10436
|
+
},
|
|
10437
|
+
{
|
|
10438
|
+
id: "js-cookie-insecure-flag",
|
|
10439
|
+
title: "cookie set with Secure disabled",
|
|
10440
|
+
consequence: "Without Secure the cookie travels over plain HTTP, where anyone on the path reads it. A session cookie set this way is a session anyone can lift.",
|
|
10441
|
+
cwe: "CWE-614",
|
|
10442
|
+
severity: "medium",
|
|
10443
|
+
languages: ["javascript", "typescript"],
|
|
10444
|
+
pattern: /\.\s*cookie\s*\([^\n]*\bsecure\s*:\s*false\b/i
|
|
10445
|
+
},
|
|
10446
|
+
{
|
|
10447
|
+
id: "js-hardcoded-crypto-key",
|
|
10448
|
+
title: "hardcoded key material",
|
|
10449
|
+
consequence: "A key committed to source is a key everyone with the repo has. The encryption it backs protects nothing once the source is shared, forked or leaked.",
|
|
10450
|
+
cwe: "CWE-321",
|
|
10451
|
+
severity: "high",
|
|
10452
|
+
languages: ["javascript", "typescript"],
|
|
10453
|
+
pattern: /\b(?:key|secret|iv|passphrase|salt|hmac)\w*\s*=\s*Buffer\s*\.\s*from\s*\(\s*['"`]/i,
|
|
10454
|
+
fileRequires: /\bcrypto\b|createCipheriv|createDecipheriv|createHmac/
|
|
10455
|
+
},
|
|
10456
|
+
{
|
|
10457
|
+
id: "py-hardcoded-secret-key",
|
|
10458
|
+
title: "hardcoded application secret",
|
|
10459
|
+
consequence: "A signing secret in source lets anyone with the code forge what it signs \u2014 session cookies, tokens, password-reset links.",
|
|
10460
|
+
cwe: "CWE-798",
|
|
10461
|
+
severity: "high",
|
|
10462
|
+
languages: ["python"],
|
|
10463
|
+
// Assigned a *string literal*. A value from a provider call (`os.environ`,
|
|
10464
|
+
// `secret_provider(...)`) is the correct shape and starts with an
|
|
10465
|
+
// identifier after the `=`, not a quote.
|
|
10466
|
+
pattern: /\b(?:SECRET_KEY|JWT_SECRET|SIGNING_KEY|SESSION_SECRET|PRIVATE_KEY)['"\]]*\s*[=:]\s*['"`][^'"`\n]{6,}/
|
|
10467
|
+
},
|
|
10468
|
+
{
|
|
10469
|
+
id: "py-ldap-injection",
|
|
10470
|
+
title: "LDAP filter built by interpolation",
|
|
10471
|
+
consequence: "Unescaped input in an LDAP filter lets an attacker rewrite the query \u2014 widening a match, bypassing a check, or enumerating the directory.",
|
|
10472
|
+
cwe: "CWE-90",
|
|
10473
|
+
severity: "high",
|
|
10474
|
+
languages: ["python"],
|
|
10475
|
+
// An f-string whose content is an LDAP filter (`(&…` / `(|…`) with an
|
|
10476
|
+
// interpolation. The safe form escapes, so the window carries an escaper.
|
|
10477
|
+
pattern: /\bf['"][^'"\n]*\([&|][^'"\n]*\{[^}\n]+\}/,
|
|
10478
|
+
guard: /escap/i
|
|
10479
|
+
},
|
|
10480
|
+
{
|
|
10481
|
+
id: "py-xpath-injection",
|
|
10482
|
+
title: "XPath built by interpolation",
|
|
10483
|
+
consequence: "Unescaped input in an XPath expression lets an attacker rewrite the query and read nodes it was meant to exclude.",
|
|
10484
|
+
cwe: "CWE-643",
|
|
10485
|
+
severity: "high",
|
|
10486
|
+
languages: ["python"],
|
|
10487
|
+
pattern: /\bf['"][^'"\n]*(?:\/\/|\/\w+\[)[^'"\n]*\{[^}\n]+\}/
|
|
10488
|
+
},
|
|
10489
|
+
{
|
|
10490
|
+
id: "py-fast-password-hash",
|
|
10491
|
+
title: "password hashed with a fast digest",
|
|
10492
|
+
consequence: "SHA-2 is built to be fast, which is exactly wrong for a password: a leaked hash is brute-forced at billions of guesses a second. Passwords need a slow, salted KDF (bcrypt, scrypt, argon2, PBKDF2).",
|
|
10493
|
+
cwe: "CWE-759",
|
|
10494
|
+
severity: "high",
|
|
10495
|
+
languages: ["python"],
|
|
10496
|
+
pattern: /\bhashlib\s*\.\s*(?:sha224|sha256|sha384|sha512)\s*\([^)\n]*(?:password|passwd|passphrase|pwd)/i,
|
|
10497
|
+
guard: /pbkdf2|scrypt|bcrypt|argon/i
|
|
10498
|
+
},
|
|
10499
|
+
{
|
|
10500
|
+
id: "py-plaintext-password-retained",
|
|
10501
|
+
title: "plaintext password stored",
|
|
10502
|
+
consequence: "A record that keeps the password itself, not a hash, turns one database leak into every user\u2019s credential \u2014 reused across every other site they log into.",
|
|
10503
|
+
cwe: "CWE-256",
|
|
10504
|
+
severity: "high",
|
|
10505
|
+
languages: ["python"],
|
|
10506
|
+
pattern: /['"]password['"]\s*:\s*(?:form|request|req|data|payload|body|params)\b/i
|
|
10507
|
+
},
|
|
10508
|
+
{
|
|
10509
|
+
id: "js-timing-unsafe-mac-compare",
|
|
10510
|
+
title: "MAC or signature compared with ==",
|
|
10511
|
+
consequence: "`===` on a signature returns the moment a byte differs, so response time leaks how much of a forged signature is correct \u2014 enough to recover a valid one byte by byte. Use `crypto.timingSafeEqual`.",
|
|
10512
|
+
cwe: "CWE-208",
|
|
10513
|
+
severity: "medium",
|
|
10514
|
+
languages: ["javascript", "typescript"],
|
|
10515
|
+
pattern: /\b\w*(?:[Ss]ignature|[Hh]mac|[Dd]igest)\s*===?\s*\w|\w\s*===?\s*\w*(?:[Ss]ignature|[Hh]mac|[Dd]igest)\b/,
|
|
10516
|
+
// Comparing `.length` is the *safe* preamble to a constant-time check, not
|
|
10517
|
+
// the timing-unsafe value comparison this rule is about — and the
|
|
10518
|
+
// `timingSafeEqual` that follows it sits forward of the line, out of the
|
|
10519
|
+
// backward guard window.
|
|
10520
|
+
lineGuard: /\.\s*length\b/,
|
|
10521
|
+
guard: /timingSafeEqual/
|
|
10522
|
+
},
|
|
10523
|
+
{
|
|
10524
|
+
id: "js-predictable-cipher-iv",
|
|
10525
|
+
title: "static initialization vector",
|
|
10526
|
+
consequence: "A fixed IV reused across encryptions leaks whether two plaintexts are equal and, in CBC/CTR, breaks confidentiality outright. The IV must be random per message.",
|
|
10527
|
+
cwe: "CWE-329",
|
|
10528
|
+
severity: "high",
|
|
10529
|
+
languages: ["javascript", "typescript"],
|
|
10530
|
+
pattern: /\b\w*[Ii][Vv]\s*=\s*Buffer\s*\.\s*(?:alloc|from)\s*\(/,
|
|
10531
|
+
fileRequires: /createCipheriv|\bcrypto\b/,
|
|
10532
|
+
guard: /randomBytes|randomFill/
|
|
10533
|
+
},
|
|
10534
|
+
// A rule for `Math.random().toString(36)` was tried and dropped: the exact
|
|
10535
|
+
// shape generates security tokens *and* benign callback/correlation ids
|
|
10536
|
+
// (Capacitor's native bridge uses it for the latter), with no line-visible
|
|
10537
|
+
// signal between them. The credential-scoped `insecure-randomness-for-secret`
|
|
10538
|
+
// above still catches the `token = …Math.random…` case; the bare shape is
|
|
10539
|
+
// left alone rather than flagged on every id generator.
|
|
10540
|
+
{
|
|
10541
|
+
id: "js-mass-assignment",
|
|
10542
|
+
title: "mass assignment from request data",
|
|
10543
|
+
consequence: "Copying the whole request body onto a record lets a caller set fields you never exposed \u2014 `isAdmin`, `role`, `balance` \u2014 because nothing stands between the input and the object.",
|
|
10544
|
+
cwe: "CWE-915",
|
|
10545
|
+
severity: "high",
|
|
10546
|
+
languages: ["javascript", "typescript"],
|
|
10547
|
+
pattern: /\bObject\s*\.\s*assign\s*\([^,\n]+,\s*(?:req|request|ctx)\s*\.\s*(?:body|query|params)\b/
|
|
10548
|
+
},
|
|
10549
|
+
{
|
|
10550
|
+
id: "js-header-injection",
|
|
10551
|
+
title: "response header set from request input",
|
|
10552
|
+
consequence: "A CR/LF in the value splits the response \u2014 the attacker injects headers or a whole second response (cache poisoning, a forged Set-Cookie).",
|
|
10553
|
+
cwe: "CWE-113",
|
|
10554
|
+
severity: "high",
|
|
10555
|
+
languages: ["javascript", "typescript"],
|
|
10556
|
+
pattern: /\.\s*(?:setHeader|header|set)\s*\(\s*['"`][^'"`\n]+['"`]\s*,\s*(?:req|request|ctx)\s*\.\s*(?:query|body|params|headers)\b/
|
|
10408
10557
|
}
|
|
10409
10558
|
];
|
|
10410
10559
|
var COMMENT_PREFIX = /^\s*(?:\/\/|\/\*|\*|#|--|<!--)/;
|