seaworthycode 1.2.19 → 1.2.21
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/index.js +536 -231
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -471,6 +471,9 @@ var CHECK_PRECEDENCE = {
|
|
|
471
471
|
"configuration.default-credentials": 1
|
|
472
472
|
};
|
|
473
473
|
var CROSS_DEDUPE_CHECK_IDS = new Set(Object.keys(CHECK_PRECEDENCE));
|
|
474
|
+
var SUPERSESSION_RULES = [
|
|
475
|
+
{ winner: "security.taint-xss", loser: "security.xss-surface" }
|
|
476
|
+
];
|
|
474
477
|
function crossCheckKey(finding) {
|
|
475
478
|
return `${finding.file ?? ""}:${finding.line ?? 0}`;
|
|
476
479
|
}
|
|
@@ -511,7 +514,23 @@ function dedupeCrossCheck(findings) {
|
|
|
511
514
|
}
|
|
512
515
|
mergedCrossFindings.push(winner);
|
|
513
516
|
}
|
|
514
|
-
|
|
517
|
+
let result = [...otherFindings, ...mergedCrossFindings];
|
|
518
|
+
if (SUPERSESSION_RULES.length > 0) {
|
|
519
|
+
const supersededKeys = /* @__PURE__ */ new Set();
|
|
520
|
+
for (const rule of SUPERSESSION_RULES) {
|
|
521
|
+
for (const f of result) {
|
|
522
|
+
if (f.checkId === rule.winner && f.file != null && f.line != null) {
|
|
523
|
+
supersededKeys.add(`${rule.loser}:${f.file}:${f.line}`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (supersededKeys.size > 0) {
|
|
528
|
+
result = result.filter(
|
|
529
|
+
(f) => f.file == null || f.line == null || !supersededKeys.has(`${f.checkId}:${f.file}:${f.line}`)
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return result;
|
|
515
534
|
}
|
|
516
535
|
var SEVERITY_ORDER = {
|
|
517
536
|
critical: 0,
|
|
@@ -641,6 +660,14 @@ function filterByMinConfidence(findings, minConfidence) {
|
|
|
641
660
|
if (!minConfidence) return findings;
|
|
642
661
|
return findings.filter((f) => meetsMinConfidence(f.confidence, minConfidence));
|
|
643
662
|
}
|
|
663
|
+
var SEVERITY_LEVELS = ["critical", "high", "medium", "low", "info"];
|
|
664
|
+
function meetsMinSeverity(findingSeverity, minSeverity) {
|
|
665
|
+
return SEVERITY_LEVELS.indexOf(findingSeverity) <= SEVERITY_LEVELS.indexOf(minSeverity);
|
|
666
|
+
}
|
|
667
|
+
function filterByMinSeverity(findings, minSeverity) {
|
|
668
|
+
if (!minSeverity) return findings;
|
|
669
|
+
return findings.filter((f) => meetsMinSeverity(f.severity, minSeverity));
|
|
670
|
+
}
|
|
644
671
|
var SITE_URL = process.env.SEAWORTHY_SITE_URL ?? "https://seaworthycode.com";
|
|
645
672
|
var whyCopy = {
|
|
646
673
|
"checks.no-document-write.why": "Cross-site scripting (XSS) allows attackers to inject malicious client-side scripts. This can lead to session hijacking, cookie theft, or unauthorized actions on behalf of users (CWE-79).",
|
|
@@ -1060,6 +1087,8 @@ var copy = {
|
|
|
1060
1087
|
"checks.verbose-errors.description": "Error responses include stack traces or internal details, leaking implementation information to clients.",
|
|
1061
1088
|
"checks.pii-in-responses.name": "PII in API responses",
|
|
1062
1089
|
"checks.pii-in-responses.description": "API responses include personally identifiable information without proper filtering.",
|
|
1090
|
+
"checks.pii-in-responses.message": (match) => `PII field detected in response model: ${match}`,
|
|
1091
|
+
"checks.pii-in-responses.degraded": (path, language) => `Could not fully analyse ${path} (${language}) for PII fields \u2014 install tree-sitter grammar for complete coverage.`,
|
|
1063
1092
|
"checks.no-response-filtering.name": "No response filtering",
|
|
1064
1093
|
"checks.no-response-filtering.description": "API responses return full database rows without filtering sensitive or unnecessary fields.",
|
|
1065
1094
|
"checks.missing-lockfile.name": "Missing lockfile",
|
|
@@ -1073,6 +1102,7 @@ var copy = {
|
|
|
1073
1102
|
// Remediation hints
|
|
1074
1103
|
"remediation.security.hardcoded-secrets": "Move this value to an environment variable and reference it via process.env.",
|
|
1075
1104
|
"remediation.security.dangerous-eval": "Avoid eval(), exec(), and new Function(). Use safer alternatives like JSON.parse for data parsing, or refactor to eliminate dynamic code execution.",
|
|
1105
|
+
"checks.committed-env.env-message": (commit, refs) => `A .env file (other than .env.example) is tracked in git history (commit ${commit.slice(0, 7)}${refs ? `, refs: ${refs}` : ""}). Rotate any exposed secrets immediately.`,
|
|
1076
1106
|
"remediation.security.committed-env": "Add .env to .gitignore. If the file is already tracked, use git rm --cached to untrack it without deleting, or rotate any exposed secrets.",
|
|
1077
1107
|
"remediation.security.auth-missing-endpoints": "Apply authentication middleware to this route handler. Use requireAuth, passport, JWT verification, or session-based auth.",
|
|
1078
1108
|
"remediation.security.cors-too-permissive": "Restrict Access-Control-Allow-Origin to your specific frontend domains rather than using a wildcard.",
|
|
@@ -1728,6 +1758,7 @@ var ScanRunner = class {
|
|
|
1728
1758
|
dedupeDegradedFindings(dedupeCrossCheck(dedupeFindings(results)))
|
|
1729
1759
|
);
|
|
1730
1760
|
findings = filterByMinConfidence(findings, options.minConfidence);
|
|
1761
|
+
findings = filterByMinSeverity(findings, options.minSeverity);
|
|
1731
1762
|
if (options.baseline) {
|
|
1732
1763
|
if (options.baseline === "save") {
|
|
1733
1764
|
const baselinePath = resolveBaselinePath(options.targetDir);
|
|
@@ -1772,6 +1803,39 @@ function pathsUnderTarget(topLevel, targetDir, repoRelativePath) {
|
|
|
1772
1803
|
function isInsideGitRepo(dir) {
|
|
1773
1804
|
return gitTopLevel(dir) !== null;
|
|
1774
1805
|
}
|
|
1806
|
+
function findFileInGitHistory(dir, pattern) {
|
|
1807
|
+
const topLevel = gitTopLevel(dir);
|
|
1808
|
+
if (!topLevel) {
|
|
1809
|
+
return { found: false };
|
|
1810
|
+
}
|
|
1811
|
+
try {
|
|
1812
|
+
const output = execSync(
|
|
1813
|
+
'git log --all --format="%H|%D" --name-only --diff-filter=A',
|
|
1814
|
+
{
|
|
1815
|
+
cwd: dir,
|
|
1816
|
+
stdio: "pipe",
|
|
1817
|
+
encoding: "utf-8",
|
|
1818
|
+
maxBuffer: 10 * 1024 * 1024
|
|
1819
|
+
}
|
|
1820
|
+
);
|
|
1821
|
+
let currentCommit = "";
|
|
1822
|
+
let currentRefs = "";
|
|
1823
|
+
for (const line of output.split(/\r?\n/)) {
|
|
1824
|
+
if (line.length >= 40 && line[40] === "|" && /^[0-9a-f]{40}$/.test(line.slice(0, 40))) {
|
|
1825
|
+
currentCommit = line.slice(0, 40);
|
|
1826
|
+
currentRefs = line.slice(41).trim();
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
if (!line.length) continue;
|
|
1830
|
+
if (pathsUnderTarget(topLevel, dir, line) && pattern.test(line)) {
|
|
1831
|
+
return { found: true, commit: currentCommit, refs: currentRefs };
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
return { found: false };
|
|
1835
|
+
} catch {
|
|
1836
|
+
return { found: false };
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1775
1839
|
function hasFileInGitHistory(dir, pattern) {
|
|
1776
1840
|
const topLevel = gitTopLevel(dir);
|
|
1777
1841
|
if (!topLevel) {
|
|
@@ -4351,6 +4415,13 @@ function lineMatch(line, lineIndex, pattern) {
|
|
|
4351
4415
|
function hasShellValidation(content) {
|
|
4352
4416
|
return /\[\[\s*"?\$\d+"?\s*=~\s*\^/.test(content) || /\bcase\s+(?:"?\$\d+"?|"?\$\{[1-9][0-9]*(?::-[^}]*)?\}"?)\s+in\b/.test(content) || /\[\[\s+-[nz]\s+"?\$\d+"?\s*\]\]/.test(content);
|
|
4353
4417
|
}
|
|
4418
|
+
function isInsideSingleQuotes(line, charIndex) {
|
|
4419
|
+
let inside = false;
|
|
4420
|
+
for (let i = 0; i < charIndex; i++) {
|
|
4421
|
+
if (line[i] === "'") inside = !inside;
|
|
4422
|
+
}
|
|
4423
|
+
return inside;
|
|
4424
|
+
}
|
|
4354
4425
|
function shellLineHasTimeout(line) {
|
|
4355
4426
|
return /^\s*timeout\s+\S+/.test(line) || /(?:^|\s)(?:--max-time|--connect-timeout|--timeout|-m)(?:\s|=|$)/.test(line);
|
|
4356
4427
|
}
|
|
@@ -4438,23 +4509,24 @@ registry.register({
|
|
|
4438
4509
|
severity: "high",
|
|
4439
4510
|
minTier: "free",
|
|
4440
4511
|
async run(ctx) {
|
|
4441
|
-
const
|
|
4512
|
+
const envMatch = findFileInGitHistory(
|
|
4442
4513
|
ctx.targetDir,
|
|
4443
4514
|
/(?:^|\/)\.env(?:$|\.(?!example$).+)/
|
|
4444
4515
|
);
|
|
4445
4516
|
const hasSettings = await hasCommittedSettingsWithSecrets(ctx.targetDir);
|
|
4446
4517
|
const hasJavaConfig = await hasCommittedJavaConfigWithSecrets(ctx.targetDir);
|
|
4447
|
-
if (!
|
|
4518
|
+
if (!envMatch.found && !hasSettings && !hasJavaConfig) return [];
|
|
4448
4519
|
const findings = [];
|
|
4449
|
-
if (
|
|
4520
|
+
if (envMatch.found) {
|
|
4450
4521
|
findings.push({
|
|
4451
4522
|
id: "committed-env-1",
|
|
4452
4523
|
checkId: "security.committed-env",
|
|
4453
4524
|
category: "security",
|
|
4454
4525
|
severity: "high",
|
|
4455
|
-
message: "
|
|
4526
|
+
message: getCopy("checks.committed-env.env-message", envMatch.commit, envMatch.refs),
|
|
4456
4527
|
confidence: "high",
|
|
4457
|
-
remediation: getCopy("remediation.security.committed-env")
|
|
4528
|
+
remediation: getCopy("remediation.security.committed-env"),
|
|
4529
|
+
properties: { commit: envMatch.commit, refs: envMatch.refs }
|
|
4458
4530
|
});
|
|
4459
4531
|
}
|
|
4460
4532
|
if (hasSettings) {
|
|
@@ -4479,7 +4551,7 @@ registry.register({
|
|
|
4479
4551
|
remediation: getCopy("remediation.security.committed-env")
|
|
4480
4552
|
});
|
|
4481
4553
|
}
|
|
4482
|
-
if (
|
|
4554
|
+
if (envMatch.found) {
|
|
4483
4555
|
const sourceFindings = await scanBashLines(ctx, {
|
|
4484
4556
|
checkId: "security.committed-env",
|
|
4485
4557
|
category: "security",
|
|
@@ -5094,7 +5166,7 @@ registry.register({
|
|
|
5094
5166
|
}
|
|
5095
5167
|
if (lockfiles.length === 0) {
|
|
5096
5168
|
try {
|
|
5097
|
-
const output = execSync2("npm audit --json", {
|
|
5169
|
+
const output = execSync2("npm audit --json --omit=dev", {
|
|
5098
5170
|
cwd: ctx.targetDir,
|
|
5099
5171
|
stdio: "pipe",
|
|
5100
5172
|
encoding: "utf-8",
|
|
@@ -5117,7 +5189,7 @@ registry.register({
|
|
|
5117
5189
|
for (const lockfile of lockfiles) {
|
|
5118
5190
|
const lockfileDir = join8(ctx.targetDir, dirname2(lockfile));
|
|
5119
5191
|
try {
|
|
5120
|
-
const output = execSync2("npm audit --json", {
|
|
5192
|
+
const output = execSync2("npm audit --json --omit=dev", {
|
|
5121
5193
|
cwd: lockfileDir,
|
|
5122
5194
|
stdio: "pipe",
|
|
5123
5195
|
encoding: "utf-8",
|
|
@@ -6204,6 +6276,10 @@ async function runMechanicalCheck(ctx, template) {
|
|
|
6204
6276
|
}
|
|
6205
6277
|
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
6206
6278
|
const line = lines[lineNum];
|
|
6279
|
+
if (pattern.skipCommentLines) {
|
|
6280
|
+
const trimmed = line.trimStart();
|
|
6281
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*")) continue;
|
|
6282
|
+
}
|
|
6207
6283
|
if (pattern.regex.test(line)) {
|
|
6208
6284
|
results.push(
|
|
6209
6285
|
makeMechanicalFinding(template, pattern, file.relativePath, lineNum + 1)
|
|
@@ -6970,7 +7046,10 @@ async function scanBashNoInputValidation(ctx) {
|
|
|
6970
7046
|
remediation: getCopy("remediation.resilience.no-input-validation")
|
|
6971
7047
|
}, (line, content, index) => {
|
|
6972
7048
|
if (hasShellValidation(content)) return null;
|
|
6973
|
-
|
|
7049
|
+
const match = lineMatch(line, index, /\$\d\b|\$\{[1-9][0-9]*\}/);
|
|
7050
|
+
if (!match) return null;
|
|
7051
|
+
if (isInsideSingleQuotes(line, match.column - 1)) return null;
|
|
7052
|
+
return match;
|
|
6974
7053
|
}).then((findings) => findings.slice(0, 1));
|
|
6975
7054
|
}
|
|
6976
7055
|
registry.register({
|
|
@@ -7097,12 +7176,13 @@ registry.register({
|
|
|
7097
7176
|
return runLLMCheck(ctx, promptTemplate8);
|
|
7098
7177
|
}
|
|
7099
7178
|
});
|
|
7100
|
-
var PROMPT_VERSION9 = "1.
|
|
7179
|
+
var PROMPT_VERSION9 = "1.3.0";
|
|
7101
7180
|
var fallbackPatterns9 = [
|
|
7102
7181
|
{
|
|
7103
7182
|
regex: /(?:fetch|axios\.(?:get|post|put|delete|patch|request)|requests\.(?:get|post|put|patch|delete|head)|aiohttp\.\w*[Ss]ession|httpx\.(?:get|post|put|patch|delete|Client))\s*\(/i,
|
|
7104
7183
|
message: "Outbound HTTP call found \u2014 verify timeout is configured",
|
|
7105
|
-
severity: "medium"
|
|
7184
|
+
severity: "medium",
|
|
7185
|
+
skipCommentLines: true
|
|
7106
7186
|
}
|
|
7107
7187
|
];
|
|
7108
7188
|
async function buildPrompt9(sourceFiles) {
|
|
@@ -7376,12 +7456,13 @@ registry.register({
|
|
|
7376
7456
|
return runLLMCheck(ctx, promptTemplate12);
|
|
7377
7457
|
}
|
|
7378
7458
|
});
|
|
7379
|
-
var PROMPT_VERSION13 = "1.
|
|
7459
|
+
var PROMPT_VERSION13 = "1.2.0";
|
|
7380
7460
|
var fallbackPatterns13 = [
|
|
7381
7461
|
{
|
|
7382
7462
|
regex: /console\.(?:log|error|warn)\b/i,
|
|
7383
7463
|
message: "Console logging used \u2014 structured logging recommended",
|
|
7384
|
-
severity: "low"
|
|
7464
|
+
severity: "low",
|
|
7465
|
+
skipCommentLines: true
|
|
7385
7466
|
}
|
|
7386
7467
|
];
|
|
7387
7468
|
async function buildPrompt13(sourceFiles) {
|
|
@@ -7518,16 +7599,18 @@ registry.register({
|
|
|
7518
7599
|
return runLLMCheck(ctx, promptTemplate15);
|
|
7519
7600
|
}
|
|
7520
7601
|
});
|
|
7521
|
-
var PROMPT_VERSION16 = "1.
|
|
7602
|
+
var PROMPT_VERSION16 = "1.1.0";
|
|
7522
7603
|
var fallbackPatterns16 = [
|
|
7523
7604
|
{
|
|
7524
|
-
regex: /(?:admin:admin|
|
|
7605
|
+
regex: /(?:admin:admin|\b\w*(?:password|passwd|pwd|pass|secret|token|credential)\w*\s*[:=]\s*['"`](?:admin|password|123456|changeme|letmein|secret|test|postgres|root|default|1234|12345|qwerty|pass|guest)['"`])/i,
|
|
7525
7606
|
message: "Default credentials possibly in use",
|
|
7526
7607
|
severity: "high"
|
|
7527
7608
|
}
|
|
7528
7609
|
];
|
|
7529
7610
|
async function buildPrompt16(sourceFiles) {
|
|
7530
|
-
const parts = [
|
|
7611
|
+
const parts = [
|
|
7612
|
+
"Check if default credentials are unchanged. Look for default usernames/passwords (admin/admin, root/root, guest/guest), placeholder credentials (changeme, password), or default connection strings that have not been customised.\n\nDo NOT flag: (1) programming language control-flow keywords such as switch/case/default in Go, JavaScript, Java, Rust, or PHP \u2014 these are not credentials. (2) Comments that describe or explain a default value in prose \u2014 the comment itself is not a credential assignment. Only flag actual credential assignments where a variable whose name suggests it holds a password, token, or secret is set to a known weak literal value."
|
|
7613
|
+
];
|
|
7531
7614
|
for (const file of sourceFiles) {
|
|
7532
7615
|
const content = await file.content();
|
|
7533
7616
|
parts.push(`--- ${file.relativePath} ---
|
|
@@ -7574,7 +7657,13 @@ function isDefaultCredential(password2) {
|
|
|
7574
7657
|
"secret",
|
|
7575
7658
|
"test",
|
|
7576
7659
|
"postgres",
|
|
7577
|
-
"root"
|
|
7660
|
+
"root",
|
|
7661
|
+
"default",
|
|
7662
|
+
"pass",
|
|
7663
|
+
"1234",
|
|
7664
|
+
"12345",
|
|
7665
|
+
"qwerty",
|
|
7666
|
+
"guest"
|
|
7578
7667
|
]);
|
|
7579
7668
|
return defaults.has(normalized);
|
|
7580
7669
|
}
|
|
@@ -7706,6 +7795,115 @@ async function scanBashDefaultCredentials(ctx) {
|
|
|
7706
7795
|
return lineMatch(line, index, /(?:^|\s)(?:export\s+)?[A-Z0-9_]*(?:PASS|PASSWORD|PWD|SECRET|TOKEN)[A-Z0-9_]*=(["'])([^"']{1,64})\1/i);
|
|
7707
7796
|
});
|
|
7708
7797
|
}
|
|
7798
|
+
var CREDENTIAL_NAME_REGEX = /(?:password|passwd|pwd|pass|secret|token|credential|cred)/i;
|
|
7799
|
+
var DEFAULT_CRED_TS_QUERIES = {
|
|
7800
|
+
javascript: `
|
|
7801
|
+
(lexical_declaration
|
|
7802
|
+
(variable_declarator
|
|
7803
|
+
name: (identifier) @name
|
|
7804
|
+
value: (string) @value)) @match
|
|
7805
|
+
|
|
7806
|
+
(assignment_expression
|
|
7807
|
+
left: (identifier) @name
|
|
7808
|
+
right: (string) @value) @match
|
|
7809
|
+
|
|
7810
|
+
(pair
|
|
7811
|
+
key: (property_identifier) @name
|
|
7812
|
+
value: (string) @value) @match
|
|
7813
|
+
`,
|
|
7814
|
+
typescript: `
|
|
7815
|
+
(lexical_declaration
|
|
7816
|
+
(variable_declarator
|
|
7817
|
+
name: (identifier) @name
|
|
7818
|
+
value: (string) @value)) @match
|
|
7819
|
+
|
|
7820
|
+
(assignment_expression
|
|
7821
|
+
left: (identifier) @name
|
|
7822
|
+
right: (string) @value) @match
|
|
7823
|
+
|
|
7824
|
+
(pair
|
|
7825
|
+
key: (property_identifier) @name
|
|
7826
|
+
value: (string) @value) @match
|
|
7827
|
+
`,
|
|
7828
|
+
tsx: `
|
|
7829
|
+
(lexical_declaration
|
|
7830
|
+
(variable_declarator
|
|
7831
|
+
name: (identifier) @name
|
|
7832
|
+
value: (string) @value)) @match
|
|
7833
|
+
|
|
7834
|
+
(assignment_expression
|
|
7835
|
+
left: (identifier) @name
|
|
7836
|
+
right: (string) @value) @match
|
|
7837
|
+
|
|
7838
|
+
(pair
|
|
7839
|
+
key: (property_identifier) @name
|
|
7840
|
+
value: (string) @value) @match
|
|
7841
|
+
`,
|
|
7842
|
+
go: `
|
|
7843
|
+
(short_var_declaration
|
|
7844
|
+
left: (expression_list (identifier) @name)
|
|
7845
|
+
right: (expression_list (_) @value)) @match
|
|
7846
|
+
|
|
7847
|
+
(var_declaration
|
|
7848
|
+
(var_spec
|
|
7849
|
+
name: (identifier) @name
|
|
7850
|
+
value: (_) @value)) @match
|
|
7851
|
+
`,
|
|
7852
|
+
python: `
|
|
7853
|
+
(assignment
|
|
7854
|
+
left: (identifier) @name
|
|
7855
|
+
right: (string) @value) @match
|
|
7856
|
+
`,
|
|
7857
|
+
ruby: `
|
|
7858
|
+
(assignment left: (identifier) @name right: (string) @value) @match
|
|
7859
|
+
|
|
7860
|
+
(pair key: (_) @name value: (string) @value) @match
|
|
7861
|
+
`,
|
|
7862
|
+
java: `
|
|
7863
|
+
(local_variable_declaration
|
|
7864
|
+
declarator: (variable_declarator
|
|
7865
|
+
name: (identifier) @name
|
|
7866
|
+
value: (string_literal) @value)) @match
|
|
7867
|
+
|
|
7868
|
+
(field_declaration
|
|
7869
|
+
declarator: (variable_declarator
|
|
7870
|
+
name: (identifier) @name
|
|
7871
|
+
value: (string_literal) @value)) @match
|
|
7872
|
+
`,
|
|
7873
|
+
rust: `
|
|
7874
|
+
(let_declaration
|
|
7875
|
+
pattern: (identifier) @name
|
|
7876
|
+
value: (string_literal) @value) @match
|
|
7877
|
+
`
|
|
7878
|
+
};
|
|
7879
|
+
var astCheck = createTreeSitterCheck({
|
|
7880
|
+
id: CHECK_ID8,
|
|
7881
|
+
nameKey: "checks.default-credentials.name",
|
|
7882
|
+
descriptionKey: "checks.default-credentials.description",
|
|
7883
|
+
category: "configuration",
|
|
7884
|
+
severity: "high",
|
|
7885
|
+
minTier: "pro",
|
|
7886
|
+
queries: DEFAULT_CRED_TS_QUERIES,
|
|
7887
|
+
messageKey: "checks.default-credentials.message",
|
|
7888
|
+
degradedMessageKey: "checks.default-credentials.degraded",
|
|
7889
|
+
remediationKey: "remediation.configuration.default-credentials",
|
|
7890
|
+
fileFilter: (file) => !isMechanicalCheckExcludedPath(file.relativePath),
|
|
7891
|
+
matchFilter: (captures) => {
|
|
7892
|
+
const name = captures.name ?? "";
|
|
7893
|
+
const value = captures.value ?? "";
|
|
7894
|
+
if (!CREDENTIAL_NAME_REGEX.test(name)) return false;
|
|
7895
|
+
const trimmed = value.trim();
|
|
7896
|
+
if (!/^['"`].*['"`]$/.test(trimmed)) return false;
|
|
7897
|
+
const cleanValue = trimmed.replace(/^['"`]|['"`]$/g, "");
|
|
7898
|
+
if (looksLikeHash(cleanValue)) return false;
|
|
7899
|
+
return isDefaultCredential(cleanValue);
|
|
7900
|
+
},
|
|
7901
|
+
messageFactory: (captures) => {
|
|
7902
|
+
const name = captures.name ?? "unknown";
|
|
7903
|
+
const cleanValue = (captures.value ?? "").trim().replace(/^['"`]|['"`]$/g, "");
|
|
7904
|
+
return getCopy("checks.default-credentials.message", `${name} = "${cleanValue}"`);
|
|
7905
|
+
}
|
|
7906
|
+
});
|
|
7709
7907
|
var sqlCheck = {
|
|
7710
7908
|
id: CHECK_ID8,
|
|
7711
7909
|
name: getCopy("checks.default-credentials.name"),
|
|
@@ -7714,12 +7912,14 @@ var sqlCheck = {
|
|
|
7714
7912
|
severity: "high",
|
|
7715
7913
|
minTier: "pro",
|
|
7716
7914
|
async run(ctx) {
|
|
7717
|
-
const [sqlFindings, bashFindings, llmFindings] = await Promise.all([
|
|
7915
|
+
const [sqlFindings, bashFindings, llmFindings, astFindings] = await Promise.all([
|
|
7718
7916
|
scanSqlDefaultCredentials(ctx),
|
|
7719
7917
|
scanBashDefaultCredentials(ctx),
|
|
7720
|
-
runLLMCheck(ctx, promptTemplate16)
|
|
7918
|
+
runLLMCheck(ctx, promptTemplate16),
|
|
7919
|
+
astCheck.run(ctx)
|
|
7721
7920
|
]);
|
|
7722
|
-
|
|
7921
|
+
const nonAstFindings = [...sqlFindings, ...bashFindings, ...llmFindings];
|
|
7922
|
+
return mergeFindings(astFindings, nonAstFindings);
|
|
7723
7923
|
}
|
|
7724
7924
|
};
|
|
7725
7925
|
registry.register(sqlCheck);
|
|
@@ -7781,16 +7981,17 @@ registry.register({
|
|
|
7781
7981
|
return [...bashFindings, ...llmFindings];
|
|
7782
7982
|
}
|
|
7783
7983
|
});
|
|
7784
|
-
var PROMPT_VERSION18 = "1.
|
|
7984
|
+
var PROMPT_VERSION18 = "1.1.0";
|
|
7785
7985
|
var fallbackPatterns18 = [
|
|
7786
7986
|
{
|
|
7787
7987
|
regex: /\b(?:ssn|social_security|date_of_birth|dob|passport|national_id)\b/i,
|
|
7788
7988
|
message: "Potentially sensitive field name found \u2014 verify it is not exposed in API responses",
|
|
7789
|
-
severity: "high"
|
|
7989
|
+
severity: "high",
|
|
7990
|
+
skipCommentLines: true
|
|
7790
7991
|
}
|
|
7791
7992
|
];
|
|
7792
7993
|
async function buildPrompt18(sourceFiles) {
|
|
7793
|
-
const parts = ["Check if PII or sensitive fields are included in API responses. Look for response serialization that may include SSN, date of birth, passport numbers, or other personally identifiable information that should be filtered. In Python, flag Django REST Framework serializers or FastAPI response models that include PII fields without exclude/exclude_unset configuration."];
|
|
7994
|
+
const parts = ["Check if PII or sensitive fields are included in API responses. Look for response serialization that may include SSN, date of birth, passport numbers, or other personally identifiable information that should be filtered. In Python, flag Django REST Framework serializers or FastAPI response models that include PII fields without exclude/exclude_unset configuration.\n\nDo NOT flag field names that appear only in comments, test fixtures, or documentation strings. Only flag fields that are directly included in serialised API responses or response model definitions."];
|
|
7794
7995
|
for (const file of sourceFiles) {
|
|
7795
7996
|
const content = await file.content();
|
|
7796
7997
|
parts.push(`--- ${file.relativePath} ---
|
|
@@ -7808,15 +8009,63 @@ var promptTemplate18 = {
|
|
|
7808
8009
|
parseResponse: parseResponse18,
|
|
7809
8010
|
fallbackPatterns: fallbackPatterns18
|
|
7810
8011
|
};
|
|
8012
|
+
var CHECK_ID9 = "data-exposure.pii-in-responses";
|
|
8013
|
+
var PII_FIELD_REGEX = /\b(?:ssn|social.?security|date.?of.?birth|dob|passport|national.?id)\b/i;
|
|
8014
|
+
var PII_TS_QUERIES = {
|
|
8015
|
+
typescript: `
|
|
8016
|
+
(property_signature name: (property_identifier) @name) @match
|
|
8017
|
+
(pair key: (property_identifier) @name) @match
|
|
8018
|
+
(public_field_definition name: (property_identifier) @name) @match
|
|
8019
|
+
`,
|
|
8020
|
+
javascript: `
|
|
8021
|
+
(pair key: (property_identifier) @name) @match
|
|
8022
|
+
(method_definition name: (property_identifier) @name) @match
|
|
8023
|
+
`,
|
|
8024
|
+
tsx: `
|
|
8025
|
+
(property_signature name: (property_identifier) @name) @match
|
|
8026
|
+
(pair key: (property_identifier) @name) @match
|
|
8027
|
+
`,
|
|
8028
|
+
go: `
|
|
8029
|
+
(field_declaration name: (field_identifier) @name) @match
|
|
8030
|
+
`,
|
|
8031
|
+
python: `
|
|
8032
|
+
(assignment left: (identifier) @name) @match
|
|
8033
|
+
`,
|
|
8034
|
+
ruby: `
|
|
8035
|
+
(assignment left: (identifier) @name right: (_)) @match
|
|
8036
|
+
`,
|
|
8037
|
+
java: `
|
|
8038
|
+
(field_declaration declarator: (variable_declarator name: (identifier) @name)) @match
|
|
8039
|
+
`
|
|
8040
|
+
};
|
|
8041
|
+
var piiAstCheck = createTreeSitterCheck({
|
|
8042
|
+
id: CHECK_ID9,
|
|
8043
|
+
nameKey: "checks.pii-in-responses.name",
|
|
8044
|
+
descriptionKey: "checks.pii-in-responses.description",
|
|
8045
|
+
category: "data-exposure",
|
|
8046
|
+
severity: "high",
|
|
8047
|
+
minTier: "pro",
|
|
8048
|
+
queries: PII_TS_QUERIES,
|
|
8049
|
+
messageKey: "checks.pii-in-responses.message",
|
|
8050
|
+
degradedMessageKey: "checks.pii-in-responses.degraded",
|
|
8051
|
+
remediationKey: "remediation.data-exposure.pii-in-responses",
|
|
8052
|
+
fileFilter: (file) => !isMechanicalCheckExcludedPath(file.relativePath),
|
|
8053
|
+
matchFilter: (captures) => PII_FIELD_REGEX.test(captures.name ?? ""),
|
|
8054
|
+
messageFactory: (captures) => getCopy("checks.pii-in-responses.message", captures.name ?? "unknown")
|
|
8055
|
+
});
|
|
7811
8056
|
registry.register({
|
|
7812
|
-
id:
|
|
7813
|
-
name: "
|
|
7814
|
-
description: "
|
|
8057
|
+
id: CHECK_ID9,
|
|
8058
|
+
name: getCopy("checks.pii-in-responses.name"),
|
|
8059
|
+
description: getCopy("checks.pii-in-responses.description"),
|
|
7815
8060
|
category: "data-exposure",
|
|
7816
8061
|
severity: "high",
|
|
7817
8062
|
minTier: "pro",
|
|
7818
8063
|
async run(ctx) {
|
|
7819
|
-
|
|
8064
|
+
const [llmFindings, astFindings] = await Promise.all([
|
|
8065
|
+
runLLMCheck(ctx, promptTemplate18),
|
|
8066
|
+
piiAstCheck.run(ctx)
|
|
8067
|
+
]);
|
|
8068
|
+
return mergeFindings(astFindings, llmFindings);
|
|
7820
8069
|
}
|
|
7821
8070
|
});
|
|
7822
8071
|
var PROMPT_VERSION19 = "1.0.0";
|
|
@@ -7862,7 +8111,7 @@ registry.register({
|
|
|
7862
8111
|
return runLLMCheck(ctx, promptTemplate19);
|
|
7863
8112
|
}
|
|
7864
8113
|
});
|
|
7865
|
-
var
|
|
8114
|
+
var CHECK_ID10 = "data-exposure.html-form-pii-exposure";
|
|
7866
8115
|
var REMEDIATION12 = getCopy("remediation.data-exposure.html-form-pii-exposure");
|
|
7867
8116
|
var INPUT_PATTERN = /<input\b[^>]*?>/gi;
|
|
7868
8117
|
var AUTOCOMPLETE_PATTERN = /\bautocomplete\s*=\s*["']([^"']+)["']/i;
|
|
@@ -7889,8 +8138,8 @@ function lineAndColumn4(content, index) {
|
|
|
7889
8138
|
function pushFinding2(findings, filePath, content, index, matchText) {
|
|
7890
8139
|
const { line, column } = lineAndColumn4(content, index);
|
|
7891
8140
|
findings.push({
|
|
7892
|
-
id: `${
|
|
7893
|
-
checkId:
|
|
8141
|
+
id: `${CHECK_ID10}:${filePath}:${line}:${column}`,
|
|
8142
|
+
checkId: CHECK_ID10,
|
|
7894
8143
|
category: "data-exposure",
|
|
7895
8144
|
severity: "medium",
|
|
7896
8145
|
message: getCopy("checks.html-form-pii-exposure.message", matchText),
|
|
@@ -7916,7 +8165,7 @@ function hasSensitiveField(tagText) {
|
|
|
7916
8165
|
return SENSITIVE_TYPES.test(type.trim()) || SENSITIVE_NAME.test(name);
|
|
7917
8166
|
}
|
|
7918
8167
|
registry.register({
|
|
7919
|
-
id:
|
|
8168
|
+
id: CHECK_ID10,
|
|
7920
8169
|
name: getCopy("checks.html-form-pii-exposure.name"),
|
|
7921
8170
|
description: getCopy("checks.html-form-pii-exposure.description"),
|
|
7922
8171
|
category: "data-exposure",
|
|
@@ -7945,7 +8194,7 @@ registry.register({
|
|
|
7945
8194
|
return findings;
|
|
7946
8195
|
}
|
|
7947
8196
|
});
|
|
7948
|
-
var
|
|
8197
|
+
var CHECK_ID11 = "security.xss-surface";
|
|
7949
8198
|
var CGI_PATH_HINT = new RegExp("(^|[/_-])cgi([/_.-]|$)", "i");
|
|
7950
8199
|
function isLikelyCgiBashScript(file) {
|
|
7951
8200
|
return CGI_PATH_HINT.test(file.relativePath);
|
|
@@ -8224,7 +8473,7 @@ var QUERIES = {
|
|
|
8224
8473
|
};
|
|
8225
8474
|
registry.register(
|
|
8226
8475
|
createTreeSitterCheck({
|
|
8227
|
-
id:
|
|
8476
|
+
id: CHECK_ID11,
|
|
8228
8477
|
nameKey: "checks.xss-surface.name",
|
|
8229
8478
|
descriptionKey: "checks.xss-surface.description",
|
|
8230
8479
|
category: "security",
|
|
@@ -8239,7 +8488,7 @@ registry.register(
|
|
|
8239
8488
|
fileFilter: (file) => file.language !== "bash" || isLikelyCgiBashScript(file)
|
|
8240
8489
|
})
|
|
8241
8490
|
);
|
|
8242
|
-
var
|
|
8491
|
+
var CHECK_ID12 = "security.os-command-injection";
|
|
8243
8492
|
function isStaticShellLikeLiteral(text22) {
|
|
8244
8493
|
const t = text22.trim();
|
|
8245
8494
|
if (t.length < 2) return false;
|
|
@@ -8369,7 +8618,7 @@ var QUERIES2 = {
|
|
|
8369
8618
|
};
|
|
8370
8619
|
registry.register(
|
|
8371
8620
|
createTreeSitterCheck({
|
|
8372
|
-
id:
|
|
8621
|
+
id: CHECK_ID12,
|
|
8373
8622
|
nameKey: "checks.os-command-injection.name",
|
|
8374
8623
|
descriptionKey: "checks.os-command-injection.description",
|
|
8375
8624
|
category: "security",
|
|
@@ -8386,7 +8635,7 @@ registry.register(
|
|
|
8386
8635
|
}
|
|
8387
8636
|
})
|
|
8388
8637
|
);
|
|
8389
|
-
var
|
|
8638
|
+
var CHECK_ID13 = "security.shell-arbitrary-evaluation";
|
|
8390
8639
|
var QUERY = `
|
|
8391
8640
|
(command
|
|
8392
8641
|
name: (command_name (word) @cmd)
|
|
@@ -8400,7 +8649,7 @@ var QUERY = `
|
|
|
8400
8649
|
`;
|
|
8401
8650
|
registry.register(
|
|
8402
8651
|
createTreeSitterCheck({
|
|
8403
|
-
id:
|
|
8652
|
+
id: CHECK_ID13,
|
|
8404
8653
|
nameKey: "checks.shell-arbitrary-evaluation.name",
|
|
8405
8654
|
descriptionKey: "checks.shell-arbitrary-evaluation.description",
|
|
8406
8655
|
category: "security",
|
|
@@ -8412,7 +8661,7 @@ registry.register(
|
|
|
8412
8661
|
remediationKey: "remediation.security.shell-arbitrary-evaluation"
|
|
8413
8662
|
})
|
|
8414
8663
|
);
|
|
8415
|
-
var
|
|
8664
|
+
var CHECK_ID14 = "security.shell-unescaped-path-deletion";
|
|
8416
8665
|
var QUERY2 = `
|
|
8417
8666
|
(command
|
|
8418
8667
|
name: (command_name (word) @cmd)
|
|
@@ -8442,7 +8691,7 @@ var QUERY2 = `
|
|
|
8442
8691
|
`;
|
|
8443
8692
|
registry.register(
|
|
8444
8693
|
createTreeSitterCheck({
|
|
8445
|
-
id:
|
|
8694
|
+
id: CHECK_ID14,
|
|
8446
8695
|
nameKey: "checks.shell-unescaped-path-deletion.name",
|
|
8447
8696
|
descriptionKey: "checks.shell-unescaped-path-deletion.description",
|
|
8448
8697
|
category: "security",
|
|
@@ -8454,7 +8703,7 @@ registry.register(
|
|
|
8454
8703
|
remediationKey: "remediation.security.shell-unescaped-path-deletion"
|
|
8455
8704
|
})
|
|
8456
8705
|
);
|
|
8457
|
-
var
|
|
8706
|
+
var CHECK_ID15 = "security.file-inclusion";
|
|
8458
8707
|
var QUERIES3 = {
|
|
8459
8708
|
php: `
|
|
8460
8709
|
(include_expression
|
|
@@ -8484,7 +8733,7 @@ var QUERIES3 = {
|
|
|
8484
8733
|
};
|
|
8485
8734
|
registry.register(
|
|
8486
8735
|
createTreeSitterCheck({
|
|
8487
|
-
id:
|
|
8736
|
+
id: CHECK_ID15,
|
|
8488
8737
|
nameKey: "checks.file-inclusion.name",
|
|
8489
8738
|
descriptionKey: "checks.file-inclusion.description",
|
|
8490
8739
|
category: "security",
|
|
@@ -8496,7 +8745,7 @@ registry.register(
|
|
|
8496
8745
|
remediationKey: "remediation.security.file-inclusion"
|
|
8497
8746
|
})
|
|
8498
8747
|
);
|
|
8499
|
-
var
|
|
8748
|
+
var CHECK_ID16 = "security.open-redirect";
|
|
8500
8749
|
var QUERIES4 = {
|
|
8501
8750
|
php: `
|
|
8502
8751
|
(function_call_expression
|
|
@@ -8666,7 +8915,7 @@ var QUERIES4 = {
|
|
|
8666
8915
|
};
|
|
8667
8916
|
registry.register(
|
|
8668
8917
|
createTreeSitterCheck({
|
|
8669
|
-
id:
|
|
8918
|
+
id: CHECK_ID16,
|
|
8670
8919
|
nameKey: "checks.open-redirect.name",
|
|
8671
8920
|
descriptionKey: "checks.open-redirect.description",
|
|
8672
8921
|
category: "security",
|
|
@@ -8678,7 +8927,7 @@ registry.register(
|
|
|
8678
8927
|
remediationKey: "remediation.security.open-redirect"
|
|
8679
8928
|
})
|
|
8680
8929
|
);
|
|
8681
|
-
var
|
|
8930
|
+
var CHECK_ID17 = "security.unsafe-file-upload";
|
|
8682
8931
|
var QUERIES5 = {
|
|
8683
8932
|
javascript: `
|
|
8684
8933
|
(call_expression
|
|
@@ -8850,7 +9099,7 @@ var QUERIES5 = {
|
|
|
8850
9099
|
};
|
|
8851
9100
|
registry.register(
|
|
8852
9101
|
createTreeSitterCheck({
|
|
8853
|
-
id:
|
|
9102
|
+
id: CHECK_ID17,
|
|
8854
9103
|
nameKey: "checks.unsafe-file-upload.name",
|
|
8855
9104
|
descriptionKey: "checks.unsafe-file-upload.description",
|
|
8856
9105
|
category: "security",
|
|
@@ -8866,7 +9115,7 @@ registry.register(
|
|
|
8866
9115
|
}
|
|
8867
9116
|
})
|
|
8868
9117
|
);
|
|
8869
|
-
var
|
|
9118
|
+
var CHECK_ID18 = "security.unsafe-deserialization";
|
|
8870
9119
|
var QUERIES6 = {
|
|
8871
9120
|
javascript: `
|
|
8872
9121
|
(call_expression
|
|
@@ -9003,7 +9252,7 @@ var QUERIES6 = {
|
|
|
9003
9252
|
};
|
|
9004
9253
|
registry.register(
|
|
9005
9254
|
createTreeSitterCheck({
|
|
9006
|
-
id:
|
|
9255
|
+
id: CHECK_ID18,
|
|
9007
9256
|
nameKey: "checks.unsafe-deserialization.name",
|
|
9008
9257
|
descriptionKey: "checks.unsafe-deserialization.description",
|
|
9009
9258
|
category: "security",
|
|
@@ -9021,7 +9270,7 @@ registry.register(
|
|
|
9021
9270
|
}
|
|
9022
9271
|
})
|
|
9023
9272
|
);
|
|
9024
|
-
var
|
|
9273
|
+
var CHECK_ID19 = "security.hardcoded-passwords";
|
|
9025
9274
|
var PASSWORD_NAME_REGEX = /^(\$)?(password|pwd|passwd|secret|pass)$/i;
|
|
9026
9275
|
var REMEDIATION13 = getCopy("remediation.security.hardcoded-passwords");
|
|
9027
9276
|
var HTML_CSS_PASSWORD_PATTERNS = [
|
|
@@ -9102,7 +9351,7 @@ var QUERIES7 = {
|
|
|
9102
9351
|
dockerfile: "((identifier) @name) @match"
|
|
9103
9352
|
};
|
|
9104
9353
|
var regexCheck4 = {
|
|
9105
|
-
id:
|
|
9354
|
+
id: CHECK_ID19,
|
|
9106
9355
|
name: getCopy("checks.hardcoded-passwords.name"),
|
|
9107
9356
|
description: getCopy("checks.hardcoded-passwords.description"),
|
|
9108
9357
|
category: "security",
|
|
@@ -9127,8 +9376,8 @@ var regexCheck4 = {
|
|
|
9127
9376
|
const value = match[2] ?? match[1] ?? "";
|
|
9128
9377
|
if (match[2] === void 0 && line.toLowerCase().includes('type="password"')) {
|
|
9129
9378
|
findings.push({
|
|
9130
|
-
id: `${
|
|
9131
|
-
checkId:
|
|
9379
|
+
id: `${CHECK_ID19}:${file.relativePath}:${lineNum + 1}:${(match.index ?? 0) + 1}:${matchIdx}`,
|
|
9380
|
+
checkId: CHECK_ID19,
|
|
9132
9381
|
category: "security",
|
|
9133
9382
|
severity: "critical",
|
|
9134
9383
|
message: getCopy("checks.hardcoded-passwords.message", "password"),
|
|
@@ -9143,8 +9392,8 @@ var regexCheck4 = {
|
|
|
9143
9392
|
if (!PASSWORD_NAME_REGEX.test(name)) continue;
|
|
9144
9393
|
if ((value ?? "").trim().length === 0) continue;
|
|
9145
9394
|
findings.push({
|
|
9146
|
-
id: `${
|
|
9147
|
-
checkId:
|
|
9395
|
+
id: `${CHECK_ID19}:${file.relativePath}:${lineNum + 1}:${(match.index ?? 0) + 1}:${matchIdx}`,
|
|
9396
|
+
checkId: CHECK_ID19,
|
|
9148
9397
|
category: "security",
|
|
9149
9398
|
severity: "critical",
|
|
9150
9399
|
message: getCopy("checks.hardcoded-passwords.message", name),
|
|
@@ -9162,7 +9411,7 @@ var regexCheck4 = {
|
|
|
9162
9411
|
}
|
|
9163
9412
|
};
|
|
9164
9413
|
var treeSitterCheck = createTreeSitterCheck({
|
|
9165
|
-
id:
|
|
9414
|
+
id: CHECK_ID19,
|
|
9166
9415
|
nameKey: "checks.hardcoded-passwords.name",
|
|
9167
9416
|
descriptionKey: "checks.hardcoded-passwords.description",
|
|
9168
9417
|
category: "security",
|
|
@@ -9358,7 +9607,7 @@ function isFullHtmlDocument(content) {
|
|
|
9358
9607
|
function hasHeadElement(content) {
|
|
9359
9608
|
return /<head\b/i.test(content);
|
|
9360
9609
|
}
|
|
9361
|
-
var
|
|
9610
|
+
var CHECK_ID20 = "security.html-missing-csp";
|
|
9362
9611
|
var REMEDIATION14 = getCopy("remediation.security.html-missing-csp");
|
|
9363
9612
|
var META_CSP_PATTERN = /<meta\b[^>]*\bhttp-equiv\s*=\s*["']content-security-policy["'][^>]*>/gi;
|
|
9364
9613
|
var CSP_CONTENT_PATTERN = /\bcontent\s*=\s*(["'])([\s\S]*?)\1/i;
|
|
@@ -9366,7 +9615,7 @@ function firstHtmlAnchor(files) {
|
|
|
9366
9615
|
return files.filter((file) => file.language === "html" && !isMechanicalCheckExcludedPath(file.relativePath)).map((file) => file.relativePath).sort()[0];
|
|
9367
9616
|
}
|
|
9368
9617
|
registry.register({
|
|
9369
|
-
id:
|
|
9618
|
+
id: CHECK_ID20,
|
|
9370
9619
|
name: getCopy("checks.html-missing-csp.name"),
|
|
9371
9620
|
description: getCopy("checks.html-missing-csp.description"),
|
|
9372
9621
|
category: "security",
|
|
@@ -9396,8 +9645,8 @@ registry.register({
|
|
|
9396
9645
|
if (/default-src\s+\*|unsafe-inline|unsafe-eval/i.test(cspValue)) {
|
|
9397
9646
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9398
9647
|
findings.push({
|
|
9399
|
-
id: `${
|
|
9400
|
-
checkId:
|
|
9648
|
+
id: `${CHECK_ID20}:${file.relativePath}:${line}:${column}`,
|
|
9649
|
+
checkId: CHECK_ID20,
|
|
9401
9650
|
category: "security",
|
|
9402
9651
|
severity: "medium",
|
|
9403
9652
|
message: getCopy("checks.html-missing-csp.message", "weak Content Security Policy"),
|
|
@@ -9414,8 +9663,8 @@ registry.register({
|
|
|
9414
9663
|
}
|
|
9415
9664
|
if (findings.length === 0 && sawFullHtmlDoc && !sawValidMetaCsp) {
|
|
9416
9665
|
findings.push({
|
|
9417
|
-
id: `${
|
|
9418
|
-
checkId:
|
|
9666
|
+
id: `${CHECK_ID20}:${anchor}:1:1`,
|
|
9667
|
+
checkId: CHECK_ID20,
|
|
9419
9668
|
category: "security",
|
|
9420
9669
|
severity: "medium",
|
|
9421
9670
|
message: getCopy("checks.html-missing-csp.message", "no Content Security Policy found"),
|
|
@@ -9429,10 +9678,10 @@ registry.register({
|
|
|
9429
9678
|
return findings;
|
|
9430
9679
|
}
|
|
9431
9680
|
});
|
|
9432
|
-
var
|
|
9681
|
+
var CHECK_ID21 = "security.html-static-host-headers";
|
|
9433
9682
|
var REMEDIATION15 = getCopy("remediation.security.html-static-host-headers");
|
|
9434
9683
|
registry.register({
|
|
9435
|
-
id:
|
|
9684
|
+
id: CHECK_ID21,
|
|
9436
9685
|
name: getCopy("checks.html-static-host-headers.name"),
|
|
9437
9686
|
description: getCopy("checks.html-static-host-headers.description"),
|
|
9438
9687
|
category: "security",
|
|
@@ -9452,8 +9701,8 @@ registry.register({
|
|
|
9452
9701
|
if (!anchor) return [];
|
|
9453
9702
|
return [
|
|
9454
9703
|
{
|
|
9455
|
-
id: `${
|
|
9456
|
-
checkId:
|
|
9704
|
+
id: `${CHECK_ID21}:${anchor}:1:1`,
|
|
9705
|
+
checkId: CHECK_ID21,
|
|
9457
9706
|
category: "security",
|
|
9458
9707
|
severity: "medium",
|
|
9459
9708
|
message: getCopy("checks.html-static-host-headers.message", missing.join(", ")),
|
|
@@ -9466,7 +9715,7 @@ registry.register({
|
|
|
9466
9715
|
];
|
|
9467
9716
|
}
|
|
9468
9717
|
});
|
|
9469
|
-
var
|
|
9718
|
+
var CHECK_ID22 = "security.html-inline-unsafe-script";
|
|
9470
9719
|
var REMEDIATION16 = getCopy("remediation.security.html-inline-unsafe-script");
|
|
9471
9720
|
var SCRIPT_BLOCK_PATTERN2 = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
9472
9721
|
var EVENT_HANDLER_PATTERN = /\son[a-z]+\s*=\s*["']([^"']+)["']/gi;
|
|
@@ -9523,7 +9772,7 @@ function findEventHandlerMatches(content) {
|
|
|
9523
9772
|
return results;
|
|
9524
9773
|
}
|
|
9525
9774
|
registry.register({
|
|
9526
|
-
id:
|
|
9775
|
+
id: CHECK_ID22,
|
|
9527
9776
|
name: getCopy("checks.html-inline-unsafe-script.name"),
|
|
9528
9777
|
description: getCopy("checks.html-inline-unsafe-script.description"),
|
|
9529
9778
|
category: "security",
|
|
@@ -9538,8 +9787,8 @@ registry.register({
|
|
|
9538
9787
|
for (const match of findScriptMatches(content)) {
|
|
9539
9788
|
const { line, column } = lineAndColumn6(content, match.index);
|
|
9540
9789
|
findings.push({
|
|
9541
|
-
id: `${
|
|
9542
|
-
checkId:
|
|
9790
|
+
id: `${CHECK_ID22}:${file.relativePath}:${line}:${column}`,
|
|
9791
|
+
checkId: CHECK_ID22,
|
|
9543
9792
|
category: "security",
|
|
9544
9793
|
severity: "high",
|
|
9545
9794
|
message: getCopy("checks.html-inline-unsafe-script.message", match.text),
|
|
@@ -9553,8 +9802,8 @@ registry.register({
|
|
|
9553
9802
|
for (const match of findEventHandlerMatches(content)) {
|
|
9554
9803
|
const { line, column } = lineAndColumn6(content, match.index);
|
|
9555
9804
|
findings.push({
|
|
9556
|
-
id: `${
|
|
9557
|
-
checkId:
|
|
9805
|
+
id: `${CHECK_ID22}:${file.relativePath}:${line}:${column}:event`,
|
|
9806
|
+
checkId: CHECK_ID22,
|
|
9558
9807
|
category: "security",
|
|
9559
9808
|
severity: "high",
|
|
9560
9809
|
message: getCopy("checks.html-inline-unsafe-script.message", match.text),
|
|
@@ -9569,10 +9818,10 @@ registry.register({
|
|
|
9569
9818
|
return findings;
|
|
9570
9819
|
}
|
|
9571
9820
|
});
|
|
9572
|
-
var
|
|
9821
|
+
var CHECK_ID23 = "security.html-referrer-leak";
|
|
9573
9822
|
var REMEDIATION17 = getCopy("remediation.security.html-referrer-leak");
|
|
9574
9823
|
registry.register({
|
|
9575
|
-
id:
|
|
9824
|
+
id: CHECK_ID23,
|
|
9576
9825
|
name: getCopy("checks.html-referrer-leak.name"),
|
|
9577
9826
|
description: getCopy("checks.html-referrer-leak.description"),
|
|
9578
9827
|
category: "security",
|
|
@@ -9588,8 +9837,8 @@ registry.register({
|
|
|
9588
9837
|
if (target.hasRel) continue;
|
|
9589
9838
|
const { line, column } = lineAndColumn5(content, target.index);
|
|
9590
9839
|
findings.push({
|
|
9591
|
-
id: `${
|
|
9592
|
-
checkId:
|
|
9840
|
+
id: `${CHECK_ID23}:${file.relativePath}:${line}:${column}`,
|
|
9841
|
+
checkId: CHECK_ID23,
|
|
9593
9842
|
category: "security",
|
|
9594
9843
|
severity: "medium",
|
|
9595
9844
|
message: getCopy("checks.html-referrer-leak.message", target.text),
|
|
@@ -9604,7 +9853,7 @@ registry.register({
|
|
|
9604
9853
|
return findings;
|
|
9605
9854
|
}
|
|
9606
9855
|
});
|
|
9607
|
-
var
|
|
9856
|
+
var CHECK_ID24 = "security.html-css-comments-secrets";
|
|
9608
9857
|
var REMEDIATION18 = getCopy("remediation.security.html-css-comments-secrets");
|
|
9609
9858
|
var SECRET_MARKER = /(?:api[_-]?key|firebase[_-]?config|google[_-]?api|token|secret|password|private[_-]?key|access[_-]?key)/i;
|
|
9610
9859
|
var SECRET_VALUE = /(?:[:=]\s*|["'`])([A-Za-z0-9+/=_-]{8,}|sk_[A-Za-z0-9/+=_-]{8,})/i;
|
|
@@ -9619,8 +9868,8 @@ function scanPattern(content, filePath, checkPrefix, pattern, messageText, findi
|
|
|
9619
9868
|
const value = valueMatch?.[1] ?? text22;
|
|
9620
9869
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9621
9870
|
findings.push({
|
|
9622
|
-
id: `${
|
|
9623
|
-
checkId:
|
|
9871
|
+
id: `${CHECK_ID24}:${filePath}:${line}:${column}:${checkPrefix}`,
|
|
9872
|
+
checkId: CHECK_ID24,
|
|
9624
9873
|
category: "security",
|
|
9625
9874
|
severity: "critical",
|
|
9626
9875
|
message: getCopy("checks.html-css-comments-secrets.message", messageText),
|
|
@@ -9633,7 +9882,7 @@ function scanPattern(content, filePath, checkPrefix, pattern, messageText, findi
|
|
|
9633
9882
|
}
|
|
9634
9883
|
}
|
|
9635
9884
|
registry.register({
|
|
9636
|
-
id:
|
|
9885
|
+
id: CHECK_ID24,
|
|
9637
9886
|
name: getCopy("checks.html-css-comments-secrets.name"),
|
|
9638
9887
|
description: getCopy("checks.html-css-comments-secrets.description"),
|
|
9639
9888
|
category: "security",
|
|
@@ -9652,7 +9901,7 @@ registry.register({
|
|
|
9652
9901
|
return findings;
|
|
9653
9902
|
}
|
|
9654
9903
|
});
|
|
9655
|
-
var
|
|
9904
|
+
var CHECK_ID25 = "security.css-insecure-import";
|
|
9656
9905
|
var REMEDIATION19 = getCopy("remediation.security.css-insecure-import");
|
|
9657
9906
|
var HTTP_URL_PATTERN = /^http:\/\//i;
|
|
9658
9907
|
var IMPORT_PATTERN = /@import\s+(?:url\(\s*)?(["']?)([^"')\s;]+)\1\s*\)?\s*;/gi;
|
|
@@ -9665,8 +9914,8 @@ function lineText(content, index) {
|
|
|
9665
9914
|
function pushFinding3(findings, filePath, content, matchText, index) {
|
|
9666
9915
|
const { line, column } = lineAndColumn5(content, index);
|
|
9667
9916
|
findings.push({
|
|
9668
|
-
id: `${
|
|
9669
|
-
checkId:
|
|
9917
|
+
id: `${CHECK_ID25}:${filePath}:${line}:${column}`,
|
|
9918
|
+
checkId: CHECK_ID25,
|
|
9670
9919
|
category: "security",
|
|
9671
9920
|
severity: "medium",
|
|
9672
9921
|
message: getCopy("checks.css-insecure-import.message", matchText),
|
|
@@ -9678,7 +9927,7 @@ function pushFinding3(findings, filePath, content, matchText, index) {
|
|
|
9678
9927
|
});
|
|
9679
9928
|
}
|
|
9680
9929
|
registry.register({
|
|
9681
|
-
id:
|
|
9930
|
+
id: CHECK_ID25,
|
|
9682
9931
|
name: getCopy("checks.css-insecure-import.name"),
|
|
9683
9932
|
description: getCopy("checks.css-insecure-import.description"),
|
|
9684
9933
|
category: "security",
|
|
@@ -9705,7 +9954,7 @@ registry.register({
|
|
|
9705
9954
|
return findings;
|
|
9706
9955
|
}
|
|
9707
9956
|
});
|
|
9708
|
-
var
|
|
9957
|
+
var CHECK_ID26 = "security.html-mixed-content";
|
|
9709
9958
|
var REMEDIATION20 = getCopy("remediation.security.html-mixed-content");
|
|
9710
9959
|
var TAG_RESOURCE_PATTERN = /<(script|link|iframe|img|form|object|embed)\b[^>]*?\b(src|href|action|data)\s*=\s*["'](http:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9711
9960
|
var STYLE_ATTR_PATTERN = /\bstyle\s*=\s*["']([\s\S]*?http:\/\/[\s\S]*?)["']/gi;
|
|
@@ -9714,8 +9963,8 @@ var CSS_URL_PATTERN = /url\(\s*(["']?)(http:\/\/[^"')]+)\1\s*\)/gi;
|
|
|
9714
9963
|
function pushFinding4(findings, filePath, content, index, matchText) {
|
|
9715
9964
|
const { line, column } = lineAndColumn5(content, index);
|
|
9716
9965
|
findings.push({
|
|
9717
|
-
id: `${
|
|
9718
|
-
checkId:
|
|
9966
|
+
id: `${CHECK_ID26}:${filePath}:${line}:${column}`,
|
|
9967
|
+
checkId: CHECK_ID26,
|
|
9719
9968
|
category: "security",
|
|
9720
9969
|
severity: "medium",
|
|
9721
9970
|
message: getCopy("checks.html-mixed-content.message", matchText),
|
|
@@ -9727,7 +9976,7 @@ function pushFinding4(findings, filePath, content, index, matchText) {
|
|
|
9727
9976
|
});
|
|
9728
9977
|
}
|
|
9729
9978
|
registry.register({
|
|
9730
|
-
id:
|
|
9979
|
+
id: CHECK_ID26,
|
|
9731
9980
|
name: getCopy("checks.html-mixed-content.name"),
|
|
9732
9981
|
description: getCopy("checks.html-mixed-content.description"),
|
|
9733
9982
|
category: "security",
|
|
@@ -9765,7 +10014,7 @@ registry.register({
|
|
|
9765
10014
|
return findings;
|
|
9766
10015
|
}
|
|
9767
10016
|
});
|
|
9768
|
-
var
|
|
10017
|
+
var CHECK_ID27 = "security.html-external-asset-integrity";
|
|
9769
10018
|
var REMEDIATION21 = getCopy("remediation.security.html-external-asset-integrity");
|
|
9770
10019
|
var SCRIPT_PATTERN = /<script\b[^>]*?\bsrc\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9771
10020
|
var STYLESHEET_PATTERN = /<link\b(?=[^>]*\brel\s*=\s*["'][^"']*\bstylesheet\b[^"']*["'])[^>]*?\bhref\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
@@ -9780,7 +10029,7 @@ function isLocalDevelopmentUrl(rawUrl) {
|
|
|
9780
10029
|
}
|
|
9781
10030
|
}
|
|
9782
10031
|
registry.register({
|
|
9783
|
-
id:
|
|
10032
|
+
id: CHECK_ID27,
|
|
9784
10033
|
name: getCopy("checks.html-external-asset-integrity.name"),
|
|
9785
10034
|
description: getCopy("checks.html-external-asset-integrity.description"),
|
|
9786
10035
|
category: "security",
|
|
@@ -9801,8 +10050,8 @@ registry.register({
|
|
|
9801
10050
|
if (hasIntegrity && hasCrossorigin) continue;
|
|
9802
10051
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9803
10052
|
findings.push({
|
|
9804
|
-
id: `${
|
|
9805
|
-
checkId:
|
|
10053
|
+
id: `${CHECK_ID27}:${file.relativePath}:${line}:${column}`,
|
|
10054
|
+
checkId: CHECK_ID27,
|
|
9806
10055
|
category: "security",
|
|
9807
10056
|
severity: "medium",
|
|
9808
10057
|
message: getCopy("checks.html-external-asset-integrity.message", tagText),
|
|
@@ -9822,8 +10071,8 @@ registry.register({
|
|
|
9822
10071
|
if (hasIntegrity && hasCrossorigin) continue;
|
|
9823
10072
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9824
10073
|
findings.push({
|
|
9825
|
-
id: `${
|
|
9826
|
-
checkId:
|
|
10074
|
+
id: `${CHECK_ID27}:${file.relativePath}:${line}:${column}`,
|
|
10075
|
+
checkId: CHECK_ID27,
|
|
9827
10076
|
category: "security",
|
|
9828
10077
|
severity: "medium",
|
|
9829
10078
|
message: getCopy("checks.html-external-asset-integrity.message", tagText),
|
|
@@ -9838,7 +10087,7 @@ registry.register({
|
|
|
9838
10087
|
return findings;
|
|
9839
10088
|
}
|
|
9840
10089
|
});
|
|
9841
|
-
var
|
|
10090
|
+
var CHECK_ID28 = "security.html-insecure-form-action";
|
|
9842
10091
|
var REMEDIATION22 = getCopy("remediation.security.html-insecure-form-action");
|
|
9843
10092
|
var FORM_ACTION_PATTERN = /<form\b[^>]*?\baction\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))[^>]*?>/gi;
|
|
9844
10093
|
var LOCAL_DEV_URL_PATTERN = /^(?:https?:)?\/\/(?:localhost|127\.0\.0\.1|\[::1\]|::1)(?::\d+)?(?:\/|$)/i;
|
|
@@ -9852,7 +10101,7 @@ function lineAndColumn7(content, index) {
|
|
|
9852
10101
|
};
|
|
9853
10102
|
}
|
|
9854
10103
|
registry.register({
|
|
9855
|
-
id:
|
|
10104
|
+
id: CHECK_ID28,
|
|
9856
10105
|
name: getCopy("checks.html-insecure-form-action.name"),
|
|
9857
10106
|
description: getCopy("checks.html-insecure-form-action.description"),
|
|
9858
10107
|
category: "security",
|
|
@@ -9870,8 +10119,8 @@ registry.register({
|
|
|
9870
10119
|
if (LOCAL_DEV_URL_PATTERN.test(action)) continue;
|
|
9871
10120
|
const { line, column } = lineAndColumn7(content, match.index ?? 0);
|
|
9872
10121
|
findings.push({
|
|
9873
|
-
id: `${
|
|
9874
|
-
checkId:
|
|
10122
|
+
id: `${CHECK_ID28}:${file.relativePath}:${line}:${column}`,
|
|
10123
|
+
checkId: CHECK_ID28,
|
|
9875
10124
|
category: "security",
|
|
9876
10125
|
severity: "medium",
|
|
9877
10126
|
message: getCopy("checks.html-insecure-form-action.message", match[0] ?? action),
|
|
@@ -9886,7 +10135,7 @@ registry.register({
|
|
|
9886
10135
|
return findings;
|
|
9887
10136
|
}
|
|
9888
10137
|
});
|
|
9889
|
-
var
|
|
10138
|
+
var CHECK_ID29 = "security.html-iframe-sandbox";
|
|
9890
10139
|
var REMEDIATION23 = getCopy("remediation.security.html-iframe-sandbox");
|
|
9891
10140
|
var IFRAME_PATTERN = /<iframe\b[^>]*?>/gi;
|
|
9892
10141
|
var SRC_PATTERN = /\bsrc\s*=\s*["']([^"']+)["']/i;
|
|
@@ -9901,7 +10150,7 @@ function hasSandboxAttribute(tagText) {
|
|
|
9901
10150
|
return { present: true, value: match[1] ?? match[2] ?? match[3] ?? "" };
|
|
9902
10151
|
}
|
|
9903
10152
|
registry.register({
|
|
9904
|
-
id:
|
|
10153
|
+
id: CHECK_ID29,
|
|
9905
10154
|
name: getCopy("checks.html-iframe-sandbox.name"),
|
|
9906
10155
|
description: getCopy("checks.html-iframe-sandbox.description"),
|
|
9907
10156
|
category: "security",
|
|
@@ -9924,8 +10173,8 @@ registry.register({
|
|
|
9924
10173
|
if (sandbox.present && !hasBroadEscape) continue;
|
|
9925
10174
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9926
10175
|
findings.push({
|
|
9927
|
-
id: `${
|
|
9928
|
-
checkId:
|
|
10176
|
+
id: `${CHECK_ID29}:${file.relativePath}:${line}:${column}`,
|
|
10177
|
+
checkId: CHECK_ID29,
|
|
9929
10178
|
category: "security",
|
|
9930
10179
|
severity: "medium",
|
|
9931
10180
|
message: getCopy("checks.html-iframe-sandbox.message", tagText),
|
|
@@ -9940,7 +10189,7 @@ registry.register({
|
|
|
9940
10189
|
return findings;
|
|
9941
10190
|
}
|
|
9942
10191
|
});
|
|
9943
|
-
var
|
|
10192
|
+
var CHECK_ID30 = "security.html-unsafe-embed";
|
|
9944
10193
|
var REMEDIATION24 = getCopy("remediation.security.html-unsafe-embed");
|
|
9945
10194
|
var OBJECT_PATTERN = /<object\b[^>]*?\bdata\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9946
10195
|
var EMBED_PATTERN = /<embed\b[^>]*?\bsrc\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
@@ -9961,8 +10210,8 @@ function getTypeValue(tagText) {
|
|
|
9961
10210
|
function pushFindings(findings, filePath, content, index, tagText) {
|
|
9962
10211
|
const { line, column } = lineAndColumn8(content, index);
|
|
9963
10212
|
findings.push({
|
|
9964
|
-
id: `${
|
|
9965
|
-
checkId:
|
|
10213
|
+
id: `${CHECK_ID30}:${filePath}:${line}:${column}`,
|
|
10214
|
+
checkId: CHECK_ID30,
|
|
9966
10215
|
category: "security",
|
|
9967
10216
|
severity: "medium",
|
|
9968
10217
|
message: getCopy("checks.html-unsafe-embed.message", tagText),
|
|
@@ -9974,7 +10223,7 @@ function pushFindings(findings, filePath, content, index, tagText) {
|
|
|
9974
10223
|
});
|
|
9975
10224
|
}
|
|
9976
10225
|
registry.register({
|
|
9977
|
-
id:
|
|
10226
|
+
id: CHECK_ID30,
|
|
9978
10227
|
name: getCopy("checks.html-unsafe-embed.name"),
|
|
9979
10228
|
description: getCopy("checks.html-unsafe-embed.description"),
|
|
9980
10229
|
category: "security",
|
|
@@ -10006,7 +10255,7 @@ registry.register({
|
|
|
10006
10255
|
return findings;
|
|
10007
10256
|
}
|
|
10008
10257
|
});
|
|
10009
|
-
var
|
|
10258
|
+
var CHECK_ID31 = "security.html-meta-redirect";
|
|
10010
10259
|
var REMEDIATION25 = getCopy("remediation.security.html-meta-redirect");
|
|
10011
10260
|
var META_REFRESH_PATTERN = /<meta\b[^>]*\bhttp-equiv\s*=\s*["']refresh["'][^>]*>/gi;
|
|
10012
10261
|
var CONTENT_PATTERN = /\bcontent\s*=\s*["']([^"']+)["']/i;
|
|
@@ -10023,7 +10272,7 @@ function parseRefreshContent(value) {
|
|
|
10023
10272
|
};
|
|
10024
10273
|
}
|
|
10025
10274
|
registry.register({
|
|
10026
|
-
id:
|
|
10275
|
+
id: CHECK_ID31,
|
|
10027
10276
|
name: getCopy("checks.html-meta-redirect.name"),
|
|
10028
10277
|
description: getCopy("checks.html-meta-redirect.description"),
|
|
10029
10278
|
category: "security",
|
|
@@ -10046,8 +10295,8 @@ registry.register({
|
|
|
10046
10295
|
if (LOCAL_DEV_URL_PATTERN4.test(parsed.url)) continue;
|
|
10047
10296
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10048
10297
|
findings.push({
|
|
10049
|
-
id: `${
|
|
10050
|
-
checkId:
|
|
10298
|
+
id: `${CHECK_ID31}:${file.relativePath}:${line}:${column}`,
|
|
10299
|
+
checkId: CHECK_ID31,
|
|
10051
10300
|
category: "security",
|
|
10052
10301
|
severity: "medium",
|
|
10053
10302
|
message: getCopy("checks.html-meta-redirect.message", tagText),
|
|
@@ -10062,7 +10311,7 @@ registry.register({
|
|
|
10062
10311
|
return findings;
|
|
10063
10312
|
}
|
|
10064
10313
|
});
|
|
10065
|
-
var
|
|
10314
|
+
var CHECK_ID32 = "security.html-base-tag-hijack";
|
|
10066
10315
|
var REMEDIATION26 = getCopy("remediation.security.html-base-tag-hijack");
|
|
10067
10316
|
var BASE_PATTERN = /<base\b[^>]*?>/gi;
|
|
10068
10317
|
var HREF_PATTERN = /\bhref\s*=\s*["']([^"']+)["']/i;
|
|
@@ -10078,7 +10327,7 @@ function isOutsideHead(content, index) {
|
|
|
10078
10327
|
return false;
|
|
10079
10328
|
}
|
|
10080
10329
|
registry.register({
|
|
10081
|
-
id:
|
|
10330
|
+
id: CHECK_ID32,
|
|
10082
10331
|
name: getCopy("checks.html-base-tag-hijack.name"),
|
|
10083
10332
|
description: getCopy("checks.html-base-tag-hijack.description"),
|
|
10084
10333
|
category: "security",
|
|
@@ -10100,8 +10349,8 @@ registry.register({
|
|
|
10100
10349
|
if (!externalHref && !targetBlank && !outsideHead) continue;
|
|
10101
10350
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10102
10351
|
findings.push({
|
|
10103
|
-
id: `${
|
|
10104
|
-
checkId:
|
|
10352
|
+
id: `${CHECK_ID32}:${file.relativePath}:${line}:${column}`,
|
|
10353
|
+
checkId: CHECK_ID32,
|
|
10105
10354
|
category: "security",
|
|
10106
10355
|
severity: "medium",
|
|
10107
10356
|
message: getCopy("checks.html-base-tag-hijack.message", tagText),
|
|
@@ -10116,12 +10365,12 @@ registry.register({
|
|
|
10116
10365
|
return findings;
|
|
10117
10366
|
}
|
|
10118
10367
|
});
|
|
10119
|
-
var
|
|
10368
|
+
var CHECK_ID33 = "security.html-javascript-uri";
|
|
10120
10369
|
var REMEDIATION27 = getCopy("remediation.security.html-javascript-uri");
|
|
10121
10370
|
var TAG_PATTERN = /<(a|form|iframe|object|embed)\b[^>]*?\b(href|action|src|data)\s*=\s*["'](javascript:[^"']+)["'][^>]*?>/gi;
|
|
10122
10371
|
var VOID_PATTERN = /^javascript:\s*(?:void\s*\(?\s*0\s*\)?)(?:\s*;)?\s*$/i;
|
|
10123
10372
|
registry.register({
|
|
10124
|
-
id:
|
|
10373
|
+
id: CHECK_ID33,
|
|
10125
10374
|
name: getCopy("checks.html-javascript-uri.name"),
|
|
10126
10375
|
description: getCopy("checks.html-javascript-uri.description"),
|
|
10127
10376
|
category: "security",
|
|
@@ -10139,8 +10388,8 @@ registry.register({
|
|
|
10139
10388
|
if (VOID_PATTERN.test(jsUrl)) continue;
|
|
10140
10389
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10141
10390
|
findings.push({
|
|
10142
|
-
id: `${
|
|
10143
|
-
checkId:
|
|
10391
|
+
id: `${CHECK_ID33}:${file.relativePath}:${line}:${column}`,
|
|
10392
|
+
checkId: CHECK_ID33,
|
|
10144
10393
|
category: "security",
|
|
10145
10394
|
severity: "low",
|
|
10146
10395
|
message: getCopy("checks.html-javascript-uri.message", tagText),
|
|
@@ -10155,7 +10404,7 @@ registry.register({
|
|
|
10155
10404
|
return findings;
|
|
10156
10405
|
}
|
|
10157
10406
|
});
|
|
10158
|
-
var
|
|
10407
|
+
var CHECK_ID34 = "security.html-import-map-integrity";
|
|
10159
10408
|
var REMEDIATION28 = getCopy("remediation.security.html-import-map-integrity");
|
|
10160
10409
|
var IMPORTMAP_SCRIPT_PATTERN = /<script\b[^>]*\btype\s*=\s*["']importmap["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
10161
10410
|
var IMPORTMAP_SRC_PATTERN = /<script\b[^>]*\btype\s*=\s*["']importmap["'][^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi;
|
|
@@ -10176,8 +10425,8 @@ function isExternalUrl(value) {
|
|
|
10176
10425
|
function pushFinding5(findings, filePath, content, index, matchText) {
|
|
10177
10426
|
const { line, column } = lineAndColumn9(content, index);
|
|
10178
10427
|
findings.push({
|
|
10179
|
-
id: `${
|
|
10180
|
-
checkId:
|
|
10428
|
+
id: `${CHECK_ID34}:${filePath}:${line}:${column}`,
|
|
10429
|
+
checkId: CHECK_ID34,
|
|
10181
10430
|
category: "security",
|
|
10182
10431
|
severity: "medium",
|
|
10183
10432
|
message: getCopy("checks.html-import-map-integrity.message", matchText),
|
|
@@ -10202,7 +10451,7 @@ function scanImportMapValue(findings, filePath, content, value, contextLabel, in
|
|
|
10202
10451
|
}
|
|
10203
10452
|
}
|
|
10204
10453
|
registry.register({
|
|
10205
|
-
id:
|
|
10454
|
+
id: CHECK_ID34,
|
|
10206
10455
|
name: getCopy("checks.html-import-map-integrity.name"),
|
|
10207
10456
|
description: getCopy("checks.html-import-map-integrity.description"),
|
|
10208
10457
|
category: "security",
|
|
@@ -10235,7 +10484,7 @@ registry.register({
|
|
|
10235
10484
|
return findings;
|
|
10236
10485
|
}
|
|
10237
10486
|
});
|
|
10238
|
-
var
|
|
10487
|
+
var CHECK_ID35 = "security.css-data-exfiltration";
|
|
10239
10488
|
var REMEDIATION29 = getCopy("remediation.security.css-data-exfiltration");
|
|
10240
10489
|
var ATTR_SELECTOR_PATTERN = /\[[^\]]*(?:value|type|name|email|tel|address|cc-number|cc-exp)[^\]]*(?:\^=|\$=|\*=|~=|\|=)[^\]]*\]/i;
|
|
10241
10490
|
var EXTERNAL_HTTP_URL_PATTERN = /url\(\s*(["']?)http:\/\/[^"')]+\1\s*\)/i;
|
|
@@ -10253,8 +10502,8 @@ function lineAndColumn10(content, index) {
|
|
|
10253
10502
|
function pushFinding6(findings, filePath, content, index, matchText) {
|
|
10254
10503
|
const { line, column } = lineAndColumn10(content, index);
|
|
10255
10504
|
findings.push({
|
|
10256
|
-
id: `${
|
|
10257
|
-
checkId:
|
|
10505
|
+
id: `${CHECK_ID35}:${filePath}:${line}:${column}`,
|
|
10506
|
+
checkId: CHECK_ID35,
|
|
10258
10507
|
category: "security",
|
|
10259
10508
|
severity: "medium",
|
|
10260
10509
|
message: getCopy("checks.css-data-exfiltration.message", matchText),
|
|
@@ -10275,7 +10524,7 @@ function scanCss(findings, filePath, scanContent, originalContent, offset = 0) {
|
|
|
10275
10524
|
}
|
|
10276
10525
|
}
|
|
10277
10526
|
registry.register({
|
|
10278
|
-
id:
|
|
10527
|
+
id: CHECK_ID35,
|
|
10279
10528
|
name: getCopy("checks.css-data-exfiltration.name"),
|
|
10280
10529
|
description: getCopy("checks.css-data-exfiltration.description"),
|
|
10281
10530
|
category: "security",
|
|
@@ -10307,7 +10556,7 @@ registry.register({
|
|
|
10307
10556
|
return findings;
|
|
10308
10557
|
}
|
|
10309
10558
|
});
|
|
10310
|
-
var
|
|
10559
|
+
var CHECK_ID36 = "security.css-external-font-integrity";
|
|
10311
10560
|
var REMEDIATION30 = getCopy("remediation.security.css-external-font-integrity");
|
|
10312
10561
|
var FONT_FACE_PATTERN = /@font-face\b[\s\S]*?\}/gi;
|
|
10313
10562
|
var URL_PATTERN = /url\(\s*(["']?)([^"')]+)\1\s*\)/gi;
|
|
@@ -10339,7 +10588,7 @@ function isPublicCdnFont(url) {
|
|
|
10339
10588
|
}
|
|
10340
10589
|
}
|
|
10341
10590
|
registry.register({
|
|
10342
|
-
id:
|
|
10591
|
+
id: CHECK_ID36,
|
|
10343
10592
|
name: getCopy("checks.css-external-font-integrity.name"),
|
|
10344
10593
|
description: getCopy("checks.css-external-font-integrity.description"),
|
|
10345
10594
|
category: "security",
|
|
@@ -10360,8 +10609,8 @@ registry.register({
|
|
|
10360
10609
|
if (!isPublicCdnFont(url)) continue;
|
|
10361
10610
|
const { line, column } = lineAndColumn11(content, face.index ?? 0);
|
|
10362
10611
|
findings.push({
|
|
10363
|
-
id: `${
|
|
10364
|
-
checkId:
|
|
10612
|
+
id: `${CHECK_ID36}:${file.relativePath}:${line}:${column}`,
|
|
10613
|
+
checkId: CHECK_ID36,
|
|
10365
10614
|
category: "security",
|
|
10366
10615
|
severity: "medium",
|
|
10367
10616
|
message: getCopy("checks.css-external-font-integrity.message", match[0] ?? url),
|
|
@@ -10377,7 +10626,7 @@ registry.register({
|
|
|
10377
10626
|
return findings;
|
|
10378
10627
|
}
|
|
10379
10628
|
});
|
|
10380
|
-
var
|
|
10629
|
+
var CHECK_ID37 = "security.insecure-random";
|
|
10381
10630
|
var QUERIES8 = {
|
|
10382
10631
|
javascript: `
|
|
10383
10632
|
(call_expression
|
|
@@ -10446,7 +10695,7 @@ var QUERIES8 = {
|
|
|
10446
10695
|
};
|
|
10447
10696
|
registry.register(
|
|
10448
10697
|
createTreeSitterCheck({
|
|
10449
|
-
id:
|
|
10698
|
+
id: CHECK_ID37,
|
|
10450
10699
|
nameKey: "checks.insecure-random.name",
|
|
10451
10700
|
descriptionKey: "checks.insecure-random.description",
|
|
10452
10701
|
category: "security",
|
|
@@ -10458,7 +10707,7 @@ registry.register(
|
|
|
10458
10707
|
remediationKey: "remediation.security.insecure-random"
|
|
10459
10708
|
})
|
|
10460
10709
|
);
|
|
10461
|
-
var
|
|
10710
|
+
var CHECK_ID38 = "security.weak-key-entropy";
|
|
10462
10711
|
var REMEDIATION31 = getCopy("remediation.security.weak-key-entropy");
|
|
10463
10712
|
var RANDOM_BYTES_PATTERN = /randomBytes\s*\(\s*(\d+)\s*\)/g;
|
|
10464
10713
|
var WEAK_KEY_CONTEXT = /(?:key|token|secret|id|license|password|session|csrf|nonce|salt|iv)/i;
|
|
@@ -10471,7 +10720,7 @@ function lineAndColumn12(content, index) {
|
|
|
10471
10720
|
};
|
|
10472
10721
|
}
|
|
10473
10722
|
registry.register({
|
|
10474
|
-
id:
|
|
10723
|
+
id: CHECK_ID38,
|
|
10475
10724
|
name: getCopy("checks.weak-key-entropy.name"),
|
|
10476
10725
|
description: getCopy("checks.weak-key-entropy.description"),
|
|
10477
10726
|
category: "security",
|
|
@@ -10493,8 +10742,8 @@ registry.register({
|
|
|
10493
10742
|
if (!WEAK_KEY_CONTEXT.test(lineContent) && !WEAK_KEY_CONTEXT.test(content.slice(Math.max(0, matchIndex - 200), matchIndex))) continue;
|
|
10494
10743
|
const { line, column } = lineAndColumn12(content, matchIndex);
|
|
10495
10744
|
findings.push({
|
|
10496
|
-
id: `${
|
|
10497
|
-
checkId:
|
|
10745
|
+
id: `${CHECK_ID38}:${file.relativePath}:${line}:${column}`,
|
|
10746
|
+
checkId: CHECK_ID38,
|
|
10498
10747
|
category: "security",
|
|
10499
10748
|
severity: "high",
|
|
10500
10749
|
message: getCopy("checks.weak-key-entropy.message", match[0], String(byteCount)),
|
|
@@ -10509,7 +10758,7 @@ registry.register({
|
|
|
10509
10758
|
return findings;
|
|
10510
10759
|
}
|
|
10511
10760
|
});
|
|
10512
|
-
var
|
|
10761
|
+
var CHECK_ID39 = "security.insecure-api-url-default";
|
|
10513
10762
|
var REMEDIATION32 = getCopy("remediation.security.insecure-api-url-default");
|
|
10514
10763
|
var HTTP_URL_DEFAULT_PATTERN = /(?:['"`])(https?:\/\/[^\s'"`]+)(?:['"`])/g;
|
|
10515
10764
|
var API_CONTEXT = /(?:api[_-]?url|base[_-]?url|endpoint|server|host|webhook|resend|paddle|stripe|sendgrid|mailgun|postmark)/i;
|
|
@@ -10524,7 +10773,7 @@ function lineAndColumn13(content, index) {
|
|
|
10524
10773
|
};
|
|
10525
10774
|
}
|
|
10526
10775
|
registry.register({
|
|
10527
|
-
id:
|
|
10776
|
+
id: CHECK_ID39,
|
|
10528
10777
|
name: getCopy("checks.insecure-api-url-default.name"),
|
|
10529
10778
|
description: getCopy("checks.insecure-api-url-default.description"),
|
|
10530
10779
|
category: "security",
|
|
@@ -10549,8 +10798,8 @@ registry.register({
|
|
|
10549
10798
|
if (!isEnvDefault && !API_CONTEXT.test(matchLine)) continue;
|
|
10550
10799
|
const { line, column } = lineAndColumn13(content, matchIndex);
|
|
10551
10800
|
findings.push({
|
|
10552
|
-
id: `${
|
|
10553
|
-
checkId:
|
|
10801
|
+
id: `${CHECK_ID39}:${file.relativePath}:${line}:${column}`,
|
|
10802
|
+
checkId: CHECK_ID39,
|
|
10554
10803
|
category: "security",
|
|
10555
10804
|
severity: "high",
|
|
10556
10805
|
message: getCopy("checks.insecure-api-url-default.message", url),
|
|
@@ -10565,7 +10814,7 @@ registry.register({
|
|
|
10565
10814
|
return findings;
|
|
10566
10815
|
}
|
|
10567
10816
|
});
|
|
10568
|
-
var
|
|
10817
|
+
var CHECK_ID40 = "security.dynamic-sql-columns";
|
|
10569
10818
|
var REMEDIATION33 = getCopy("remediation.security.dynamic-sql-columns");
|
|
10570
10819
|
var SQL_SET_INTERPOLATION = /\bSET\s+.*\$\{[^}]*\}/gis;
|
|
10571
10820
|
var SQL_SET_TEMPLATE = /\bSET\s+.*`[^`]*\$\{[^}]*\}[^`]*`/gis;
|
|
@@ -10579,7 +10828,7 @@ function lineAndColumn14(content, index) {
|
|
|
10579
10828
|
};
|
|
10580
10829
|
}
|
|
10581
10830
|
registry.register({
|
|
10582
|
-
id:
|
|
10831
|
+
id: CHECK_ID40,
|
|
10583
10832
|
name: getCopy("checks.dynamic-sql-columns.name"),
|
|
10584
10833
|
description: getCopy("checks.dynamic-sql-columns.description"),
|
|
10585
10834
|
category: "security",
|
|
@@ -10600,11 +10849,11 @@ registry.register({
|
|
|
10600
10849
|
for (const match of content.matchAll(regex)) {
|
|
10601
10850
|
const matchIndex = match.index ?? 0;
|
|
10602
10851
|
const { line, column } = lineAndColumn14(content, matchIndex);
|
|
10603
|
-
const dedupKey = `${
|
|
10852
|
+
const dedupKey = `${CHECK_ID40}:${file.relativePath}:${line}`;
|
|
10604
10853
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10605
10854
|
findings.push({
|
|
10606
10855
|
id: dedupKey,
|
|
10607
|
-
checkId:
|
|
10856
|
+
checkId: CHECK_ID40,
|
|
10608
10857
|
category: "security",
|
|
10609
10858
|
severity: "medium",
|
|
10610
10859
|
message: getCopy("checks.dynamic-sql-columns.message", label),
|
|
@@ -10620,7 +10869,7 @@ registry.register({
|
|
|
10620
10869
|
return findings;
|
|
10621
10870
|
}
|
|
10622
10871
|
});
|
|
10623
|
-
var
|
|
10872
|
+
var CHECK_ID41 = "security.weak-email-validation";
|
|
10624
10873
|
var REMEDIATION34 = getCopy("remediation.security.weak-email-validation");
|
|
10625
10874
|
var WEAK_EMAIL_REGEX_LINE = /\/\^[^/]*@[^/]*\\\.[^/]{0,4}\$\/[gimsuy]*/g;
|
|
10626
10875
|
var LOOSE_REGEX_CLASS = /\[\^?\\?s@\]\+@\[\^?\\?s@\]\+\\\.\[\^?\\?s@\]\+/g;
|
|
@@ -10634,7 +10883,7 @@ function lineAndColumn15(content, index) {
|
|
|
10634
10883
|
};
|
|
10635
10884
|
}
|
|
10636
10885
|
registry.register({
|
|
10637
|
-
id:
|
|
10886
|
+
id: CHECK_ID41,
|
|
10638
10887
|
name: getCopy("checks.weak-email-validation.name"),
|
|
10639
10888
|
description: getCopy("checks.weak-email-validation.description"),
|
|
10640
10889
|
category: "security",
|
|
@@ -10655,11 +10904,11 @@ registry.register({
|
|
|
10655
10904
|
for (const match of content.matchAll(regex)) {
|
|
10656
10905
|
const matchIndex = match.index ?? 0;
|
|
10657
10906
|
const { line, column } = lineAndColumn15(content, matchIndex);
|
|
10658
|
-
const dedupKey = `${
|
|
10907
|
+
const dedupKey = `${CHECK_ID41}:${file.relativePath}:${line}`;
|
|
10659
10908
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10660
10909
|
findings.push({
|
|
10661
10910
|
id: dedupKey,
|
|
10662
|
-
checkId:
|
|
10911
|
+
checkId: CHECK_ID41,
|
|
10663
10912
|
category: "security",
|
|
10664
10913
|
severity: "medium",
|
|
10665
10914
|
message: getCopy("checks.weak-email-validation.message", `${match[0]} (${label})`),
|
|
@@ -10675,7 +10924,7 @@ registry.register({
|
|
|
10675
10924
|
return findings;
|
|
10676
10925
|
}
|
|
10677
10926
|
});
|
|
10678
|
-
var
|
|
10927
|
+
var CHECK_ID42 = "security.idempotency-key-leak";
|
|
10679
10928
|
var REMEDIATION35 = getCopy("remediation.security.idempotency-key-leak");
|
|
10680
10929
|
var IDEMPOTENCY_CONTEXT = /idempotency[_-]?key|idempotency[_-]?token/i;
|
|
10681
10930
|
var TEMPLATE_LITERAL_INTERPOLATION = new RegExp("`[^`]*\\$\\{[^}]+\\}[^`]*`", "g");
|
|
@@ -10689,7 +10938,7 @@ function lineAndColumn16(content, index) {
|
|
|
10689
10938
|
};
|
|
10690
10939
|
}
|
|
10691
10940
|
registry.register({
|
|
10692
|
-
id:
|
|
10941
|
+
id: CHECK_ID42,
|
|
10693
10942
|
name: getCopy("checks.idempotency-key-leak.name"),
|
|
10694
10943
|
description: getCopy("checks.idempotency-key-leak.description"),
|
|
10695
10944
|
category: "security",
|
|
@@ -10708,11 +10957,11 @@ registry.register({
|
|
|
10708
10957
|
if (!STRUCTURED_ID_PATTERN.test(templateStr)) continue;
|
|
10709
10958
|
const matchIndex = match.index ?? 0;
|
|
10710
10959
|
const { line, column } = lineAndColumn16(content, matchIndex);
|
|
10711
|
-
const dedupKey = `${
|
|
10960
|
+
const dedupKey = `${CHECK_ID42}:${file.relativePath}:${line}`;
|
|
10712
10961
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10713
10962
|
findings.push({
|
|
10714
10963
|
id: dedupKey,
|
|
10715
|
-
checkId:
|
|
10964
|
+
checkId: CHECK_ID42,
|
|
10716
10965
|
category: "security",
|
|
10717
10966
|
severity: "medium",
|
|
10718
10967
|
message: getCopy("checks.idempotency-key-leak.message", templateStr.slice(0, 80)),
|
|
@@ -10727,7 +10976,7 @@ registry.register({
|
|
|
10727
10976
|
return findings;
|
|
10728
10977
|
}
|
|
10729
10978
|
});
|
|
10730
|
-
var
|
|
10979
|
+
var CHECK_ID43 = "data-exposure.plaintext-secret-in-email";
|
|
10731
10980
|
var REMEDIATION36 = getCopy("remediation.data-exposure.plaintext-secret-in-email");
|
|
10732
10981
|
var EMAIL_FILE_PATTERN = /email|mail|notify|notification/i;
|
|
10733
10982
|
var SECRET_IN_TEXT_PATTERN = /(?:license[_-]?key|api[_-]?key|secret[_-]?key|token|password|auth[_-]?code|verification[_-]?code)\b/i;
|
|
@@ -10741,7 +10990,7 @@ function lineAndColumn17(content, index) {
|
|
|
10741
10990
|
};
|
|
10742
10991
|
}
|
|
10743
10992
|
registry.register({
|
|
10744
|
-
id:
|
|
10993
|
+
id: CHECK_ID43,
|
|
10745
10994
|
name: getCopy("checks.plaintext-secret-in-email.name"),
|
|
10746
10995
|
description: getCopy("checks.plaintext-secret-in-email.description"),
|
|
10747
10996
|
category: "data-exposure",
|
|
@@ -10763,8 +11012,8 @@ registry.register({
|
|
|
10763
11012
|
const { line: lineNum, column } = lineAndColumn17(content, lineIndex);
|
|
10764
11013
|
const secretMatch = SECRET_IN_TEXT_PATTERN.exec(lineText2);
|
|
10765
11014
|
findings.push({
|
|
10766
|
-
id: `${
|
|
10767
|
-
checkId:
|
|
11015
|
+
id: `${CHECK_ID43}:${file.relativePath}:${lineNum}:${column}`,
|
|
11016
|
+
checkId: CHECK_ID43,
|
|
10768
11017
|
category: "data-exposure",
|
|
10769
11018
|
severity: "medium",
|
|
10770
11019
|
message: getCopy("checks.plaintext-secret-in-email.message", secretMatch?.[0] ?? "secret"),
|
|
@@ -12974,7 +13223,7 @@ registry.register(
|
|
|
12974
13223
|
attribution: "CWE-94"
|
|
12975
13224
|
})
|
|
12976
13225
|
);
|
|
12977
|
-
var
|
|
13226
|
+
var CHECK_ID44 = "security.missing-csrf-protection";
|
|
12978
13227
|
var CSRF_MARKERS = /\bcsrf\b|xsrf|csrfmiddlewaretoken|double[\s-]submit[\s-]cookie|csurf\b|cookie-parser.*csrf|protect_from_forgery|verify_authenticity_token/i;
|
|
12979
13228
|
var QUERIES9 = {
|
|
12980
13229
|
php: `
|
|
@@ -13031,7 +13280,7 @@ async function hasProjectCsrfProtection(files) {
|
|
|
13031
13280
|
return false;
|
|
13032
13281
|
}
|
|
13033
13282
|
var check = {
|
|
13034
|
-
id:
|
|
13283
|
+
id: CHECK_ID44,
|
|
13035
13284
|
name: resolveCopy3("checks.missing-csrf-protection.name"),
|
|
13036
13285
|
description: resolveCopy3("checks.missing-csrf-protection.description"),
|
|
13037
13286
|
category: "security",
|
|
@@ -13071,8 +13320,8 @@ var check = {
|
|
|
13071
13320
|
const sample = eligible.find((f) => f.language === lang);
|
|
13072
13321
|
if (!sample) continue;
|
|
13073
13322
|
findings.push({
|
|
13074
|
-
id: `${
|
|
13075
|
-
checkId:
|
|
13323
|
+
id: `${CHECK_ID44}:${sample.relativePath}:degraded:${lang}`,
|
|
13324
|
+
checkId: CHECK_ID44,
|
|
13076
13325
|
category: "security",
|
|
13077
13326
|
severity: "info",
|
|
13078
13327
|
message: resolveCopy3("checks.missing-csrf-protection.degraded", sample.relativePath, lang),
|
|
@@ -13093,8 +13342,8 @@ var check = {
|
|
|
13093
13342
|
handlerCount > 1 ? `${handlerCount} state-changing handlers (e.g. ${exampleRoute})` : exampleRoute
|
|
13094
13343
|
);
|
|
13095
13344
|
const finding = {
|
|
13096
|
-
id: `${
|
|
13097
|
-
checkId:
|
|
13345
|
+
id: `${CHECK_ID44}:project:${anchor.relativePath}:1`,
|
|
13346
|
+
checkId: CHECK_ID44,
|
|
13098
13347
|
category: "security",
|
|
13099
13348
|
severity: "medium",
|
|
13100
13349
|
message,
|
|
@@ -13109,7 +13358,7 @@ var check = {
|
|
|
13109
13358
|
}
|
|
13110
13359
|
};
|
|
13111
13360
|
registry.register(check);
|
|
13112
|
-
var
|
|
13361
|
+
var CHECK_ID45 = "security.weak-session-id";
|
|
13113
13362
|
var QUERIES10 = {
|
|
13114
13363
|
php: `
|
|
13115
13364
|
(function_call_expression
|
|
@@ -13196,7 +13445,7 @@ var QUERIES10 = {
|
|
|
13196
13445
|
};
|
|
13197
13446
|
registry.register(
|
|
13198
13447
|
createTreeSitterCheck({
|
|
13199
|
-
id:
|
|
13448
|
+
id: CHECK_ID45,
|
|
13200
13449
|
nameKey: "checks.weak-session-id.name",
|
|
13201
13450
|
descriptionKey: "checks.weak-session-id.description",
|
|
13202
13451
|
category: "security",
|
|
@@ -13214,7 +13463,7 @@ registry.register(
|
|
|
13214
13463
|
}
|
|
13215
13464
|
})
|
|
13216
13465
|
);
|
|
13217
|
-
var
|
|
13466
|
+
var CHECK_ID46 = "security.rails-mass-assignment";
|
|
13218
13467
|
var QUERIES11 = {
|
|
13219
13468
|
ruby: `
|
|
13220
13469
|
(call
|
|
@@ -13237,7 +13486,7 @@ var QUERIES11 = {
|
|
|
13237
13486
|
};
|
|
13238
13487
|
registry.register(
|
|
13239
13488
|
createTreeSitterCheck({
|
|
13240
|
-
id:
|
|
13489
|
+
id: CHECK_ID46,
|
|
13241
13490
|
nameKey: "checks.rails-mass-assignment.name",
|
|
13242
13491
|
descriptionKey: "checks.rails-mass-assignment.description",
|
|
13243
13492
|
category: "security",
|
|
@@ -13249,7 +13498,7 @@ registry.register(
|
|
|
13249
13498
|
remediationKey: "remediation.security.rails-mass-assignment"
|
|
13250
13499
|
})
|
|
13251
13500
|
);
|
|
13252
|
-
var
|
|
13501
|
+
var CHECK_ID47 = "security.rails-insecure-session-config";
|
|
13253
13502
|
var REMEDIATION37 = getCopy("remediation.security.rails-insecure-session-config");
|
|
13254
13503
|
var SESSION_STORE_PATH = /config\/initializers\/session_store\.rb$/;
|
|
13255
13504
|
function hasSecureFlag(content) {
|
|
@@ -13262,7 +13511,7 @@ function hasSameSiteFlag(content) {
|
|
|
13262
13511
|
return /\bsame_site:\s*:(strict|lax)\b/.test(content);
|
|
13263
13512
|
}
|
|
13264
13513
|
registry.register({
|
|
13265
|
-
id:
|
|
13514
|
+
id: CHECK_ID47,
|
|
13266
13515
|
name: getCopy("checks.rails-insecure-session-config.name"),
|
|
13267
13516
|
description: getCopy("checks.rails-insecure-session-config.description"),
|
|
13268
13517
|
category: "security",
|
|
@@ -13282,8 +13531,8 @@ registry.register({
|
|
|
13282
13531
|
if (!hasSameSiteFlag(content)) missing.push("same_site: :strict or :lax");
|
|
13283
13532
|
if (missing.length > 0) {
|
|
13284
13533
|
findings.push({
|
|
13285
|
-
id: `${
|
|
13286
|
-
checkId:
|
|
13534
|
+
id: `${CHECK_ID47}:${file.relativePath}:1`,
|
|
13535
|
+
checkId: CHECK_ID47,
|
|
13287
13536
|
category: "security",
|
|
13288
13537
|
severity: "medium",
|
|
13289
13538
|
message: getCopy(
|
|
@@ -13300,7 +13549,7 @@ registry.register({
|
|
|
13300
13549
|
return findings;
|
|
13301
13550
|
}
|
|
13302
13551
|
});
|
|
13303
|
-
var
|
|
13552
|
+
var CHECK_ID48 = "security.xxe-surface";
|
|
13304
13553
|
var LIBXML_PARSE_QUERY = `
|
|
13305
13554
|
(call_expression
|
|
13306
13555
|
function: (member_expression
|
|
@@ -13347,7 +13596,7 @@ var QUERIES12 = {
|
|
|
13347
13596
|
};
|
|
13348
13597
|
registry.register(
|
|
13349
13598
|
createTreeSitterCheck({
|
|
13350
|
-
id:
|
|
13599
|
+
id: CHECK_ID48,
|
|
13351
13600
|
nameKey: "checks.xxe-surface.name",
|
|
13352
13601
|
descriptionKey: "checks.xxe-surface.description",
|
|
13353
13602
|
category: "security",
|
|
@@ -13546,12 +13795,13 @@ registry.register({
|
|
|
13546
13795
|
return runLLMCheck(ctx, promptTemplate22);
|
|
13547
13796
|
}
|
|
13548
13797
|
});
|
|
13549
|
-
var PROMPT_VERSION23 = "1.
|
|
13798
|
+
var PROMPT_VERSION23 = "1.1.0";
|
|
13550
13799
|
var fallbackPatterns23 = [
|
|
13551
13800
|
{
|
|
13552
13801
|
regex: /while\s*\(\s*(?:true|1)\s*\)|while\s+True\s*:/i,
|
|
13553
13802
|
message: "Infinite loop found \u2014 verify retry logic has a maximum attempt limit",
|
|
13554
|
-
severity: "medium"
|
|
13803
|
+
severity: "medium",
|
|
13804
|
+
skipCommentLines: true
|
|
13555
13805
|
}
|
|
13556
13806
|
];
|
|
13557
13807
|
async function buildPrompt23(sourceFiles) {
|
|
@@ -13613,12 +13863,13 @@ registry.register({
|
|
|
13613
13863
|
return [...bashFindings, ...llmFindings];
|
|
13614
13864
|
}
|
|
13615
13865
|
});
|
|
13616
|
-
var PROMPT_VERSION24 = "1.
|
|
13866
|
+
var PROMPT_VERSION24 = "1.2.0";
|
|
13617
13867
|
var fallbackPatterns24 = [
|
|
13618
13868
|
{
|
|
13619
13869
|
regex: /console\.(?:log|error|warn)\b/,
|
|
13620
13870
|
message: "Unstructured logging detected \u2014 verify observability hooks are present",
|
|
13621
|
-
severity: "medium"
|
|
13871
|
+
severity: "medium",
|
|
13872
|
+
skipCommentLines: true
|
|
13622
13873
|
}
|
|
13623
13874
|
];
|
|
13624
13875
|
async function buildPrompt24(sourceFiles) {
|
|
@@ -13841,12 +14092,13 @@ registry.register({
|
|
|
13841
14092
|
return runLLMCheck(ctx, promptTemplate27);
|
|
13842
14093
|
}
|
|
13843
14094
|
});
|
|
13844
|
-
var PROMPT_VERSION28 = "1.
|
|
14095
|
+
var PROMPT_VERSION28 = "1.1.0";
|
|
13845
14096
|
var fallbackPatterns28 = [
|
|
13846
14097
|
{
|
|
13847
14098
|
regex: /\b(?:fingerprint|deviceId|visitorId|trackingId|correlationId|analyticsId)\b/i,
|
|
13848
14099
|
message: "Derived identifier may link records across sessions or users",
|
|
13849
|
-
severity: "medium"
|
|
14100
|
+
severity: "medium",
|
|
14101
|
+
skipCommentLines: true
|
|
13850
14102
|
},
|
|
13851
14103
|
{
|
|
13852
14104
|
regex: /\bhash\s*\(\s*(?:email|phone|userId|id)\s*\)/i,
|
|
@@ -13880,15 +14132,64 @@ var promptTemplate28 = {
|
|
|
13880
14132
|
parseResponse: parseResponse28,
|
|
13881
14133
|
fallbackPatterns: fallbackPatterns28
|
|
13882
14134
|
};
|
|
14135
|
+
var CHECK_ID49 = "data-exposure.derived-identifiers";
|
|
14136
|
+
var DERIVED_ID_REGEX = /\b(?:fingerprint|device.?id|visitor.?id|tracking.?id|correlation.?id|analytics.?id)\b/i;
|
|
14137
|
+
var DERIVED_TS_QUERIES = {
|
|
14138
|
+
javascript: `
|
|
14139
|
+
(lexical_declaration (variable_declarator name: (identifier) @name)) @match
|
|
14140
|
+
(pair key: (property_identifier) @name) @match
|
|
14141
|
+
(assignment_expression left: (identifier) @name) @match
|
|
14142
|
+
`,
|
|
14143
|
+
typescript: `
|
|
14144
|
+
(lexical_declaration (variable_declarator name: (identifier) @name)) @match
|
|
14145
|
+
(pair key: (property_identifier) @name) @match
|
|
14146
|
+
(property_signature name: (property_identifier) @name) @match
|
|
14147
|
+
`,
|
|
14148
|
+
tsx: `
|
|
14149
|
+
(lexical_declaration (variable_declarator name: (identifier) @name)) @match
|
|
14150
|
+
(pair key: (property_identifier) @name) @match
|
|
14151
|
+
(property_signature name: (property_identifier) @name) @match
|
|
14152
|
+
`,
|
|
14153
|
+
go: `
|
|
14154
|
+
(short_var_declaration left: (expression_list (identifier) @name)) @match
|
|
14155
|
+
(var_declaration (var_spec name: (identifier) @name)) @match
|
|
14156
|
+
`,
|
|
14157
|
+
python: `
|
|
14158
|
+
(assignment left: (identifier) @name) @match
|
|
14159
|
+
`,
|
|
14160
|
+
java: `
|
|
14161
|
+
(local_variable_declaration declarator: (variable_declarator name: (identifier) @name)) @match
|
|
14162
|
+
(field_declaration declarator: (variable_declarator name: (identifier) @name)) @match
|
|
14163
|
+
`
|
|
14164
|
+
};
|
|
14165
|
+
var derivedIdAstCheck = createTreeSitterCheck({
|
|
14166
|
+
id: CHECK_ID49,
|
|
14167
|
+
nameKey: "checks.derived-identifiers.name",
|
|
14168
|
+
descriptionKey: "checks.derived-identifiers.description",
|
|
14169
|
+
category: "data-exposure",
|
|
14170
|
+
severity: "medium",
|
|
14171
|
+
minTier: "pro",
|
|
14172
|
+
queries: DERIVED_TS_QUERIES,
|
|
14173
|
+
messageKey: "checks.derived-identifiers.message",
|
|
14174
|
+
degradedMessageKey: "checks.derived-identifiers.degraded",
|
|
14175
|
+
remediationKey: "remediation.data-exposure.derived-identifiers",
|
|
14176
|
+
fileFilter: (file) => !isMechanicalCheckExcludedPath(file.relativePath),
|
|
14177
|
+
matchFilter: (captures) => DERIVED_ID_REGEX.test(captures.name ?? ""),
|
|
14178
|
+
messageFactory: (captures) => getCopy("checks.derived-identifiers.message", captures.name ?? "unknown")
|
|
14179
|
+
});
|
|
13883
14180
|
registry.register({
|
|
13884
|
-
id:
|
|
14181
|
+
id: CHECK_ID49,
|
|
13885
14182
|
name: getCopy("checks.derived-identifiers.name"),
|
|
13886
14183
|
description: getCopy("checks.derived-identifiers.description"),
|
|
13887
14184
|
category: "data-exposure",
|
|
13888
14185
|
severity: "medium",
|
|
13889
14186
|
minTier: "pro",
|
|
13890
14187
|
async run(ctx) {
|
|
13891
|
-
|
|
14188
|
+
const [llmFindings, astFindings] = await Promise.all([
|
|
14189
|
+
runLLMCheck(ctx, promptTemplate28),
|
|
14190
|
+
derivedIdAstCheck.run(ctx)
|
|
14191
|
+
]);
|
|
14192
|
+
return mergeFindings(astFindings, llmFindings);
|
|
13892
14193
|
}
|
|
13893
14194
|
});
|
|
13894
14195
|
var PROMPT_VERSION29 = "1.0.0";
|
|
@@ -13954,12 +14255,12 @@ registry.register({
|
|
|
13954
14255
|
return [...bashFindings, ...llmFindings];
|
|
13955
14256
|
}
|
|
13956
14257
|
});
|
|
13957
|
-
var
|
|
14258
|
+
var CHECK_ID50 = "security.permissive-grants";
|
|
13958
14259
|
var GRANT_PATTERN = /\bgrant\s+(?:all|all\s+privileges|select|insert|update|delete|usage|execute)\b[\s\S]*?\bto\s+(?:public|anon|anonymous|guest)\b/gi;
|
|
13959
14260
|
var ALTER_DEFAULT_PRIVILEGES_PATTERN = /\balter\s+default\s+privileges\b[\s\S]*?\bgrant\s+[\s\S]*?\bto\s+(?:public|anon|anonymous|guest)\b/gi;
|
|
13960
14261
|
var REMEDIATION38 = getCopy("remediation.security.permissive-grants");
|
|
13961
14262
|
var permissiveGrantsCheck = {
|
|
13962
|
-
id:
|
|
14263
|
+
id: CHECK_ID50,
|
|
13963
14264
|
name: getCopy("checks.permissive-grants.name"),
|
|
13964
14265
|
description: getCopy("checks.permissive-grants.description"),
|
|
13965
14266
|
category: "security",
|
|
@@ -13976,8 +14277,8 @@ var permissiveGrantsCheck = {
|
|
|
13976
14277
|
const beforeMatch = content.slice(0, match.index);
|
|
13977
14278
|
const lineNum = beforeMatch.split("\n").length;
|
|
13978
14279
|
findings.push({
|
|
13979
|
-
id: `${
|
|
13980
|
-
checkId:
|
|
14280
|
+
id: `${CHECK_ID50}:${file.relativePath}:${lineNum}:1`,
|
|
14281
|
+
checkId: CHECK_ID50,
|
|
13981
14282
|
category: "security",
|
|
13982
14283
|
severity: "high",
|
|
13983
14284
|
message: getCopy("checks.permissive-grants.message", match[0].trim()),
|
|
@@ -13994,8 +14295,8 @@ var permissiveGrantsCheck = {
|
|
|
13994
14295
|
const beforeMatch = content.slice(0, altMatch.index);
|
|
13995
14296
|
const lineNum = beforeMatch.split("\n").length;
|
|
13996
14297
|
findings.push({
|
|
13997
|
-
id: `${
|
|
13998
|
-
checkId:
|
|
14298
|
+
id: `${CHECK_ID50}:${file.relativePath}:${lineNum}:1`,
|
|
14299
|
+
checkId: CHECK_ID50,
|
|
13999
14300
|
category: "security",
|
|
14000
14301
|
severity: "high",
|
|
14001
14302
|
message: getCopy("checks.permissive-grants.message", altMatch[0].trim()),
|
|
@@ -14035,10 +14336,10 @@ function classifySqlDialect(content) {
|
|
|
14035
14336
|
}
|
|
14036
14337
|
return "unknown";
|
|
14037
14338
|
}
|
|
14038
|
-
var
|
|
14339
|
+
var CHECK_ID51 = "security.sql-rls-static";
|
|
14039
14340
|
var REMEDIATION39 = getCopy("remediation.security.sql-rls-static");
|
|
14040
14341
|
var sqlRlsStaticCheck = {
|
|
14041
|
-
id:
|
|
14342
|
+
id: CHECK_ID51,
|
|
14042
14343
|
name: getCopy("checks.sql-rls-static.name"),
|
|
14043
14344
|
description: getCopy("checks.sql-rls-static.description"),
|
|
14044
14345
|
category: "security",
|
|
@@ -14093,8 +14394,8 @@ var sqlRlsStaticCheck = {
|
|
|
14093
14394
|
const isRlsEnabled = enabledTables.has(table.name);
|
|
14094
14395
|
if (!isRlsEnabled) {
|
|
14095
14396
|
findings.push({
|
|
14096
|
-
id: `${
|
|
14097
|
-
checkId:
|
|
14397
|
+
id: `${CHECK_ID51}:${file.relativePath}:${table.line}:1:missing-rls`,
|
|
14398
|
+
checkId: CHECK_ID51,
|
|
14098
14399
|
category: "security",
|
|
14099
14400
|
severity: "high",
|
|
14100
14401
|
message: getCopy("checks.sql-rls-static.message", `Table "${table.name}" contains tenant columns but Row-Level Security is not enabled.`),
|
|
@@ -14108,8 +14409,8 @@ var sqlRlsStaticCheck = {
|
|
|
14108
14409
|
const isRlsForced = forcedTables.has(table.name);
|
|
14109
14410
|
if (!isRlsForced) {
|
|
14110
14411
|
findings.push({
|
|
14111
|
-
id: `${
|
|
14112
|
-
checkId:
|
|
14412
|
+
id: `${CHECK_ID51}:${file.relativePath}:${table.line}:1:missing-force-rls`,
|
|
14413
|
+
checkId: CHECK_ID51,
|
|
14113
14414
|
category: "security",
|
|
14114
14415
|
severity: "high",
|
|
14115
14416
|
message: getCopy("checks.sql-rls-static.message", `Table "${table.name}" has Row-Level Security enabled but lacks FORCE ROW LEVEL SECURITY, creating tenant bypass risks for table owners.`),
|
|
@@ -14133,8 +14434,8 @@ var sqlRlsStaticCheck = {
|
|
|
14133
14434
|
const beforeMatch = content.slice(0, policyMatch.index);
|
|
14134
14435
|
const lineNum = beforeMatch.split("\n").length;
|
|
14135
14436
|
findings.push({
|
|
14136
|
-
id: `${
|
|
14137
|
-
checkId:
|
|
14437
|
+
id: `${CHECK_ID51}:${file.relativePath}:${lineNum}:1:weak-policy`,
|
|
14438
|
+
checkId: CHECK_ID51,
|
|
14138
14439
|
category: "security",
|
|
14139
14440
|
severity: "high",
|
|
14140
14441
|
message: getCopy("checks.sql-rls-static.message", `Policy ${policyName} on table "${tableName}" uses an unrestrictive tautology (like true or 1=1).`),
|
|
@@ -14152,10 +14453,10 @@ var sqlRlsStaticCheck = {
|
|
|
14152
14453
|
}
|
|
14153
14454
|
};
|
|
14154
14455
|
registry.register(sqlRlsStaticCheck);
|
|
14155
|
-
var
|
|
14456
|
+
var CHECK_ID52 = "security.sql-security-definer-search-path";
|
|
14156
14457
|
var REMEDIATION40 = getCopy("remediation.security.sql-security-definer-search-path");
|
|
14157
14458
|
var sqlSecurityDefinerSearchPathCheck = {
|
|
14158
|
-
id:
|
|
14459
|
+
id: CHECK_ID52,
|
|
14159
14460
|
name: getCopy("checks.sql-security-definer-search-path.name"),
|
|
14160
14461
|
description: getCopy("checks.sql-security-definer-search-path.description"),
|
|
14161
14462
|
category: "security",
|
|
@@ -14181,8 +14482,8 @@ var sqlSecurityDefinerSearchPathCheck = {
|
|
|
14181
14482
|
const beforeMatch = content.slice(0, match.index);
|
|
14182
14483
|
const lineNum = beforeMatch.split("\n").length;
|
|
14183
14484
|
findings.push({
|
|
14184
|
-
id: `${
|
|
14185
|
-
checkId:
|
|
14485
|
+
id: `${CHECK_ID52}:${file.relativePath}:${lineNum}:1`,
|
|
14486
|
+
checkId: CHECK_ID52,
|
|
14186
14487
|
category: "security",
|
|
14187
14488
|
severity: "high",
|
|
14188
14489
|
message: getCopy("checks.sql-security-definer-search-path.message", `Function "${functionName}" is defined as SECURITY DEFINER but is missing a locked search_path.`),
|
|
@@ -14201,7 +14502,7 @@ var sqlSecurityDefinerSearchPathCheck = {
|
|
|
14201
14502
|
}
|
|
14202
14503
|
};
|
|
14203
14504
|
registry.register(sqlSecurityDefinerSearchPathCheck);
|
|
14204
|
-
var
|
|
14505
|
+
var CHECK_ID53 = "resilience.missing-transaction-guard";
|
|
14205
14506
|
var REMEDIATION41 = getCopy("remediation.resilience.missing-transaction-guard");
|
|
14206
14507
|
var MUTATION_KEYWORDS = [
|
|
14207
14508
|
/\binsert\s+into\b/i,
|
|
@@ -14214,7 +14515,7 @@ var MUTATION_KEYWORDS = [
|
|
|
14214
14515
|
/\bcreate\s+table\s+\w+\s+as\b/i
|
|
14215
14516
|
];
|
|
14216
14517
|
var missingTransactionGuardCheck = {
|
|
14217
|
-
id:
|
|
14518
|
+
id: CHECK_ID53,
|
|
14218
14519
|
name: getCopy("checks.missing-transaction-guard.name"),
|
|
14219
14520
|
description: getCopy("checks.missing-transaction-guard.description"),
|
|
14220
14521
|
category: "resilience",
|
|
@@ -14244,8 +14545,8 @@ var missingTransactionGuardCheck = {
|
|
|
14244
14545
|
}
|
|
14245
14546
|
if (mutationCount > 5) {
|
|
14246
14547
|
findings.push({
|
|
14247
|
-
id: `${
|
|
14248
|
-
checkId:
|
|
14548
|
+
id: `${CHECK_ID53}:${file.relativePath}:1:1`,
|
|
14549
|
+
checkId: CHECK_ID53,
|
|
14249
14550
|
category: "resilience",
|
|
14250
14551
|
severity: "medium",
|
|
14251
14552
|
message: getCopy("checks.missing-transaction-guard.message"),
|
|
@@ -14260,10 +14561,10 @@ var missingTransactionGuardCheck = {
|
|
|
14260
14561
|
}
|
|
14261
14562
|
};
|
|
14262
14563
|
registry.register(missingTransactionGuardCheck);
|
|
14263
|
-
var
|
|
14564
|
+
var CHECK_ID54 = "resilience.destructive-sql-migration";
|
|
14264
14565
|
var REMEDIATION42 = getCopy("remediation.resilience.destructive-sql-migration");
|
|
14265
14566
|
var destructiveSqlMigrationCheck = {
|
|
14266
|
-
id:
|
|
14567
|
+
id: CHECK_ID54,
|
|
14267
14568
|
name: getCopy("checks.destructive-sql-migration.name"),
|
|
14268
14569
|
description: getCopy("checks.destructive-sql-migration.description"),
|
|
14269
14570
|
category: "resilience",
|
|
@@ -14284,8 +14585,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14284
14585
|
const highMatch = highRiskRegex.exec(line);
|
|
14285
14586
|
if (highMatch) {
|
|
14286
14587
|
findings.push({
|
|
14287
|
-
id: `${
|
|
14288
|
-
checkId:
|
|
14588
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(highMatch.index ?? 0) + 1}:high`,
|
|
14589
|
+
checkId: CHECK_ID54,
|
|
14289
14590
|
category: "resilience",
|
|
14290
14591
|
severity: "high",
|
|
14291
14592
|
message: getCopy("checks.destructive-sql-migration.message", highMatch[0]),
|
|
@@ -14298,8 +14599,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14298
14599
|
const truncateMatch = truncateRegex.exec(line);
|
|
14299
14600
|
if (truncateMatch) {
|
|
14300
14601
|
findings.push({
|
|
14301
|
-
id: `${
|
|
14302
|
-
checkId:
|
|
14602
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(truncateMatch.index ?? 0) + 1}:high`,
|
|
14603
|
+
checkId: CHECK_ID54,
|
|
14303
14604
|
category: "resilience",
|
|
14304
14605
|
severity: "high",
|
|
14305
14606
|
message: getCopy("checks.destructive-sql-migration.message", truncateMatch[0]),
|
|
@@ -14315,8 +14616,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14315
14616
|
const colMatch = dropColumnRegex.exec(line);
|
|
14316
14617
|
if (colMatch) {
|
|
14317
14618
|
findings.push({
|
|
14318
|
-
id: `${
|
|
14319
|
-
checkId:
|
|
14619
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(colMatch.index ?? 0) + 1}:col`,
|
|
14620
|
+
checkId: CHECK_ID54,
|
|
14320
14621
|
category: "resilience",
|
|
14321
14622
|
severity: "medium",
|
|
14322
14623
|
message: getCopy("checks.destructive-sql-migration.message", colMatch[0]),
|
|
@@ -14329,8 +14630,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14329
14630
|
const policyMatch = dropPolicyRegex.exec(line);
|
|
14330
14631
|
if (policyMatch) {
|
|
14331
14632
|
findings.push({
|
|
14332
|
-
id: `${
|
|
14333
|
-
checkId:
|
|
14633
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(policyMatch.index ?? 0) + 1}:policy`,
|
|
14634
|
+
checkId: CHECK_ID54,
|
|
14334
14635
|
category: "resilience",
|
|
14335
14636
|
severity: "medium",
|
|
14336
14637
|
message: getCopy("checks.destructive-sql-migration.message", policyMatch[0]),
|
|
@@ -14343,8 +14644,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14343
14644
|
const indexMatch = dropIndexRegex.exec(line);
|
|
14344
14645
|
if (indexMatch) {
|
|
14345
14646
|
findings.push({
|
|
14346
|
-
id: `${
|
|
14347
|
-
checkId:
|
|
14647
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(indexMatch.index ?? 0) + 1}:index`,
|
|
14648
|
+
checkId: CHECK_ID54,
|
|
14348
14649
|
category: "resilience",
|
|
14349
14650
|
severity: "medium",
|
|
14350
14651
|
message: getCopy("checks.destructive-sql-migration.message", indexMatch[0]),
|
|
@@ -14360,7 +14661,7 @@ var destructiveSqlMigrationCheck = {
|
|
|
14360
14661
|
}
|
|
14361
14662
|
};
|
|
14362
14663
|
registry.register(destructiveSqlMigrationCheck);
|
|
14363
|
-
var
|
|
14664
|
+
var CHECK_ID55 = "resilience.sql-broad-mutation";
|
|
14364
14665
|
var REMEDIATION43 = getCopy("remediation.resilience.sql-broad-mutation");
|
|
14365
14666
|
function splitSqlStatements(content) {
|
|
14366
14667
|
const statements = [];
|
|
@@ -14398,7 +14699,7 @@ function splitSqlStatements(content) {
|
|
|
14398
14699
|
return statements;
|
|
14399
14700
|
}
|
|
14400
14701
|
var sqlBroadMutationCheck = {
|
|
14401
|
-
id:
|
|
14702
|
+
id: CHECK_ID55,
|
|
14402
14703
|
name: getCopy("checks.sql-broad-mutation.name"),
|
|
14403
14704
|
description: getCopy("checks.sql-broad-mutation.description"),
|
|
14404
14705
|
category: "resilience",
|
|
@@ -14444,8 +14745,8 @@ var sqlBroadMutationCheck = {
|
|
|
14444
14745
|
const beforeStatement = content.slice(0, charOffset + leadingWhitespace);
|
|
14445
14746
|
const lineNum = beforeStatement.split("\n").length;
|
|
14446
14747
|
findings.push({
|
|
14447
|
-
id: `${
|
|
14448
|
-
checkId:
|
|
14748
|
+
id: `${CHECK_ID55}:${file.relativePath}:${lineNum}:1`,
|
|
14749
|
+
checkId: CHECK_ID55,
|
|
14449
14750
|
category: "resilience",
|
|
14450
14751
|
severity: "high",
|
|
14451
14752
|
message: getCopy("checks.sql-broad-mutation.message", matchedKeyword),
|
|
@@ -14463,7 +14764,7 @@ var sqlBroadMutationCheck = {
|
|
|
14463
14764
|
}
|
|
14464
14765
|
};
|
|
14465
14766
|
registry.register(sqlBroadMutationCheck);
|
|
14466
|
-
var
|
|
14767
|
+
var CHECK_ID56 = "data-exposure.unprotected-sensitive-sql-columns";
|
|
14467
14768
|
var REMEDIATION44 = getCopy("remediation.data-exposure.unprotected-sensitive-sql-columns");
|
|
14468
14769
|
var SENSITIVE_KEYWORDS = [
|
|
14469
14770
|
"ssn",
|
|
@@ -14495,7 +14796,7 @@ var PROTECTED_SUFFIXES = [
|
|
|
14495
14796
|
"_key_id"
|
|
14496
14797
|
];
|
|
14497
14798
|
var unprotectedSensitiveSqlColumnsCheck = {
|
|
14498
|
-
id:
|
|
14799
|
+
id: CHECK_ID56,
|
|
14499
14800
|
name: getCopy("checks.unprotected-sensitive-sql-columns.name"),
|
|
14500
14801
|
description: getCopy("checks.unprotected-sensitive-sql-columns.description"),
|
|
14501
14802
|
category: "data-exposure",
|
|
@@ -14525,8 +14826,8 @@ var unprotectedSensitiveSqlColumnsCheck = {
|
|
|
14525
14826
|
const isProtected = PROTECTED_SUFFIXES.some((suf) => colName.endsWith(suf));
|
|
14526
14827
|
if (isSensitive && isPlainText && !isProtected) {
|
|
14527
14828
|
findings.push({
|
|
14528
|
-
id: `${
|
|
14529
|
-
checkId:
|
|
14829
|
+
id: `${CHECK_ID56}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 1}`,
|
|
14830
|
+
checkId: CHECK_ID56,
|
|
14530
14831
|
category: "data-exposure",
|
|
14531
14832
|
severity: "high",
|
|
14532
14833
|
message: getCopy("checks.unprotected-sensitive-sql-columns.message", `Column "${colNameRaw}" with type "${colTypeRaw}"`),
|
|
@@ -14590,9 +14891,9 @@ registry.register({
|
|
|
14590
14891
|
return runLLMCheck(ctx, promptTemplate30);
|
|
14591
14892
|
}
|
|
14592
14893
|
});
|
|
14593
|
-
var
|
|
14894
|
+
var CHECK_ID57 = "security.rust-unsafe-blocks";
|
|
14594
14895
|
registry.register({
|
|
14595
|
-
id:
|
|
14896
|
+
id: CHECK_ID57,
|
|
14596
14897
|
name: getCopy("checks.rust-unsafe-blocks.name"),
|
|
14597
14898
|
description: getCopy("checks.rust-unsafe-blocks.description"),
|
|
14598
14899
|
category: "security",
|
|
@@ -14679,8 +14980,8 @@ registry.register({
|
|
|
14679
14980
|
}
|
|
14680
14981
|
if (!foundSafety) {
|
|
14681
14982
|
const finding = {
|
|
14682
|
-
id: `${
|
|
14683
|
-
checkId:
|
|
14983
|
+
id: `${CHECK_ID57}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 0}:${i}`,
|
|
14984
|
+
checkId: CHECK_ID57,
|
|
14684
14985
|
category: "security",
|
|
14685
14986
|
severity: "medium",
|
|
14686
14987
|
message: getCopy("checks.rust-unsafe-blocks.message", match.nodeText),
|
|
@@ -14698,7 +14999,7 @@ registry.register({
|
|
|
14698
14999
|
return findings;
|
|
14699
15000
|
}
|
|
14700
15001
|
});
|
|
14701
|
-
var
|
|
15002
|
+
var CHECK_ID58 = "resilience.rust-handler-panics";
|
|
14702
15003
|
var RUST_QUERY = `
|
|
14703
15004
|
(call_expression
|
|
14704
15005
|
function: (field_expression
|
|
@@ -14724,7 +15025,7 @@ function hasAsyncModifier(node) {
|
|
|
14724
15025
|
return false;
|
|
14725
15026
|
}
|
|
14726
15027
|
registry.register({
|
|
14727
|
-
id:
|
|
15028
|
+
id: CHECK_ID58,
|
|
14728
15029
|
name: getCopy("checks.rust-handler-panics.name"),
|
|
14729
15030
|
description: getCopy("checks.rust-handler-panics.description"),
|
|
14730
15031
|
category: "resilience",
|
|
@@ -14790,8 +15091,8 @@ registry.register({
|
|
|
14790
15091
|
const isAsync = hasAsyncModifier(enclosingFunction);
|
|
14791
15092
|
if (isAsync) {
|
|
14792
15093
|
const finding = {
|
|
14793
|
-
id: `${
|
|
14794
|
-
checkId:
|
|
15094
|
+
id: `${CHECK_ID58}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 0}:${i}`,
|
|
15095
|
+
checkId: CHECK_ID58,
|
|
14795
15096
|
category: "resilience",
|
|
14796
15097
|
severity: "high",
|
|
14797
15098
|
message: getCopy("checks.rust-handler-panics.message", match.nodeText),
|
|
@@ -16229,6 +16530,7 @@ async function executeScan(args, ctx) {
|
|
|
16229
16530
|
debug: args.debug,
|
|
16230
16531
|
baseline: args.baseline,
|
|
16231
16532
|
minConfidence: args.minConfidence,
|
|
16533
|
+
minSeverity: args.minSeverity,
|
|
16232
16534
|
onCheckComplete: (completed, total, checkName) => {
|
|
16233
16535
|
heartbeat();
|
|
16234
16536
|
if (showProgress) {
|
|
@@ -16608,6 +16910,7 @@ async function runCli(options) {
|
|
|
16608
16910
|
debug: options.debug,
|
|
16609
16911
|
baseline: options.baseline,
|
|
16610
16912
|
minConfidence: options.minConfidence,
|
|
16913
|
+
minSeverity: options.minSeverity,
|
|
16611
16914
|
showIds: options.showIds,
|
|
16612
16915
|
all: options.all
|
|
16613
16916
|
};
|
|
@@ -16786,12 +17089,13 @@ var require5 = createRequire4(import.meta.url);
|
|
|
16786
17089
|
var { version } = require5("../package.json");
|
|
16787
17090
|
var cli = cac("seaworthycode");
|
|
16788
17091
|
cli.version(version);
|
|
16789
|
-
cli.command("[path]", "Scan a directory for issues").option("--json", "Output JSON to stdout").option("--output <path>", "Write HTML report to file").option("--sarif <path>", "Write SARIF report to file").option("--sarif-stdout", "Write SARIF report to stdout").option("--fail-on-findings", "Exit with code 5 when findings are found").option("--tier <tier>", "Select tier (free, pro)").option("--provider <provider>", "LLM provider (anthropic, openai, kimi, moonshot, deepseek, gemini, minimax, ollama, openrouter)").option("--max-file-size <bytes>", "Skip files larger than this (default: 1 MB)").option("--show-ids", "Show check IDs alongside names in terminal output").option("--debug", "Enable debug logging").option("--baseline [ref]", "Compare against a baseline file or save current findings as baseline (--baseline save)").option("--min-confidence <level>", "Hide findings below this confidence (high, medium, low)").option("--all", "List every finding; do not collapse low-severity findings").action(async (targetDir, options) => {
|
|
17092
|
+
cli.command("[path]", "Scan a directory for issues").option("--json", "Output JSON to stdout").option("--output <path>", "Write HTML report to file").option("--sarif <path>", "Write SARIF report to file").option("--sarif-stdout", "Write SARIF report to stdout").option("--fail-on-findings", "Exit with code 5 when findings are found").option("--tier <tier>", "Select tier (free, pro)").option("--provider <provider>", "LLM provider (anthropic, openai, kimi, moonshot, deepseek, gemini, minimax, ollama, openrouter)").option("--max-file-size <bytes>", "Skip files larger than this (default: 1 MB)").option("--show-ids", "Show check IDs alongside names in terminal output").option("--debug", "Enable debug logging").option("--baseline [ref]", "Compare against a baseline file or save current findings as baseline (--baseline save)").option("--min-confidence <level>", "Hide findings below this confidence (high, medium, low)").option("--min-severity <level>", "Hide findings below this severity (critical, high, medium, low, info)").option("--all", "List every finding; do not collapse low-severity findings").action(async (targetDir, options) => {
|
|
16790
17093
|
const maxFileSize = options.maxFileSize ? Number(options.maxFileSize) : void 0;
|
|
16791
17094
|
const tier = options.tier;
|
|
16792
17095
|
const baseline = options.baseline === "save" ? "save" : options.baseline;
|
|
16793
17096
|
const minConfidence = options.minConfidence;
|
|
16794
|
-
const
|
|
17097
|
+
const minSeverity = options.minSeverity;
|
|
17098
|
+
const result = await runCli({ targetDir, json: options.json, output: options.output, sarif: options.sarif, sarifStdout: options.sarifStdout, failOnFindings: options.failOnFindings, tier, provider: options.provider, maxFileSize, showIds: options.showIds, debug: options.debug, baseline, minConfidence, minSeverity, all: options.all });
|
|
16795
17099
|
if (result.updateMessage) {
|
|
16796
17100
|
console.error(result.updateMessage);
|
|
16797
17101
|
}
|
|
@@ -16859,6 +17163,7 @@ cli.help(() => {
|
|
|
16859
17163
|
--max-file-size <n> Skip files larger than n bytes (default: 1 MB)
|
|
16860
17164
|
--baseline [ref] Compare against baseline file or save (--baseline save)
|
|
16861
17165
|
--min-confidence <level> Hide findings below confidence (high, medium, low)
|
|
17166
|
+
--min-severity <level> Hide findings below severity (critical, high, medium, low, info)
|
|
16862
17167
|
--show-ids Show check IDs alongside names
|
|
16863
17168
|
--all List every finding; do not collapse low-severity findings
|
|
16864
17169
|
--debug Enable debug logging`
|