seaworthycode 1.2.19 → 1.2.20
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 +462 -221
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -641,6 +641,14 @@ function filterByMinConfidence(findings, minConfidence) {
|
|
|
641
641
|
if (!minConfidence) return findings;
|
|
642
642
|
return findings.filter((f) => meetsMinConfidence(f.confidence, minConfidence));
|
|
643
643
|
}
|
|
644
|
+
var SEVERITY_LEVELS = ["critical", "high", "medium", "low", "info"];
|
|
645
|
+
function meetsMinSeverity(findingSeverity, minSeverity) {
|
|
646
|
+
return SEVERITY_LEVELS.indexOf(findingSeverity) <= SEVERITY_LEVELS.indexOf(minSeverity);
|
|
647
|
+
}
|
|
648
|
+
function filterByMinSeverity(findings, minSeverity) {
|
|
649
|
+
if (!minSeverity) return findings;
|
|
650
|
+
return findings.filter((f) => meetsMinSeverity(f.severity, minSeverity));
|
|
651
|
+
}
|
|
644
652
|
var SITE_URL = process.env.SEAWORTHY_SITE_URL ?? "https://seaworthycode.com";
|
|
645
653
|
var whyCopy = {
|
|
646
654
|
"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 +1068,8 @@ var copy = {
|
|
|
1060
1068
|
"checks.verbose-errors.description": "Error responses include stack traces or internal details, leaking implementation information to clients.",
|
|
1061
1069
|
"checks.pii-in-responses.name": "PII in API responses",
|
|
1062
1070
|
"checks.pii-in-responses.description": "API responses include personally identifiable information without proper filtering.",
|
|
1071
|
+
"checks.pii-in-responses.message": (match) => `PII field detected in response model: ${match}`,
|
|
1072
|
+
"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
1073
|
"checks.no-response-filtering.name": "No response filtering",
|
|
1064
1074
|
"checks.no-response-filtering.description": "API responses return full database rows without filtering sensitive or unnecessary fields.",
|
|
1065
1075
|
"checks.missing-lockfile.name": "Missing lockfile",
|
|
@@ -1728,6 +1738,7 @@ var ScanRunner = class {
|
|
|
1728
1738
|
dedupeDegradedFindings(dedupeCrossCheck(dedupeFindings(results)))
|
|
1729
1739
|
);
|
|
1730
1740
|
findings = filterByMinConfidence(findings, options.minConfidence);
|
|
1741
|
+
findings = filterByMinSeverity(findings, options.minSeverity);
|
|
1731
1742
|
if (options.baseline) {
|
|
1732
1743
|
if (options.baseline === "save") {
|
|
1733
1744
|
const baselinePath = resolveBaselinePath(options.targetDir);
|
|
@@ -6204,6 +6215,10 @@ async function runMechanicalCheck(ctx, template) {
|
|
|
6204
6215
|
}
|
|
6205
6216
|
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
6206
6217
|
const line = lines[lineNum];
|
|
6218
|
+
if (pattern.skipCommentLines) {
|
|
6219
|
+
const trimmed = line.trimStart();
|
|
6220
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*")) continue;
|
|
6221
|
+
}
|
|
6207
6222
|
if (pattern.regex.test(line)) {
|
|
6208
6223
|
results.push(
|
|
6209
6224
|
makeMechanicalFinding(template, pattern, file.relativePath, lineNum + 1)
|
|
@@ -7097,12 +7112,13 @@ registry.register({
|
|
|
7097
7112
|
return runLLMCheck(ctx, promptTemplate8);
|
|
7098
7113
|
}
|
|
7099
7114
|
});
|
|
7100
|
-
var PROMPT_VERSION9 = "1.
|
|
7115
|
+
var PROMPT_VERSION9 = "1.3.0";
|
|
7101
7116
|
var fallbackPatterns9 = [
|
|
7102
7117
|
{
|
|
7103
7118
|
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
7119
|
message: "Outbound HTTP call found \u2014 verify timeout is configured",
|
|
7105
|
-
severity: "medium"
|
|
7120
|
+
severity: "medium",
|
|
7121
|
+
skipCommentLines: true
|
|
7106
7122
|
}
|
|
7107
7123
|
];
|
|
7108
7124
|
async function buildPrompt9(sourceFiles) {
|
|
@@ -7376,12 +7392,13 @@ registry.register({
|
|
|
7376
7392
|
return runLLMCheck(ctx, promptTemplate12);
|
|
7377
7393
|
}
|
|
7378
7394
|
});
|
|
7379
|
-
var PROMPT_VERSION13 = "1.
|
|
7395
|
+
var PROMPT_VERSION13 = "1.2.0";
|
|
7380
7396
|
var fallbackPatterns13 = [
|
|
7381
7397
|
{
|
|
7382
7398
|
regex: /console\.(?:log|error|warn)\b/i,
|
|
7383
7399
|
message: "Console logging used \u2014 structured logging recommended",
|
|
7384
|
-
severity: "low"
|
|
7400
|
+
severity: "low",
|
|
7401
|
+
skipCommentLines: true
|
|
7385
7402
|
}
|
|
7386
7403
|
];
|
|
7387
7404
|
async function buildPrompt13(sourceFiles) {
|
|
@@ -7518,16 +7535,18 @@ registry.register({
|
|
|
7518
7535
|
return runLLMCheck(ctx, promptTemplate15);
|
|
7519
7536
|
}
|
|
7520
7537
|
});
|
|
7521
|
-
var PROMPT_VERSION16 = "1.
|
|
7538
|
+
var PROMPT_VERSION16 = "1.1.0";
|
|
7522
7539
|
var fallbackPatterns16 = [
|
|
7523
7540
|
{
|
|
7524
|
-
regex: /(?:admin:admin|
|
|
7541
|
+
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
7542
|
message: "Default credentials possibly in use",
|
|
7526
7543
|
severity: "high"
|
|
7527
7544
|
}
|
|
7528
7545
|
];
|
|
7529
7546
|
async function buildPrompt16(sourceFiles) {
|
|
7530
|
-
const parts = [
|
|
7547
|
+
const parts = [
|
|
7548
|
+
"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."
|
|
7549
|
+
];
|
|
7531
7550
|
for (const file of sourceFiles) {
|
|
7532
7551
|
const content = await file.content();
|
|
7533
7552
|
parts.push(`--- ${file.relativePath} ---
|
|
@@ -7574,7 +7593,13 @@ function isDefaultCredential(password2) {
|
|
|
7574
7593
|
"secret",
|
|
7575
7594
|
"test",
|
|
7576
7595
|
"postgres",
|
|
7577
|
-
"root"
|
|
7596
|
+
"root",
|
|
7597
|
+
"default",
|
|
7598
|
+
"pass",
|
|
7599
|
+
"1234",
|
|
7600
|
+
"12345",
|
|
7601
|
+
"qwerty",
|
|
7602
|
+
"guest"
|
|
7578
7603
|
]);
|
|
7579
7604
|
return defaults.has(normalized);
|
|
7580
7605
|
}
|
|
@@ -7706,6 +7731,115 @@ async function scanBashDefaultCredentials(ctx) {
|
|
|
7706
7731
|
return lineMatch(line, index, /(?:^|\s)(?:export\s+)?[A-Z0-9_]*(?:PASS|PASSWORD|PWD|SECRET|TOKEN)[A-Z0-9_]*=(["'])([^"']{1,64})\1/i);
|
|
7707
7732
|
});
|
|
7708
7733
|
}
|
|
7734
|
+
var CREDENTIAL_NAME_REGEX = /(?:password|passwd|pwd|pass|secret|token|credential|cred)/i;
|
|
7735
|
+
var DEFAULT_CRED_TS_QUERIES = {
|
|
7736
|
+
javascript: `
|
|
7737
|
+
(lexical_declaration
|
|
7738
|
+
(variable_declarator
|
|
7739
|
+
name: (identifier) @name
|
|
7740
|
+
value: (string) @value)) @match
|
|
7741
|
+
|
|
7742
|
+
(assignment_expression
|
|
7743
|
+
left: (identifier) @name
|
|
7744
|
+
right: (string) @value) @match
|
|
7745
|
+
|
|
7746
|
+
(pair
|
|
7747
|
+
key: (property_identifier) @name
|
|
7748
|
+
value: (string) @value) @match
|
|
7749
|
+
`,
|
|
7750
|
+
typescript: `
|
|
7751
|
+
(lexical_declaration
|
|
7752
|
+
(variable_declarator
|
|
7753
|
+
name: (identifier) @name
|
|
7754
|
+
value: (string) @value)) @match
|
|
7755
|
+
|
|
7756
|
+
(assignment_expression
|
|
7757
|
+
left: (identifier) @name
|
|
7758
|
+
right: (string) @value) @match
|
|
7759
|
+
|
|
7760
|
+
(pair
|
|
7761
|
+
key: (property_identifier) @name
|
|
7762
|
+
value: (string) @value) @match
|
|
7763
|
+
`,
|
|
7764
|
+
tsx: `
|
|
7765
|
+
(lexical_declaration
|
|
7766
|
+
(variable_declarator
|
|
7767
|
+
name: (identifier) @name
|
|
7768
|
+
value: (string) @value)) @match
|
|
7769
|
+
|
|
7770
|
+
(assignment_expression
|
|
7771
|
+
left: (identifier) @name
|
|
7772
|
+
right: (string) @value) @match
|
|
7773
|
+
|
|
7774
|
+
(pair
|
|
7775
|
+
key: (property_identifier) @name
|
|
7776
|
+
value: (string) @value) @match
|
|
7777
|
+
`,
|
|
7778
|
+
go: `
|
|
7779
|
+
(short_var_declaration
|
|
7780
|
+
left: (expression_list (identifier) @name)
|
|
7781
|
+
right: (expression_list (_) @value)) @match
|
|
7782
|
+
|
|
7783
|
+
(var_declaration
|
|
7784
|
+
(var_spec
|
|
7785
|
+
name: (identifier) @name
|
|
7786
|
+
value: (_) @value)) @match
|
|
7787
|
+
`,
|
|
7788
|
+
python: `
|
|
7789
|
+
(assignment
|
|
7790
|
+
left: (identifier) @name
|
|
7791
|
+
right: (string) @value) @match
|
|
7792
|
+
`,
|
|
7793
|
+
ruby: `
|
|
7794
|
+
(assignment left: (identifier) @name right: (string) @value) @match
|
|
7795
|
+
|
|
7796
|
+
(pair key: (_) @name value: (string) @value) @match
|
|
7797
|
+
`,
|
|
7798
|
+
java: `
|
|
7799
|
+
(local_variable_declaration
|
|
7800
|
+
declarator: (variable_declarator
|
|
7801
|
+
name: (identifier) @name
|
|
7802
|
+
value: (string_literal) @value)) @match
|
|
7803
|
+
|
|
7804
|
+
(field_declaration
|
|
7805
|
+
declarator: (variable_declarator
|
|
7806
|
+
name: (identifier) @name
|
|
7807
|
+
value: (string_literal) @value)) @match
|
|
7808
|
+
`,
|
|
7809
|
+
rust: `
|
|
7810
|
+
(let_declaration
|
|
7811
|
+
pattern: (identifier) @name
|
|
7812
|
+
value: (string_literal) @value) @match
|
|
7813
|
+
`
|
|
7814
|
+
};
|
|
7815
|
+
var astCheck = createTreeSitterCheck({
|
|
7816
|
+
id: CHECK_ID8,
|
|
7817
|
+
nameKey: "checks.default-credentials.name",
|
|
7818
|
+
descriptionKey: "checks.default-credentials.description",
|
|
7819
|
+
category: "configuration",
|
|
7820
|
+
severity: "high",
|
|
7821
|
+
minTier: "pro",
|
|
7822
|
+
queries: DEFAULT_CRED_TS_QUERIES,
|
|
7823
|
+
messageKey: "checks.default-credentials.message",
|
|
7824
|
+
degradedMessageKey: "checks.default-credentials.degraded",
|
|
7825
|
+
remediationKey: "remediation.configuration.default-credentials",
|
|
7826
|
+
fileFilter: (file) => !isMechanicalCheckExcludedPath(file.relativePath),
|
|
7827
|
+
matchFilter: (captures) => {
|
|
7828
|
+
const name = captures.name ?? "";
|
|
7829
|
+
const value = captures.value ?? "";
|
|
7830
|
+
if (!CREDENTIAL_NAME_REGEX.test(name)) return false;
|
|
7831
|
+
const trimmed = value.trim();
|
|
7832
|
+
if (!/^['"`].*['"`]$/.test(trimmed)) return false;
|
|
7833
|
+
const cleanValue = trimmed.replace(/^['"`]|['"`]$/g, "");
|
|
7834
|
+
if (looksLikeHash(cleanValue)) return false;
|
|
7835
|
+
return isDefaultCredential(cleanValue);
|
|
7836
|
+
},
|
|
7837
|
+
messageFactory: (captures) => {
|
|
7838
|
+
const name = captures.name ?? "unknown";
|
|
7839
|
+
const cleanValue = (captures.value ?? "").trim().replace(/^['"`]|['"`]$/g, "");
|
|
7840
|
+
return getCopy("checks.default-credentials.message", `${name} = "${cleanValue}"`);
|
|
7841
|
+
}
|
|
7842
|
+
});
|
|
7709
7843
|
var sqlCheck = {
|
|
7710
7844
|
id: CHECK_ID8,
|
|
7711
7845
|
name: getCopy("checks.default-credentials.name"),
|
|
@@ -7714,12 +7848,14 @@ var sqlCheck = {
|
|
|
7714
7848
|
severity: "high",
|
|
7715
7849
|
minTier: "pro",
|
|
7716
7850
|
async run(ctx) {
|
|
7717
|
-
const [sqlFindings, bashFindings, llmFindings] = await Promise.all([
|
|
7851
|
+
const [sqlFindings, bashFindings, llmFindings, astFindings] = await Promise.all([
|
|
7718
7852
|
scanSqlDefaultCredentials(ctx),
|
|
7719
7853
|
scanBashDefaultCredentials(ctx),
|
|
7720
|
-
runLLMCheck(ctx, promptTemplate16)
|
|
7854
|
+
runLLMCheck(ctx, promptTemplate16),
|
|
7855
|
+
astCheck.run(ctx)
|
|
7721
7856
|
]);
|
|
7722
|
-
|
|
7857
|
+
const nonAstFindings = [...sqlFindings, ...bashFindings, ...llmFindings];
|
|
7858
|
+
return mergeFindings(astFindings, nonAstFindings);
|
|
7723
7859
|
}
|
|
7724
7860
|
};
|
|
7725
7861
|
registry.register(sqlCheck);
|
|
@@ -7781,16 +7917,17 @@ registry.register({
|
|
|
7781
7917
|
return [...bashFindings, ...llmFindings];
|
|
7782
7918
|
}
|
|
7783
7919
|
});
|
|
7784
|
-
var PROMPT_VERSION18 = "1.
|
|
7920
|
+
var PROMPT_VERSION18 = "1.1.0";
|
|
7785
7921
|
var fallbackPatterns18 = [
|
|
7786
7922
|
{
|
|
7787
7923
|
regex: /\b(?:ssn|social_security|date_of_birth|dob|passport|national_id)\b/i,
|
|
7788
7924
|
message: "Potentially sensitive field name found \u2014 verify it is not exposed in API responses",
|
|
7789
|
-
severity: "high"
|
|
7925
|
+
severity: "high",
|
|
7926
|
+
skipCommentLines: true
|
|
7790
7927
|
}
|
|
7791
7928
|
];
|
|
7792
7929
|
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."];
|
|
7930
|
+
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
7931
|
for (const file of sourceFiles) {
|
|
7795
7932
|
const content = await file.content();
|
|
7796
7933
|
parts.push(`--- ${file.relativePath} ---
|
|
@@ -7808,15 +7945,63 @@ var promptTemplate18 = {
|
|
|
7808
7945
|
parseResponse: parseResponse18,
|
|
7809
7946
|
fallbackPatterns: fallbackPatterns18
|
|
7810
7947
|
};
|
|
7948
|
+
var CHECK_ID9 = "data-exposure.pii-in-responses";
|
|
7949
|
+
var PII_FIELD_REGEX = /\b(?:ssn|social.?security|date.?of.?birth|dob|passport|national.?id)\b/i;
|
|
7950
|
+
var PII_TS_QUERIES = {
|
|
7951
|
+
typescript: `
|
|
7952
|
+
(property_signature name: (property_identifier) @name) @match
|
|
7953
|
+
(pair key: (property_identifier) @name) @match
|
|
7954
|
+
(public_field_definition name: (property_identifier) @name) @match
|
|
7955
|
+
`,
|
|
7956
|
+
javascript: `
|
|
7957
|
+
(pair key: (property_identifier) @name) @match
|
|
7958
|
+
(method_definition name: (property_identifier) @name) @match
|
|
7959
|
+
`,
|
|
7960
|
+
tsx: `
|
|
7961
|
+
(property_signature name: (property_identifier) @name) @match
|
|
7962
|
+
(pair key: (property_identifier) @name) @match
|
|
7963
|
+
`,
|
|
7964
|
+
go: `
|
|
7965
|
+
(field_declaration name: (field_identifier) @name) @match
|
|
7966
|
+
`,
|
|
7967
|
+
python: `
|
|
7968
|
+
(assignment left: (identifier) @name) @match
|
|
7969
|
+
`,
|
|
7970
|
+
ruby: `
|
|
7971
|
+
(assignment left: (identifier) @name right: (_)) @match
|
|
7972
|
+
`,
|
|
7973
|
+
java: `
|
|
7974
|
+
(field_declaration declarator: (variable_declarator name: (identifier) @name)) @match
|
|
7975
|
+
`
|
|
7976
|
+
};
|
|
7977
|
+
var piiAstCheck = createTreeSitterCheck({
|
|
7978
|
+
id: CHECK_ID9,
|
|
7979
|
+
nameKey: "checks.pii-in-responses.name",
|
|
7980
|
+
descriptionKey: "checks.pii-in-responses.description",
|
|
7981
|
+
category: "data-exposure",
|
|
7982
|
+
severity: "high",
|
|
7983
|
+
minTier: "pro",
|
|
7984
|
+
queries: PII_TS_QUERIES,
|
|
7985
|
+
messageKey: "checks.pii-in-responses.message",
|
|
7986
|
+
degradedMessageKey: "checks.pii-in-responses.degraded",
|
|
7987
|
+
remediationKey: "remediation.data-exposure.pii-in-responses",
|
|
7988
|
+
fileFilter: (file) => !isMechanicalCheckExcludedPath(file.relativePath),
|
|
7989
|
+
matchFilter: (captures) => PII_FIELD_REGEX.test(captures.name ?? ""),
|
|
7990
|
+
messageFactory: (captures) => getCopy("checks.pii-in-responses.message", captures.name ?? "unknown")
|
|
7991
|
+
});
|
|
7811
7992
|
registry.register({
|
|
7812
|
-
id:
|
|
7813
|
-
name: "
|
|
7814
|
-
description: "
|
|
7993
|
+
id: CHECK_ID9,
|
|
7994
|
+
name: getCopy("checks.pii-in-responses.name"),
|
|
7995
|
+
description: getCopy("checks.pii-in-responses.description"),
|
|
7815
7996
|
category: "data-exposure",
|
|
7816
7997
|
severity: "high",
|
|
7817
7998
|
minTier: "pro",
|
|
7818
7999
|
async run(ctx) {
|
|
7819
|
-
|
|
8000
|
+
const [llmFindings, astFindings] = await Promise.all([
|
|
8001
|
+
runLLMCheck(ctx, promptTemplate18),
|
|
8002
|
+
piiAstCheck.run(ctx)
|
|
8003
|
+
]);
|
|
8004
|
+
return mergeFindings(astFindings, llmFindings);
|
|
7820
8005
|
}
|
|
7821
8006
|
});
|
|
7822
8007
|
var PROMPT_VERSION19 = "1.0.0";
|
|
@@ -7862,7 +8047,7 @@ registry.register({
|
|
|
7862
8047
|
return runLLMCheck(ctx, promptTemplate19);
|
|
7863
8048
|
}
|
|
7864
8049
|
});
|
|
7865
|
-
var
|
|
8050
|
+
var CHECK_ID10 = "data-exposure.html-form-pii-exposure";
|
|
7866
8051
|
var REMEDIATION12 = getCopy("remediation.data-exposure.html-form-pii-exposure");
|
|
7867
8052
|
var INPUT_PATTERN = /<input\b[^>]*?>/gi;
|
|
7868
8053
|
var AUTOCOMPLETE_PATTERN = /\bautocomplete\s*=\s*["']([^"']+)["']/i;
|
|
@@ -7889,8 +8074,8 @@ function lineAndColumn4(content, index) {
|
|
|
7889
8074
|
function pushFinding2(findings, filePath, content, index, matchText) {
|
|
7890
8075
|
const { line, column } = lineAndColumn4(content, index);
|
|
7891
8076
|
findings.push({
|
|
7892
|
-
id: `${
|
|
7893
|
-
checkId:
|
|
8077
|
+
id: `${CHECK_ID10}:${filePath}:${line}:${column}`,
|
|
8078
|
+
checkId: CHECK_ID10,
|
|
7894
8079
|
category: "data-exposure",
|
|
7895
8080
|
severity: "medium",
|
|
7896
8081
|
message: getCopy("checks.html-form-pii-exposure.message", matchText),
|
|
@@ -7916,7 +8101,7 @@ function hasSensitiveField(tagText) {
|
|
|
7916
8101
|
return SENSITIVE_TYPES.test(type.trim()) || SENSITIVE_NAME.test(name);
|
|
7917
8102
|
}
|
|
7918
8103
|
registry.register({
|
|
7919
|
-
id:
|
|
8104
|
+
id: CHECK_ID10,
|
|
7920
8105
|
name: getCopy("checks.html-form-pii-exposure.name"),
|
|
7921
8106
|
description: getCopy("checks.html-form-pii-exposure.description"),
|
|
7922
8107
|
category: "data-exposure",
|
|
@@ -7945,7 +8130,7 @@ registry.register({
|
|
|
7945
8130
|
return findings;
|
|
7946
8131
|
}
|
|
7947
8132
|
});
|
|
7948
|
-
var
|
|
8133
|
+
var CHECK_ID11 = "security.xss-surface";
|
|
7949
8134
|
var CGI_PATH_HINT = new RegExp("(^|[/_-])cgi([/_.-]|$)", "i");
|
|
7950
8135
|
function isLikelyCgiBashScript(file) {
|
|
7951
8136
|
return CGI_PATH_HINT.test(file.relativePath);
|
|
@@ -8224,7 +8409,7 @@ var QUERIES = {
|
|
|
8224
8409
|
};
|
|
8225
8410
|
registry.register(
|
|
8226
8411
|
createTreeSitterCheck({
|
|
8227
|
-
id:
|
|
8412
|
+
id: CHECK_ID11,
|
|
8228
8413
|
nameKey: "checks.xss-surface.name",
|
|
8229
8414
|
descriptionKey: "checks.xss-surface.description",
|
|
8230
8415
|
category: "security",
|
|
@@ -8239,7 +8424,7 @@ registry.register(
|
|
|
8239
8424
|
fileFilter: (file) => file.language !== "bash" || isLikelyCgiBashScript(file)
|
|
8240
8425
|
})
|
|
8241
8426
|
);
|
|
8242
|
-
var
|
|
8427
|
+
var CHECK_ID12 = "security.os-command-injection";
|
|
8243
8428
|
function isStaticShellLikeLiteral(text22) {
|
|
8244
8429
|
const t = text22.trim();
|
|
8245
8430
|
if (t.length < 2) return false;
|
|
@@ -8369,7 +8554,7 @@ var QUERIES2 = {
|
|
|
8369
8554
|
};
|
|
8370
8555
|
registry.register(
|
|
8371
8556
|
createTreeSitterCheck({
|
|
8372
|
-
id:
|
|
8557
|
+
id: CHECK_ID12,
|
|
8373
8558
|
nameKey: "checks.os-command-injection.name",
|
|
8374
8559
|
descriptionKey: "checks.os-command-injection.description",
|
|
8375
8560
|
category: "security",
|
|
@@ -8386,7 +8571,7 @@ registry.register(
|
|
|
8386
8571
|
}
|
|
8387
8572
|
})
|
|
8388
8573
|
);
|
|
8389
|
-
var
|
|
8574
|
+
var CHECK_ID13 = "security.shell-arbitrary-evaluation";
|
|
8390
8575
|
var QUERY = `
|
|
8391
8576
|
(command
|
|
8392
8577
|
name: (command_name (word) @cmd)
|
|
@@ -8400,7 +8585,7 @@ var QUERY = `
|
|
|
8400
8585
|
`;
|
|
8401
8586
|
registry.register(
|
|
8402
8587
|
createTreeSitterCheck({
|
|
8403
|
-
id:
|
|
8588
|
+
id: CHECK_ID13,
|
|
8404
8589
|
nameKey: "checks.shell-arbitrary-evaluation.name",
|
|
8405
8590
|
descriptionKey: "checks.shell-arbitrary-evaluation.description",
|
|
8406
8591
|
category: "security",
|
|
@@ -8412,7 +8597,7 @@ registry.register(
|
|
|
8412
8597
|
remediationKey: "remediation.security.shell-arbitrary-evaluation"
|
|
8413
8598
|
})
|
|
8414
8599
|
);
|
|
8415
|
-
var
|
|
8600
|
+
var CHECK_ID14 = "security.shell-unescaped-path-deletion";
|
|
8416
8601
|
var QUERY2 = `
|
|
8417
8602
|
(command
|
|
8418
8603
|
name: (command_name (word) @cmd)
|
|
@@ -8442,7 +8627,7 @@ var QUERY2 = `
|
|
|
8442
8627
|
`;
|
|
8443
8628
|
registry.register(
|
|
8444
8629
|
createTreeSitterCheck({
|
|
8445
|
-
id:
|
|
8630
|
+
id: CHECK_ID14,
|
|
8446
8631
|
nameKey: "checks.shell-unescaped-path-deletion.name",
|
|
8447
8632
|
descriptionKey: "checks.shell-unescaped-path-deletion.description",
|
|
8448
8633
|
category: "security",
|
|
@@ -8454,7 +8639,7 @@ registry.register(
|
|
|
8454
8639
|
remediationKey: "remediation.security.shell-unescaped-path-deletion"
|
|
8455
8640
|
})
|
|
8456
8641
|
);
|
|
8457
|
-
var
|
|
8642
|
+
var CHECK_ID15 = "security.file-inclusion";
|
|
8458
8643
|
var QUERIES3 = {
|
|
8459
8644
|
php: `
|
|
8460
8645
|
(include_expression
|
|
@@ -8484,7 +8669,7 @@ var QUERIES3 = {
|
|
|
8484
8669
|
};
|
|
8485
8670
|
registry.register(
|
|
8486
8671
|
createTreeSitterCheck({
|
|
8487
|
-
id:
|
|
8672
|
+
id: CHECK_ID15,
|
|
8488
8673
|
nameKey: "checks.file-inclusion.name",
|
|
8489
8674
|
descriptionKey: "checks.file-inclusion.description",
|
|
8490
8675
|
category: "security",
|
|
@@ -8496,7 +8681,7 @@ registry.register(
|
|
|
8496
8681
|
remediationKey: "remediation.security.file-inclusion"
|
|
8497
8682
|
})
|
|
8498
8683
|
);
|
|
8499
|
-
var
|
|
8684
|
+
var CHECK_ID16 = "security.open-redirect";
|
|
8500
8685
|
var QUERIES4 = {
|
|
8501
8686
|
php: `
|
|
8502
8687
|
(function_call_expression
|
|
@@ -8666,7 +8851,7 @@ var QUERIES4 = {
|
|
|
8666
8851
|
};
|
|
8667
8852
|
registry.register(
|
|
8668
8853
|
createTreeSitterCheck({
|
|
8669
|
-
id:
|
|
8854
|
+
id: CHECK_ID16,
|
|
8670
8855
|
nameKey: "checks.open-redirect.name",
|
|
8671
8856
|
descriptionKey: "checks.open-redirect.description",
|
|
8672
8857
|
category: "security",
|
|
@@ -8678,7 +8863,7 @@ registry.register(
|
|
|
8678
8863
|
remediationKey: "remediation.security.open-redirect"
|
|
8679
8864
|
})
|
|
8680
8865
|
);
|
|
8681
|
-
var
|
|
8866
|
+
var CHECK_ID17 = "security.unsafe-file-upload";
|
|
8682
8867
|
var QUERIES5 = {
|
|
8683
8868
|
javascript: `
|
|
8684
8869
|
(call_expression
|
|
@@ -8850,7 +9035,7 @@ var QUERIES5 = {
|
|
|
8850
9035
|
};
|
|
8851
9036
|
registry.register(
|
|
8852
9037
|
createTreeSitterCheck({
|
|
8853
|
-
id:
|
|
9038
|
+
id: CHECK_ID17,
|
|
8854
9039
|
nameKey: "checks.unsafe-file-upload.name",
|
|
8855
9040
|
descriptionKey: "checks.unsafe-file-upload.description",
|
|
8856
9041
|
category: "security",
|
|
@@ -8866,7 +9051,7 @@ registry.register(
|
|
|
8866
9051
|
}
|
|
8867
9052
|
})
|
|
8868
9053
|
);
|
|
8869
|
-
var
|
|
9054
|
+
var CHECK_ID18 = "security.unsafe-deserialization";
|
|
8870
9055
|
var QUERIES6 = {
|
|
8871
9056
|
javascript: `
|
|
8872
9057
|
(call_expression
|
|
@@ -9003,7 +9188,7 @@ var QUERIES6 = {
|
|
|
9003
9188
|
};
|
|
9004
9189
|
registry.register(
|
|
9005
9190
|
createTreeSitterCheck({
|
|
9006
|
-
id:
|
|
9191
|
+
id: CHECK_ID18,
|
|
9007
9192
|
nameKey: "checks.unsafe-deserialization.name",
|
|
9008
9193
|
descriptionKey: "checks.unsafe-deserialization.description",
|
|
9009
9194
|
category: "security",
|
|
@@ -9021,7 +9206,7 @@ registry.register(
|
|
|
9021
9206
|
}
|
|
9022
9207
|
})
|
|
9023
9208
|
);
|
|
9024
|
-
var
|
|
9209
|
+
var CHECK_ID19 = "security.hardcoded-passwords";
|
|
9025
9210
|
var PASSWORD_NAME_REGEX = /^(\$)?(password|pwd|passwd|secret|pass)$/i;
|
|
9026
9211
|
var REMEDIATION13 = getCopy("remediation.security.hardcoded-passwords");
|
|
9027
9212
|
var HTML_CSS_PASSWORD_PATTERNS = [
|
|
@@ -9102,7 +9287,7 @@ var QUERIES7 = {
|
|
|
9102
9287
|
dockerfile: "((identifier) @name) @match"
|
|
9103
9288
|
};
|
|
9104
9289
|
var regexCheck4 = {
|
|
9105
|
-
id:
|
|
9290
|
+
id: CHECK_ID19,
|
|
9106
9291
|
name: getCopy("checks.hardcoded-passwords.name"),
|
|
9107
9292
|
description: getCopy("checks.hardcoded-passwords.description"),
|
|
9108
9293
|
category: "security",
|
|
@@ -9127,8 +9312,8 @@ var regexCheck4 = {
|
|
|
9127
9312
|
const value = match[2] ?? match[1] ?? "";
|
|
9128
9313
|
if (match[2] === void 0 && line.toLowerCase().includes('type="password"')) {
|
|
9129
9314
|
findings.push({
|
|
9130
|
-
id: `${
|
|
9131
|
-
checkId:
|
|
9315
|
+
id: `${CHECK_ID19}:${file.relativePath}:${lineNum + 1}:${(match.index ?? 0) + 1}:${matchIdx}`,
|
|
9316
|
+
checkId: CHECK_ID19,
|
|
9132
9317
|
category: "security",
|
|
9133
9318
|
severity: "critical",
|
|
9134
9319
|
message: getCopy("checks.hardcoded-passwords.message", "password"),
|
|
@@ -9143,8 +9328,8 @@ var regexCheck4 = {
|
|
|
9143
9328
|
if (!PASSWORD_NAME_REGEX.test(name)) continue;
|
|
9144
9329
|
if ((value ?? "").trim().length === 0) continue;
|
|
9145
9330
|
findings.push({
|
|
9146
|
-
id: `${
|
|
9147
|
-
checkId:
|
|
9331
|
+
id: `${CHECK_ID19}:${file.relativePath}:${lineNum + 1}:${(match.index ?? 0) + 1}:${matchIdx}`,
|
|
9332
|
+
checkId: CHECK_ID19,
|
|
9148
9333
|
category: "security",
|
|
9149
9334
|
severity: "critical",
|
|
9150
9335
|
message: getCopy("checks.hardcoded-passwords.message", name),
|
|
@@ -9162,7 +9347,7 @@ var regexCheck4 = {
|
|
|
9162
9347
|
}
|
|
9163
9348
|
};
|
|
9164
9349
|
var treeSitterCheck = createTreeSitterCheck({
|
|
9165
|
-
id:
|
|
9350
|
+
id: CHECK_ID19,
|
|
9166
9351
|
nameKey: "checks.hardcoded-passwords.name",
|
|
9167
9352
|
descriptionKey: "checks.hardcoded-passwords.description",
|
|
9168
9353
|
category: "security",
|
|
@@ -9358,7 +9543,7 @@ function isFullHtmlDocument(content) {
|
|
|
9358
9543
|
function hasHeadElement(content) {
|
|
9359
9544
|
return /<head\b/i.test(content);
|
|
9360
9545
|
}
|
|
9361
|
-
var
|
|
9546
|
+
var CHECK_ID20 = "security.html-missing-csp";
|
|
9362
9547
|
var REMEDIATION14 = getCopy("remediation.security.html-missing-csp");
|
|
9363
9548
|
var META_CSP_PATTERN = /<meta\b[^>]*\bhttp-equiv\s*=\s*["']content-security-policy["'][^>]*>/gi;
|
|
9364
9549
|
var CSP_CONTENT_PATTERN = /\bcontent\s*=\s*(["'])([\s\S]*?)\1/i;
|
|
@@ -9366,7 +9551,7 @@ function firstHtmlAnchor(files) {
|
|
|
9366
9551
|
return files.filter((file) => file.language === "html" && !isMechanicalCheckExcludedPath(file.relativePath)).map((file) => file.relativePath).sort()[0];
|
|
9367
9552
|
}
|
|
9368
9553
|
registry.register({
|
|
9369
|
-
id:
|
|
9554
|
+
id: CHECK_ID20,
|
|
9370
9555
|
name: getCopy("checks.html-missing-csp.name"),
|
|
9371
9556
|
description: getCopy("checks.html-missing-csp.description"),
|
|
9372
9557
|
category: "security",
|
|
@@ -9396,8 +9581,8 @@ registry.register({
|
|
|
9396
9581
|
if (/default-src\s+\*|unsafe-inline|unsafe-eval/i.test(cspValue)) {
|
|
9397
9582
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9398
9583
|
findings.push({
|
|
9399
|
-
id: `${
|
|
9400
|
-
checkId:
|
|
9584
|
+
id: `${CHECK_ID20}:${file.relativePath}:${line}:${column}`,
|
|
9585
|
+
checkId: CHECK_ID20,
|
|
9401
9586
|
category: "security",
|
|
9402
9587
|
severity: "medium",
|
|
9403
9588
|
message: getCopy("checks.html-missing-csp.message", "weak Content Security Policy"),
|
|
@@ -9414,8 +9599,8 @@ registry.register({
|
|
|
9414
9599
|
}
|
|
9415
9600
|
if (findings.length === 0 && sawFullHtmlDoc && !sawValidMetaCsp) {
|
|
9416
9601
|
findings.push({
|
|
9417
|
-
id: `${
|
|
9418
|
-
checkId:
|
|
9602
|
+
id: `${CHECK_ID20}:${anchor}:1:1`,
|
|
9603
|
+
checkId: CHECK_ID20,
|
|
9419
9604
|
category: "security",
|
|
9420
9605
|
severity: "medium",
|
|
9421
9606
|
message: getCopy("checks.html-missing-csp.message", "no Content Security Policy found"),
|
|
@@ -9429,10 +9614,10 @@ registry.register({
|
|
|
9429
9614
|
return findings;
|
|
9430
9615
|
}
|
|
9431
9616
|
});
|
|
9432
|
-
var
|
|
9617
|
+
var CHECK_ID21 = "security.html-static-host-headers";
|
|
9433
9618
|
var REMEDIATION15 = getCopy("remediation.security.html-static-host-headers");
|
|
9434
9619
|
registry.register({
|
|
9435
|
-
id:
|
|
9620
|
+
id: CHECK_ID21,
|
|
9436
9621
|
name: getCopy("checks.html-static-host-headers.name"),
|
|
9437
9622
|
description: getCopy("checks.html-static-host-headers.description"),
|
|
9438
9623
|
category: "security",
|
|
@@ -9452,8 +9637,8 @@ registry.register({
|
|
|
9452
9637
|
if (!anchor) return [];
|
|
9453
9638
|
return [
|
|
9454
9639
|
{
|
|
9455
|
-
id: `${
|
|
9456
|
-
checkId:
|
|
9640
|
+
id: `${CHECK_ID21}:${anchor}:1:1`,
|
|
9641
|
+
checkId: CHECK_ID21,
|
|
9457
9642
|
category: "security",
|
|
9458
9643
|
severity: "medium",
|
|
9459
9644
|
message: getCopy("checks.html-static-host-headers.message", missing.join(", ")),
|
|
@@ -9466,7 +9651,7 @@ registry.register({
|
|
|
9466
9651
|
];
|
|
9467
9652
|
}
|
|
9468
9653
|
});
|
|
9469
|
-
var
|
|
9654
|
+
var CHECK_ID22 = "security.html-inline-unsafe-script";
|
|
9470
9655
|
var REMEDIATION16 = getCopy("remediation.security.html-inline-unsafe-script");
|
|
9471
9656
|
var SCRIPT_BLOCK_PATTERN2 = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
9472
9657
|
var EVENT_HANDLER_PATTERN = /\son[a-z]+\s*=\s*["']([^"']+)["']/gi;
|
|
@@ -9523,7 +9708,7 @@ function findEventHandlerMatches(content) {
|
|
|
9523
9708
|
return results;
|
|
9524
9709
|
}
|
|
9525
9710
|
registry.register({
|
|
9526
|
-
id:
|
|
9711
|
+
id: CHECK_ID22,
|
|
9527
9712
|
name: getCopy("checks.html-inline-unsafe-script.name"),
|
|
9528
9713
|
description: getCopy("checks.html-inline-unsafe-script.description"),
|
|
9529
9714
|
category: "security",
|
|
@@ -9538,8 +9723,8 @@ registry.register({
|
|
|
9538
9723
|
for (const match of findScriptMatches(content)) {
|
|
9539
9724
|
const { line, column } = lineAndColumn6(content, match.index);
|
|
9540
9725
|
findings.push({
|
|
9541
|
-
id: `${
|
|
9542
|
-
checkId:
|
|
9726
|
+
id: `${CHECK_ID22}:${file.relativePath}:${line}:${column}`,
|
|
9727
|
+
checkId: CHECK_ID22,
|
|
9543
9728
|
category: "security",
|
|
9544
9729
|
severity: "high",
|
|
9545
9730
|
message: getCopy("checks.html-inline-unsafe-script.message", match.text),
|
|
@@ -9553,8 +9738,8 @@ registry.register({
|
|
|
9553
9738
|
for (const match of findEventHandlerMatches(content)) {
|
|
9554
9739
|
const { line, column } = lineAndColumn6(content, match.index);
|
|
9555
9740
|
findings.push({
|
|
9556
|
-
id: `${
|
|
9557
|
-
checkId:
|
|
9741
|
+
id: `${CHECK_ID22}:${file.relativePath}:${line}:${column}:event`,
|
|
9742
|
+
checkId: CHECK_ID22,
|
|
9558
9743
|
category: "security",
|
|
9559
9744
|
severity: "high",
|
|
9560
9745
|
message: getCopy("checks.html-inline-unsafe-script.message", match.text),
|
|
@@ -9569,10 +9754,10 @@ registry.register({
|
|
|
9569
9754
|
return findings;
|
|
9570
9755
|
}
|
|
9571
9756
|
});
|
|
9572
|
-
var
|
|
9757
|
+
var CHECK_ID23 = "security.html-referrer-leak";
|
|
9573
9758
|
var REMEDIATION17 = getCopy("remediation.security.html-referrer-leak");
|
|
9574
9759
|
registry.register({
|
|
9575
|
-
id:
|
|
9760
|
+
id: CHECK_ID23,
|
|
9576
9761
|
name: getCopy("checks.html-referrer-leak.name"),
|
|
9577
9762
|
description: getCopy("checks.html-referrer-leak.description"),
|
|
9578
9763
|
category: "security",
|
|
@@ -9588,8 +9773,8 @@ registry.register({
|
|
|
9588
9773
|
if (target.hasRel) continue;
|
|
9589
9774
|
const { line, column } = lineAndColumn5(content, target.index);
|
|
9590
9775
|
findings.push({
|
|
9591
|
-
id: `${
|
|
9592
|
-
checkId:
|
|
9776
|
+
id: `${CHECK_ID23}:${file.relativePath}:${line}:${column}`,
|
|
9777
|
+
checkId: CHECK_ID23,
|
|
9593
9778
|
category: "security",
|
|
9594
9779
|
severity: "medium",
|
|
9595
9780
|
message: getCopy("checks.html-referrer-leak.message", target.text),
|
|
@@ -9604,7 +9789,7 @@ registry.register({
|
|
|
9604
9789
|
return findings;
|
|
9605
9790
|
}
|
|
9606
9791
|
});
|
|
9607
|
-
var
|
|
9792
|
+
var CHECK_ID24 = "security.html-css-comments-secrets";
|
|
9608
9793
|
var REMEDIATION18 = getCopy("remediation.security.html-css-comments-secrets");
|
|
9609
9794
|
var SECRET_MARKER = /(?:api[_-]?key|firebase[_-]?config|google[_-]?api|token|secret|password|private[_-]?key|access[_-]?key)/i;
|
|
9610
9795
|
var SECRET_VALUE = /(?:[:=]\s*|["'`])([A-Za-z0-9+/=_-]{8,}|sk_[A-Za-z0-9/+=_-]{8,})/i;
|
|
@@ -9619,8 +9804,8 @@ function scanPattern(content, filePath, checkPrefix, pattern, messageText, findi
|
|
|
9619
9804
|
const value = valueMatch?.[1] ?? text22;
|
|
9620
9805
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9621
9806
|
findings.push({
|
|
9622
|
-
id: `${
|
|
9623
|
-
checkId:
|
|
9807
|
+
id: `${CHECK_ID24}:${filePath}:${line}:${column}:${checkPrefix}`,
|
|
9808
|
+
checkId: CHECK_ID24,
|
|
9624
9809
|
category: "security",
|
|
9625
9810
|
severity: "critical",
|
|
9626
9811
|
message: getCopy("checks.html-css-comments-secrets.message", messageText),
|
|
@@ -9633,7 +9818,7 @@ function scanPattern(content, filePath, checkPrefix, pattern, messageText, findi
|
|
|
9633
9818
|
}
|
|
9634
9819
|
}
|
|
9635
9820
|
registry.register({
|
|
9636
|
-
id:
|
|
9821
|
+
id: CHECK_ID24,
|
|
9637
9822
|
name: getCopy("checks.html-css-comments-secrets.name"),
|
|
9638
9823
|
description: getCopy("checks.html-css-comments-secrets.description"),
|
|
9639
9824
|
category: "security",
|
|
@@ -9652,7 +9837,7 @@ registry.register({
|
|
|
9652
9837
|
return findings;
|
|
9653
9838
|
}
|
|
9654
9839
|
});
|
|
9655
|
-
var
|
|
9840
|
+
var CHECK_ID25 = "security.css-insecure-import";
|
|
9656
9841
|
var REMEDIATION19 = getCopy("remediation.security.css-insecure-import");
|
|
9657
9842
|
var HTTP_URL_PATTERN = /^http:\/\//i;
|
|
9658
9843
|
var IMPORT_PATTERN = /@import\s+(?:url\(\s*)?(["']?)([^"')\s;]+)\1\s*\)?\s*;/gi;
|
|
@@ -9665,8 +9850,8 @@ function lineText(content, index) {
|
|
|
9665
9850
|
function pushFinding3(findings, filePath, content, matchText, index) {
|
|
9666
9851
|
const { line, column } = lineAndColumn5(content, index);
|
|
9667
9852
|
findings.push({
|
|
9668
|
-
id: `${
|
|
9669
|
-
checkId:
|
|
9853
|
+
id: `${CHECK_ID25}:${filePath}:${line}:${column}`,
|
|
9854
|
+
checkId: CHECK_ID25,
|
|
9670
9855
|
category: "security",
|
|
9671
9856
|
severity: "medium",
|
|
9672
9857
|
message: getCopy("checks.css-insecure-import.message", matchText),
|
|
@@ -9678,7 +9863,7 @@ function pushFinding3(findings, filePath, content, matchText, index) {
|
|
|
9678
9863
|
});
|
|
9679
9864
|
}
|
|
9680
9865
|
registry.register({
|
|
9681
|
-
id:
|
|
9866
|
+
id: CHECK_ID25,
|
|
9682
9867
|
name: getCopy("checks.css-insecure-import.name"),
|
|
9683
9868
|
description: getCopy("checks.css-insecure-import.description"),
|
|
9684
9869
|
category: "security",
|
|
@@ -9705,7 +9890,7 @@ registry.register({
|
|
|
9705
9890
|
return findings;
|
|
9706
9891
|
}
|
|
9707
9892
|
});
|
|
9708
|
-
var
|
|
9893
|
+
var CHECK_ID26 = "security.html-mixed-content";
|
|
9709
9894
|
var REMEDIATION20 = getCopy("remediation.security.html-mixed-content");
|
|
9710
9895
|
var TAG_RESOURCE_PATTERN = /<(script|link|iframe|img|form|object|embed)\b[^>]*?\b(src|href|action|data)\s*=\s*["'](http:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9711
9896
|
var STYLE_ATTR_PATTERN = /\bstyle\s*=\s*["']([\s\S]*?http:\/\/[\s\S]*?)["']/gi;
|
|
@@ -9714,8 +9899,8 @@ var CSS_URL_PATTERN = /url\(\s*(["']?)(http:\/\/[^"')]+)\1\s*\)/gi;
|
|
|
9714
9899
|
function pushFinding4(findings, filePath, content, index, matchText) {
|
|
9715
9900
|
const { line, column } = lineAndColumn5(content, index);
|
|
9716
9901
|
findings.push({
|
|
9717
|
-
id: `${
|
|
9718
|
-
checkId:
|
|
9902
|
+
id: `${CHECK_ID26}:${filePath}:${line}:${column}`,
|
|
9903
|
+
checkId: CHECK_ID26,
|
|
9719
9904
|
category: "security",
|
|
9720
9905
|
severity: "medium",
|
|
9721
9906
|
message: getCopy("checks.html-mixed-content.message", matchText),
|
|
@@ -9727,7 +9912,7 @@ function pushFinding4(findings, filePath, content, index, matchText) {
|
|
|
9727
9912
|
});
|
|
9728
9913
|
}
|
|
9729
9914
|
registry.register({
|
|
9730
|
-
id:
|
|
9915
|
+
id: CHECK_ID26,
|
|
9731
9916
|
name: getCopy("checks.html-mixed-content.name"),
|
|
9732
9917
|
description: getCopy("checks.html-mixed-content.description"),
|
|
9733
9918
|
category: "security",
|
|
@@ -9765,7 +9950,7 @@ registry.register({
|
|
|
9765
9950
|
return findings;
|
|
9766
9951
|
}
|
|
9767
9952
|
});
|
|
9768
|
-
var
|
|
9953
|
+
var CHECK_ID27 = "security.html-external-asset-integrity";
|
|
9769
9954
|
var REMEDIATION21 = getCopy("remediation.security.html-external-asset-integrity");
|
|
9770
9955
|
var SCRIPT_PATTERN = /<script\b[^>]*?\bsrc\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9771
9956
|
var STYLESHEET_PATTERN = /<link\b(?=[^>]*\brel\s*=\s*["'][^"']*\bstylesheet\b[^"']*["'])[^>]*?\bhref\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
@@ -9780,7 +9965,7 @@ function isLocalDevelopmentUrl(rawUrl) {
|
|
|
9780
9965
|
}
|
|
9781
9966
|
}
|
|
9782
9967
|
registry.register({
|
|
9783
|
-
id:
|
|
9968
|
+
id: CHECK_ID27,
|
|
9784
9969
|
name: getCopy("checks.html-external-asset-integrity.name"),
|
|
9785
9970
|
description: getCopy("checks.html-external-asset-integrity.description"),
|
|
9786
9971
|
category: "security",
|
|
@@ -9801,8 +9986,8 @@ registry.register({
|
|
|
9801
9986
|
if (hasIntegrity && hasCrossorigin) continue;
|
|
9802
9987
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9803
9988
|
findings.push({
|
|
9804
|
-
id: `${
|
|
9805
|
-
checkId:
|
|
9989
|
+
id: `${CHECK_ID27}:${file.relativePath}:${line}:${column}`,
|
|
9990
|
+
checkId: CHECK_ID27,
|
|
9806
9991
|
category: "security",
|
|
9807
9992
|
severity: "medium",
|
|
9808
9993
|
message: getCopy("checks.html-external-asset-integrity.message", tagText),
|
|
@@ -9822,8 +10007,8 @@ registry.register({
|
|
|
9822
10007
|
if (hasIntegrity && hasCrossorigin) continue;
|
|
9823
10008
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9824
10009
|
findings.push({
|
|
9825
|
-
id: `${
|
|
9826
|
-
checkId:
|
|
10010
|
+
id: `${CHECK_ID27}:${file.relativePath}:${line}:${column}`,
|
|
10011
|
+
checkId: CHECK_ID27,
|
|
9827
10012
|
category: "security",
|
|
9828
10013
|
severity: "medium",
|
|
9829
10014
|
message: getCopy("checks.html-external-asset-integrity.message", tagText),
|
|
@@ -9838,7 +10023,7 @@ registry.register({
|
|
|
9838
10023
|
return findings;
|
|
9839
10024
|
}
|
|
9840
10025
|
});
|
|
9841
|
-
var
|
|
10026
|
+
var CHECK_ID28 = "security.html-insecure-form-action";
|
|
9842
10027
|
var REMEDIATION22 = getCopy("remediation.security.html-insecure-form-action");
|
|
9843
10028
|
var FORM_ACTION_PATTERN = /<form\b[^>]*?\baction\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))[^>]*?>/gi;
|
|
9844
10029
|
var LOCAL_DEV_URL_PATTERN = /^(?:https?:)?\/\/(?:localhost|127\.0\.0\.1|\[::1\]|::1)(?::\d+)?(?:\/|$)/i;
|
|
@@ -9852,7 +10037,7 @@ function lineAndColumn7(content, index) {
|
|
|
9852
10037
|
};
|
|
9853
10038
|
}
|
|
9854
10039
|
registry.register({
|
|
9855
|
-
id:
|
|
10040
|
+
id: CHECK_ID28,
|
|
9856
10041
|
name: getCopy("checks.html-insecure-form-action.name"),
|
|
9857
10042
|
description: getCopy("checks.html-insecure-form-action.description"),
|
|
9858
10043
|
category: "security",
|
|
@@ -9870,8 +10055,8 @@ registry.register({
|
|
|
9870
10055
|
if (LOCAL_DEV_URL_PATTERN.test(action)) continue;
|
|
9871
10056
|
const { line, column } = lineAndColumn7(content, match.index ?? 0);
|
|
9872
10057
|
findings.push({
|
|
9873
|
-
id: `${
|
|
9874
|
-
checkId:
|
|
10058
|
+
id: `${CHECK_ID28}:${file.relativePath}:${line}:${column}`,
|
|
10059
|
+
checkId: CHECK_ID28,
|
|
9875
10060
|
category: "security",
|
|
9876
10061
|
severity: "medium",
|
|
9877
10062
|
message: getCopy("checks.html-insecure-form-action.message", match[0] ?? action),
|
|
@@ -9886,7 +10071,7 @@ registry.register({
|
|
|
9886
10071
|
return findings;
|
|
9887
10072
|
}
|
|
9888
10073
|
});
|
|
9889
|
-
var
|
|
10074
|
+
var CHECK_ID29 = "security.html-iframe-sandbox";
|
|
9890
10075
|
var REMEDIATION23 = getCopy("remediation.security.html-iframe-sandbox");
|
|
9891
10076
|
var IFRAME_PATTERN = /<iframe\b[^>]*?>/gi;
|
|
9892
10077
|
var SRC_PATTERN = /\bsrc\s*=\s*["']([^"']+)["']/i;
|
|
@@ -9901,7 +10086,7 @@ function hasSandboxAttribute(tagText) {
|
|
|
9901
10086
|
return { present: true, value: match[1] ?? match[2] ?? match[3] ?? "" };
|
|
9902
10087
|
}
|
|
9903
10088
|
registry.register({
|
|
9904
|
-
id:
|
|
10089
|
+
id: CHECK_ID29,
|
|
9905
10090
|
name: getCopy("checks.html-iframe-sandbox.name"),
|
|
9906
10091
|
description: getCopy("checks.html-iframe-sandbox.description"),
|
|
9907
10092
|
category: "security",
|
|
@@ -9924,8 +10109,8 @@ registry.register({
|
|
|
9924
10109
|
if (sandbox.present && !hasBroadEscape) continue;
|
|
9925
10110
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9926
10111
|
findings.push({
|
|
9927
|
-
id: `${
|
|
9928
|
-
checkId:
|
|
10112
|
+
id: `${CHECK_ID29}:${file.relativePath}:${line}:${column}`,
|
|
10113
|
+
checkId: CHECK_ID29,
|
|
9929
10114
|
category: "security",
|
|
9930
10115
|
severity: "medium",
|
|
9931
10116
|
message: getCopy("checks.html-iframe-sandbox.message", tagText),
|
|
@@ -9940,7 +10125,7 @@ registry.register({
|
|
|
9940
10125
|
return findings;
|
|
9941
10126
|
}
|
|
9942
10127
|
});
|
|
9943
|
-
var
|
|
10128
|
+
var CHECK_ID30 = "security.html-unsafe-embed";
|
|
9944
10129
|
var REMEDIATION24 = getCopy("remediation.security.html-unsafe-embed");
|
|
9945
10130
|
var OBJECT_PATTERN = /<object\b[^>]*?\bdata\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9946
10131
|
var EMBED_PATTERN = /<embed\b[^>]*?\bsrc\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
@@ -9961,8 +10146,8 @@ function getTypeValue(tagText) {
|
|
|
9961
10146
|
function pushFindings(findings, filePath, content, index, tagText) {
|
|
9962
10147
|
const { line, column } = lineAndColumn8(content, index);
|
|
9963
10148
|
findings.push({
|
|
9964
|
-
id: `${
|
|
9965
|
-
checkId:
|
|
10149
|
+
id: `${CHECK_ID30}:${filePath}:${line}:${column}`,
|
|
10150
|
+
checkId: CHECK_ID30,
|
|
9966
10151
|
category: "security",
|
|
9967
10152
|
severity: "medium",
|
|
9968
10153
|
message: getCopy("checks.html-unsafe-embed.message", tagText),
|
|
@@ -9974,7 +10159,7 @@ function pushFindings(findings, filePath, content, index, tagText) {
|
|
|
9974
10159
|
});
|
|
9975
10160
|
}
|
|
9976
10161
|
registry.register({
|
|
9977
|
-
id:
|
|
10162
|
+
id: CHECK_ID30,
|
|
9978
10163
|
name: getCopy("checks.html-unsafe-embed.name"),
|
|
9979
10164
|
description: getCopy("checks.html-unsafe-embed.description"),
|
|
9980
10165
|
category: "security",
|
|
@@ -10006,7 +10191,7 @@ registry.register({
|
|
|
10006
10191
|
return findings;
|
|
10007
10192
|
}
|
|
10008
10193
|
});
|
|
10009
|
-
var
|
|
10194
|
+
var CHECK_ID31 = "security.html-meta-redirect";
|
|
10010
10195
|
var REMEDIATION25 = getCopy("remediation.security.html-meta-redirect");
|
|
10011
10196
|
var META_REFRESH_PATTERN = /<meta\b[^>]*\bhttp-equiv\s*=\s*["']refresh["'][^>]*>/gi;
|
|
10012
10197
|
var CONTENT_PATTERN = /\bcontent\s*=\s*["']([^"']+)["']/i;
|
|
@@ -10023,7 +10208,7 @@ function parseRefreshContent(value) {
|
|
|
10023
10208
|
};
|
|
10024
10209
|
}
|
|
10025
10210
|
registry.register({
|
|
10026
|
-
id:
|
|
10211
|
+
id: CHECK_ID31,
|
|
10027
10212
|
name: getCopy("checks.html-meta-redirect.name"),
|
|
10028
10213
|
description: getCopy("checks.html-meta-redirect.description"),
|
|
10029
10214
|
category: "security",
|
|
@@ -10046,8 +10231,8 @@ registry.register({
|
|
|
10046
10231
|
if (LOCAL_DEV_URL_PATTERN4.test(parsed.url)) continue;
|
|
10047
10232
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10048
10233
|
findings.push({
|
|
10049
|
-
id: `${
|
|
10050
|
-
checkId:
|
|
10234
|
+
id: `${CHECK_ID31}:${file.relativePath}:${line}:${column}`,
|
|
10235
|
+
checkId: CHECK_ID31,
|
|
10051
10236
|
category: "security",
|
|
10052
10237
|
severity: "medium",
|
|
10053
10238
|
message: getCopy("checks.html-meta-redirect.message", tagText),
|
|
@@ -10062,7 +10247,7 @@ registry.register({
|
|
|
10062
10247
|
return findings;
|
|
10063
10248
|
}
|
|
10064
10249
|
});
|
|
10065
|
-
var
|
|
10250
|
+
var CHECK_ID32 = "security.html-base-tag-hijack";
|
|
10066
10251
|
var REMEDIATION26 = getCopy("remediation.security.html-base-tag-hijack");
|
|
10067
10252
|
var BASE_PATTERN = /<base\b[^>]*?>/gi;
|
|
10068
10253
|
var HREF_PATTERN = /\bhref\s*=\s*["']([^"']+)["']/i;
|
|
@@ -10078,7 +10263,7 @@ function isOutsideHead(content, index) {
|
|
|
10078
10263
|
return false;
|
|
10079
10264
|
}
|
|
10080
10265
|
registry.register({
|
|
10081
|
-
id:
|
|
10266
|
+
id: CHECK_ID32,
|
|
10082
10267
|
name: getCopy("checks.html-base-tag-hijack.name"),
|
|
10083
10268
|
description: getCopy("checks.html-base-tag-hijack.description"),
|
|
10084
10269
|
category: "security",
|
|
@@ -10100,8 +10285,8 @@ registry.register({
|
|
|
10100
10285
|
if (!externalHref && !targetBlank && !outsideHead) continue;
|
|
10101
10286
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10102
10287
|
findings.push({
|
|
10103
|
-
id: `${
|
|
10104
|
-
checkId:
|
|
10288
|
+
id: `${CHECK_ID32}:${file.relativePath}:${line}:${column}`,
|
|
10289
|
+
checkId: CHECK_ID32,
|
|
10105
10290
|
category: "security",
|
|
10106
10291
|
severity: "medium",
|
|
10107
10292
|
message: getCopy("checks.html-base-tag-hijack.message", tagText),
|
|
@@ -10116,12 +10301,12 @@ registry.register({
|
|
|
10116
10301
|
return findings;
|
|
10117
10302
|
}
|
|
10118
10303
|
});
|
|
10119
|
-
var
|
|
10304
|
+
var CHECK_ID33 = "security.html-javascript-uri";
|
|
10120
10305
|
var REMEDIATION27 = getCopy("remediation.security.html-javascript-uri");
|
|
10121
10306
|
var TAG_PATTERN = /<(a|form|iframe|object|embed)\b[^>]*?\b(href|action|src|data)\s*=\s*["'](javascript:[^"']+)["'][^>]*?>/gi;
|
|
10122
10307
|
var VOID_PATTERN = /^javascript:\s*(?:void\s*\(?\s*0\s*\)?)(?:\s*;)?\s*$/i;
|
|
10123
10308
|
registry.register({
|
|
10124
|
-
id:
|
|
10309
|
+
id: CHECK_ID33,
|
|
10125
10310
|
name: getCopy("checks.html-javascript-uri.name"),
|
|
10126
10311
|
description: getCopy("checks.html-javascript-uri.description"),
|
|
10127
10312
|
category: "security",
|
|
@@ -10139,8 +10324,8 @@ registry.register({
|
|
|
10139
10324
|
if (VOID_PATTERN.test(jsUrl)) continue;
|
|
10140
10325
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10141
10326
|
findings.push({
|
|
10142
|
-
id: `${
|
|
10143
|
-
checkId:
|
|
10327
|
+
id: `${CHECK_ID33}:${file.relativePath}:${line}:${column}`,
|
|
10328
|
+
checkId: CHECK_ID33,
|
|
10144
10329
|
category: "security",
|
|
10145
10330
|
severity: "low",
|
|
10146
10331
|
message: getCopy("checks.html-javascript-uri.message", tagText),
|
|
@@ -10155,7 +10340,7 @@ registry.register({
|
|
|
10155
10340
|
return findings;
|
|
10156
10341
|
}
|
|
10157
10342
|
});
|
|
10158
|
-
var
|
|
10343
|
+
var CHECK_ID34 = "security.html-import-map-integrity";
|
|
10159
10344
|
var REMEDIATION28 = getCopy("remediation.security.html-import-map-integrity");
|
|
10160
10345
|
var IMPORTMAP_SCRIPT_PATTERN = /<script\b[^>]*\btype\s*=\s*["']importmap["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
10161
10346
|
var IMPORTMAP_SRC_PATTERN = /<script\b[^>]*\btype\s*=\s*["']importmap["'][^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi;
|
|
@@ -10176,8 +10361,8 @@ function isExternalUrl(value) {
|
|
|
10176
10361
|
function pushFinding5(findings, filePath, content, index, matchText) {
|
|
10177
10362
|
const { line, column } = lineAndColumn9(content, index);
|
|
10178
10363
|
findings.push({
|
|
10179
|
-
id: `${
|
|
10180
|
-
checkId:
|
|
10364
|
+
id: `${CHECK_ID34}:${filePath}:${line}:${column}`,
|
|
10365
|
+
checkId: CHECK_ID34,
|
|
10181
10366
|
category: "security",
|
|
10182
10367
|
severity: "medium",
|
|
10183
10368
|
message: getCopy("checks.html-import-map-integrity.message", matchText),
|
|
@@ -10202,7 +10387,7 @@ function scanImportMapValue(findings, filePath, content, value, contextLabel, in
|
|
|
10202
10387
|
}
|
|
10203
10388
|
}
|
|
10204
10389
|
registry.register({
|
|
10205
|
-
id:
|
|
10390
|
+
id: CHECK_ID34,
|
|
10206
10391
|
name: getCopy("checks.html-import-map-integrity.name"),
|
|
10207
10392
|
description: getCopy("checks.html-import-map-integrity.description"),
|
|
10208
10393
|
category: "security",
|
|
@@ -10235,7 +10420,7 @@ registry.register({
|
|
|
10235
10420
|
return findings;
|
|
10236
10421
|
}
|
|
10237
10422
|
});
|
|
10238
|
-
var
|
|
10423
|
+
var CHECK_ID35 = "security.css-data-exfiltration";
|
|
10239
10424
|
var REMEDIATION29 = getCopy("remediation.security.css-data-exfiltration");
|
|
10240
10425
|
var ATTR_SELECTOR_PATTERN = /\[[^\]]*(?:value|type|name|email|tel|address|cc-number|cc-exp)[^\]]*(?:\^=|\$=|\*=|~=|\|=)[^\]]*\]/i;
|
|
10241
10426
|
var EXTERNAL_HTTP_URL_PATTERN = /url\(\s*(["']?)http:\/\/[^"')]+\1\s*\)/i;
|
|
@@ -10253,8 +10438,8 @@ function lineAndColumn10(content, index) {
|
|
|
10253
10438
|
function pushFinding6(findings, filePath, content, index, matchText) {
|
|
10254
10439
|
const { line, column } = lineAndColumn10(content, index);
|
|
10255
10440
|
findings.push({
|
|
10256
|
-
id: `${
|
|
10257
|
-
checkId:
|
|
10441
|
+
id: `${CHECK_ID35}:${filePath}:${line}:${column}`,
|
|
10442
|
+
checkId: CHECK_ID35,
|
|
10258
10443
|
category: "security",
|
|
10259
10444
|
severity: "medium",
|
|
10260
10445
|
message: getCopy("checks.css-data-exfiltration.message", matchText),
|
|
@@ -10275,7 +10460,7 @@ function scanCss(findings, filePath, scanContent, originalContent, offset = 0) {
|
|
|
10275
10460
|
}
|
|
10276
10461
|
}
|
|
10277
10462
|
registry.register({
|
|
10278
|
-
id:
|
|
10463
|
+
id: CHECK_ID35,
|
|
10279
10464
|
name: getCopy("checks.css-data-exfiltration.name"),
|
|
10280
10465
|
description: getCopy("checks.css-data-exfiltration.description"),
|
|
10281
10466
|
category: "security",
|
|
@@ -10307,7 +10492,7 @@ registry.register({
|
|
|
10307
10492
|
return findings;
|
|
10308
10493
|
}
|
|
10309
10494
|
});
|
|
10310
|
-
var
|
|
10495
|
+
var CHECK_ID36 = "security.css-external-font-integrity";
|
|
10311
10496
|
var REMEDIATION30 = getCopy("remediation.security.css-external-font-integrity");
|
|
10312
10497
|
var FONT_FACE_PATTERN = /@font-face\b[\s\S]*?\}/gi;
|
|
10313
10498
|
var URL_PATTERN = /url\(\s*(["']?)([^"')]+)\1\s*\)/gi;
|
|
@@ -10339,7 +10524,7 @@ function isPublicCdnFont(url) {
|
|
|
10339
10524
|
}
|
|
10340
10525
|
}
|
|
10341
10526
|
registry.register({
|
|
10342
|
-
id:
|
|
10527
|
+
id: CHECK_ID36,
|
|
10343
10528
|
name: getCopy("checks.css-external-font-integrity.name"),
|
|
10344
10529
|
description: getCopy("checks.css-external-font-integrity.description"),
|
|
10345
10530
|
category: "security",
|
|
@@ -10360,8 +10545,8 @@ registry.register({
|
|
|
10360
10545
|
if (!isPublicCdnFont(url)) continue;
|
|
10361
10546
|
const { line, column } = lineAndColumn11(content, face.index ?? 0);
|
|
10362
10547
|
findings.push({
|
|
10363
|
-
id: `${
|
|
10364
|
-
checkId:
|
|
10548
|
+
id: `${CHECK_ID36}:${file.relativePath}:${line}:${column}`,
|
|
10549
|
+
checkId: CHECK_ID36,
|
|
10365
10550
|
category: "security",
|
|
10366
10551
|
severity: "medium",
|
|
10367
10552
|
message: getCopy("checks.css-external-font-integrity.message", match[0] ?? url),
|
|
@@ -10377,7 +10562,7 @@ registry.register({
|
|
|
10377
10562
|
return findings;
|
|
10378
10563
|
}
|
|
10379
10564
|
});
|
|
10380
|
-
var
|
|
10565
|
+
var CHECK_ID37 = "security.insecure-random";
|
|
10381
10566
|
var QUERIES8 = {
|
|
10382
10567
|
javascript: `
|
|
10383
10568
|
(call_expression
|
|
@@ -10446,7 +10631,7 @@ var QUERIES8 = {
|
|
|
10446
10631
|
};
|
|
10447
10632
|
registry.register(
|
|
10448
10633
|
createTreeSitterCheck({
|
|
10449
|
-
id:
|
|
10634
|
+
id: CHECK_ID37,
|
|
10450
10635
|
nameKey: "checks.insecure-random.name",
|
|
10451
10636
|
descriptionKey: "checks.insecure-random.description",
|
|
10452
10637
|
category: "security",
|
|
@@ -10458,7 +10643,7 @@ registry.register(
|
|
|
10458
10643
|
remediationKey: "remediation.security.insecure-random"
|
|
10459
10644
|
})
|
|
10460
10645
|
);
|
|
10461
|
-
var
|
|
10646
|
+
var CHECK_ID38 = "security.weak-key-entropy";
|
|
10462
10647
|
var REMEDIATION31 = getCopy("remediation.security.weak-key-entropy");
|
|
10463
10648
|
var RANDOM_BYTES_PATTERN = /randomBytes\s*\(\s*(\d+)\s*\)/g;
|
|
10464
10649
|
var WEAK_KEY_CONTEXT = /(?:key|token|secret|id|license|password|session|csrf|nonce|salt|iv)/i;
|
|
@@ -10471,7 +10656,7 @@ function lineAndColumn12(content, index) {
|
|
|
10471
10656
|
};
|
|
10472
10657
|
}
|
|
10473
10658
|
registry.register({
|
|
10474
|
-
id:
|
|
10659
|
+
id: CHECK_ID38,
|
|
10475
10660
|
name: getCopy("checks.weak-key-entropy.name"),
|
|
10476
10661
|
description: getCopy("checks.weak-key-entropy.description"),
|
|
10477
10662
|
category: "security",
|
|
@@ -10493,8 +10678,8 @@ registry.register({
|
|
|
10493
10678
|
if (!WEAK_KEY_CONTEXT.test(lineContent) && !WEAK_KEY_CONTEXT.test(content.slice(Math.max(0, matchIndex - 200), matchIndex))) continue;
|
|
10494
10679
|
const { line, column } = lineAndColumn12(content, matchIndex);
|
|
10495
10680
|
findings.push({
|
|
10496
|
-
id: `${
|
|
10497
|
-
checkId:
|
|
10681
|
+
id: `${CHECK_ID38}:${file.relativePath}:${line}:${column}`,
|
|
10682
|
+
checkId: CHECK_ID38,
|
|
10498
10683
|
category: "security",
|
|
10499
10684
|
severity: "high",
|
|
10500
10685
|
message: getCopy("checks.weak-key-entropy.message", match[0], String(byteCount)),
|
|
@@ -10509,7 +10694,7 @@ registry.register({
|
|
|
10509
10694
|
return findings;
|
|
10510
10695
|
}
|
|
10511
10696
|
});
|
|
10512
|
-
var
|
|
10697
|
+
var CHECK_ID39 = "security.insecure-api-url-default";
|
|
10513
10698
|
var REMEDIATION32 = getCopy("remediation.security.insecure-api-url-default");
|
|
10514
10699
|
var HTTP_URL_DEFAULT_PATTERN = /(?:['"`])(https?:\/\/[^\s'"`]+)(?:['"`])/g;
|
|
10515
10700
|
var API_CONTEXT = /(?:api[_-]?url|base[_-]?url|endpoint|server|host|webhook|resend|paddle|stripe|sendgrid|mailgun|postmark)/i;
|
|
@@ -10524,7 +10709,7 @@ function lineAndColumn13(content, index) {
|
|
|
10524
10709
|
};
|
|
10525
10710
|
}
|
|
10526
10711
|
registry.register({
|
|
10527
|
-
id:
|
|
10712
|
+
id: CHECK_ID39,
|
|
10528
10713
|
name: getCopy("checks.insecure-api-url-default.name"),
|
|
10529
10714
|
description: getCopy("checks.insecure-api-url-default.description"),
|
|
10530
10715
|
category: "security",
|
|
@@ -10549,8 +10734,8 @@ registry.register({
|
|
|
10549
10734
|
if (!isEnvDefault && !API_CONTEXT.test(matchLine)) continue;
|
|
10550
10735
|
const { line, column } = lineAndColumn13(content, matchIndex);
|
|
10551
10736
|
findings.push({
|
|
10552
|
-
id: `${
|
|
10553
|
-
checkId:
|
|
10737
|
+
id: `${CHECK_ID39}:${file.relativePath}:${line}:${column}`,
|
|
10738
|
+
checkId: CHECK_ID39,
|
|
10554
10739
|
category: "security",
|
|
10555
10740
|
severity: "high",
|
|
10556
10741
|
message: getCopy("checks.insecure-api-url-default.message", url),
|
|
@@ -10565,7 +10750,7 @@ registry.register({
|
|
|
10565
10750
|
return findings;
|
|
10566
10751
|
}
|
|
10567
10752
|
});
|
|
10568
|
-
var
|
|
10753
|
+
var CHECK_ID40 = "security.dynamic-sql-columns";
|
|
10569
10754
|
var REMEDIATION33 = getCopy("remediation.security.dynamic-sql-columns");
|
|
10570
10755
|
var SQL_SET_INTERPOLATION = /\bSET\s+.*\$\{[^}]*\}/gis;
|
|
10571
10756
|
var SQL_SET_TEMPLATE = /\bSET\s+.*`[^`]*\$\{[^}]*\}[^`]*`/gis;
|
|
@@ -10579,7 +10764,7 @@ function lineAndColumn14(content, index) {
|
|
|
10579
10764
|
};
|
|
10580
10765
|
}
|
|
10581
10766
|
registry.register({
|
|
10582
|
-
id:
|
|
10767
|
+
id: CHECK_ID40,
|
|
10583
10768
|
name: getCopy("checks.dynamic-sql-columns.name"),
|
|
10584
10769
|
description: getCopy("checks.dynamic-sql-columns.description"),
|
|
10585
10770
|
category: "security",
|
|
@@ -10600,11 +10785,11 @@ registry.register({
|
|
|
10600
10785
|
for (const match of content.matchAll(regex)) {
|
|
10601
10786
|
const matchIndex = match.index ?? 0;
|
|
10602
10787
|
const { line, column } = lineAndColumn14(content, matchIndex);
|
|
10603
|
-
const dedupKey = `${
|
|
10788
|
+
const dedupKey = `${CHECK_ID40}:${file.relativePath}:${line}`;
|
|
10604
10789
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10605
10790
|
findings.push({
|
|
10606
10791
|
id: dedupKey,
|
|
10607
|
-
checkId:
|
|
10792
|
+
checkId: CHECK_ID40,
|
|
10608
10793
|
category: "security",
|
|
10609
10794
|
severity: "medium",
|
|
10610
10795
|
message: getCopy("checks.dynamic-sql-columns.message", label),
|
|
@@ -10620,7 +10805,7 @@ registry.register({
|
|
|
10620
10805
|
return findings;
|
|
10621
10806
|
}
|
|
10622
10807
|
});
|
|
10623
|
-
var
|
|
10808
|
+
var CHECK_ID41 = "security.weak-email-validation";
|
|
10624
10809
|
var REMEDIATION34 = getCopy("remediation.security.weak-email-validation");
|
|
10625
10810
|
var WEAK_EMAIL_REGEX_LINE = /\/\^[^/]*@[^/]*\\\.[^/]{0,4}\$\/[gimsuy]*/g;
|
|
10626
10811
|
var LOOSE_REGEX_CLASS = /\[\^?\\?s@\]\+@\[\^?\\?s@\]\+\\\.\[\^?\\?s@\]\+/g;
|
|
@@ -10634,7 +10819,7 @@ function lineAndColumn15(content, index) {
|
|
|
10634
10819
|
};
|
|
10635
10820
|
}
|
|
10636
10821
|
registry.register({
|
|
10637
|
-
id:
|
|
10822
|
+
id: CHECK_ID41,
|
|
10638
10823
|
name: getCopy("checks.weak-email-validation.name"),
|
|
10639
10824
|
description: getCopy("checks.weak-email-validation.description"),
|
|
10640
10825
|
category: "security",
|
|
@@ -10655,11 +10840,11 @@ registry.register({
|
|
|
10655
10840
|
for (const match of content.matchAll(regex)) {
|
|
10656
10841
|
const matchIndex = match.index ?? 0;
|
|
10657
10842
|
const { line, column } = lineAndColumn15(content, matchIndex);
|
|
10658
|
-
const dedupKey = `${
|
|
10843
|
+
const dedupKey = `${CHECK_ID41}:${file.relativePath}:${line}`;
|
|
10659
10844
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10660
10845
|
findings.push({
|
|
10661
10846
|
id: dedupKey,
|
|
10662
|
-
checkId:
|
|
10847
|
+
checkId: CHECK_ID41,
|
|
10663
10848
|
category: "security",
|
|
10664
10849
|
severity: "medium",
|
|
10665
10850
|
message: getCopy("checks.weak-email-validation.message", `${match[0]} (${label})`),
|
|
@@ -10675,7 +10860,7 @@ registry.register({
|
|
|
10675
10860
|
return findings;
|
|
10676
10861
|
}
|
|
10677
10862
|
});
|
|
10678
|
-
var
|
|
10863
|
+
var CHECK_ID42 = "security.idempotency-key-leak";
|
|
10679
10864
|
var REMEDIATION35 = getCopy("remediation.security.idempotency-key-leak");
|
|
10680
10865
|
var IDEMPOTENCY_CONTEXT = /idempotency[_-]?key|idempotency[_-]?token/i;
|
|
10681
10866
|
var TEMPLATE_LITERAL_INTERPOLATION = new RegExp("`[^`]*\\$\\{[^}]+\\}[^`]*`", "g");
|
|
@@ -10689,7 +10874,7 @@ function lineAndColumn16(content, index) {
|
|
|
10689
10874
|
};
|
|
10690
10875
|
}
|
|
10691
10876
|
registry.register({
|
|
10692
|
-
id:
|
|
10877
|
+
id: CHECK_ID42,
|
|
10693
10878
|
name: getCopy("checks.idempotency-key-leak.name"),
|
|
10694
10879
|
description: getCopy("checks.idempotency-key-leak.description"),
|
|
10695
10880
|
category: "security",
|
|
@@ -10708,11 +10893,11 @@ registry.register({
|
|
|
10708
10893
|
if (!STRUCTURED_ID_PATTERN.test(templateStr)) continue;
|
|
10709
10894
|
const matchIndex = match.index ?? 0;
|
|
10710
10895
|
const { line, column } = lineAndColumn16(content, matchIndex);
|
|
10711
|
-
const dedupKey = `${
|
|
10896
|
+
const dedupKey = `${CHECK_ID42}:${file.relativePath}:${line}`;
|
|
10712
10897
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10713
10898
|
findings.push({
|
|
10714
10899
|
id: dedupKey,
|
|
10715
|
-
checkId:
|
|
10900
|
+
checkId: CHECK_ID42,
|
|
10716
10901
|
category: "security",
|
|
10717
10902
|
severity: "medium",
|
|
10718
10903
|
message: getCopy("checks.idempotency-key-leak.message", templateStr.slice(0, 80)),
|
|
@@ -10727,7 +10912,7 @@ registry.register({
|
|
|
10727
10912
|
return findings;
|
|
10728
10913
|
}
|
|
10729
10914
|
});
|
|
10730
|
-
var
|
|
10915
|
+
var CHECK_ID43 = "data-exposure.plaintext-secret-in-email";
|
|
10731
10916
|
var REMEDIATION36 = getCopy("remediation.data-exposure.plaintext-secret-in-email");
|
|
10732
10917
|
var EMAIL_FILE_PATTERN = /email|mail|notify|notification/i;
|
|
10733
10918
|
var SECRET_IN_TEXT_PATTERN = /(?:license[_-]?key|api[_-]?key|secret[_-]?key|token|password|auth[_-]?code|verification[_-]?code)\b/i;
|
|
@@ -10741,7 +10926,7 @@ function lineAndColumn17(content, index) {
|
|
|
10741
10926
|
};
|
|
10742
10927
|
}
|
|
10743
10928
|
registry.register({
|
|
10744
|
-
id:
|
|
10929
|
+
id: CHECK_ID43,
|
|
10745
10930
|
name: getCopy("checks.plaintext-secret-in-email.name"),
|
|
10746
10931
|
description: getCopy("checks.plaintext-secret-in-email.description"),
|
|
10747
10932
|
category: "data-exposure",
|
|
@@ -10763,8 +10948,8 @@ registry.register({
|
|
|
10763
10948
|
const { line: lineNum, column } = lineAndColumn17(content, lineIndex);
|
|
10764
10949
|
const secretMatch = SECRET_IN_TEXT_PATTERN.exec(lineText2);
|
|
10765
10950
|
findings.push({
|
|
10766
|
-
id: `${
|
|
10767
|
-
checkId:
|
|
10951
|
+
id: `${CHECK_ID43}:${file.relativePath}:${lineNum}:${column}`,
|
|
10952
|
+
checkId: CHECK_ID43,
|
|
10768
10953
|
category: "data-exposure",
|
|
10769
10954
|
severity: "medium",
|
|
10770
10955
|
message: getCopy("checks.plaintext-secret-in-email.message", secretMatch?.[0] ?? "secret"),
|
|
@@ -12974,7 +13159,7 @@ registry.register(
|
|
|
12974
13159
|
attribution: "CWE-94"
|
|
12975
13160
|
})
|
|
12976
13161
|
);
|
|
12977
|
-
var
|
|
13162
|
+
var CHECK_ID44 = "security.missing-csrf-protection";
|
|
12978
13163
|
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
13164
|
var QUERIES9 = {
|
|
12980
13165
|
php: `
|
|
@@ -13031,7 +13216,7 @@ async function hasProjectCsrfProtection(files) {
|
|
|
13031
13216
|
return false;
|
|
13032
13217
|
}
|
|
13033
13218
|
var check = {
|
|
13034
|
-
id:
|
|
13219
|
+
id: CHECK_ID44,
|
|
13035
13220
|
name: resolveCopy3("checks.missing-csrf-protection.name"),
|
|
13036
13221
|
description: resolveCopy3("checks.missing-csrf-protection.description"),
|
|
13037
13222
|
category: "security",
|
|
@@ -13071,8 +13256,8 @@ var check = {
|
|
|
13071
13256
|
const sample = eligible.find((f) => f.language === lang);
|
|
13072
13257
|
if (!sample) continue;
|
|
13073
13258
|
findings.push({
|
|
13074
|
-
id: `${
|
|
13075
|
-
checkId:
|
|
13259
|
+
id: `${CHECK_ID44}:${sample.relativePath}:degraded:${lang}`,
|
|
13260
|
+
checkId: CHECK_ID44,
|
|
13076
13261
|
category: "security",
|
|
13077
13262
|
severity: "info",
|
|
13078
13263
|
message: resolveCopy3("checks.missing-csrf-protection.degraded", sample.relativePath, lang),
|
|
@@ -13093,8 +13278,8 @@ var check = {
|
|
|
13093
13278
|
handlerCount > 1 ? `${handlerCount} state-changing handlers (e.g. ${exampleRoute})` : exampleRoute
|
|
13094
13279
|
);
|
|
13095
13280
|
const finding = {
|
|
13096
|
-
id: `${
|
|
13097
|
-
checkId:
|
|
13281
|
+
id: `${CHECK_ID44}:project:${anchor.relativePath}:1`,
|
|
13282
|
+
checkId: CHECK_ID44,
|
|
13098
13283
|
category: "security",
|
|
13099
13284
|
severity: "medium",
|
|
13100
13285
|
message,
|
|
@@ -13109,7 +13294,7 @@ var check = {
|
|
|
13109
13294
|
}
|
|
13110
13295
|
};
|
|
13111
13296
|
registry.register(check);
|
|
13112
|
-
var
|
|
13297
|
+
var CHECK_ID45 = "security.weak-session-id";
|
|
13113
13298
|
var QUERIES10 = {
|
|
13114
13299
|
php: `
|
|
13115
13300
|
(function_call_expression
|
|
@@ -13196,7 +13381,7 @@ var QUERIES10 = {
|
|
|
13196
13381
|
};
|
|
13197
13382
|
registry.register(
|
|
13198
13383
|
createTreeSitterCheck({
|
|
13199
|
-
id:
|
|
13384
|
+
id: CHECK_ID45,
|
|
13200
13385
|
nameKey: "checks.weak-session-id.name",
|
|
13201
13386
|
descriptionKey: "checks.weak-session-id.description",
|
|
13202
13387
|
category: "security",
|
|
@@ -13214,7 +13399,7 @@ registry.register(
|
|
|
13214
13399
|
}
|
|
13215
13400
|
})
|
|
13216
13401
|
);
|
|
13217
|
-
var
|
|
13402
|
+
var CHECK_ID46 = "security.rails-mass-assignment";
|
|
13218
13403
|
var QUERIES11 = {
|
|
13219
13404
|
ruby: `
|
|
13220
13405
|
(call
|
|
@@ -13237,7 +13422,7 @@ var QUERIES11 = {
|
|
|
13237
13422
|
};
|
|
13238
13423
|
registry.register(
|
|
13239
13424
|
createTreeSitterCheck({
|
|
13240
|
-
id:
|
|
13425
|
+
id: CHECK_ID46,
|
|
13241
13426
|
nameKey: "checks.rails-mass-assignment.name",
|
|
13242
13427
|
descriptionKey: "checks.rails-mass-assignment.description",
|
|
13243
13428
|
category: "security",
|
|
@@ -13249,7 +13434,7 @@ registry.register(
|
|
|
13249
13434
|
remediationKey: "remediation.security.rails-mass-assignment"
|
|
13250
13435
|
})
|
|
13251
13436
|
);
|
|
13252
|
-
var
|
|
13437
|
+
var CHECK_ID47 = "security.rails-insecure-session-config";
|
|
13253
13438
|
var REMEDIATION37 = getCopy("remediation.security.rails-insecure-session-config");
|
|
13254
13439
|
var SESSION_STORE_PATH = /config\/initializers\/session_store\.rb$/;
|
|
13255
13440
|
function hasSecureFlag(content) {
|
|
@@ -13262,7 +13447,7 @@ function hasSameSiteFlag(content) {
|
|
|
13262
13447
|
return /\bsame_site:\s*:(strict|lax)\b/.test(content);
|
|
13263
13448
|
}
|
|
13264
13449
|
registry.register({
|
|
13265
|
-
id:
|
|
13450
|
+
id: CHECK_ID47,
|
|
13266
13451
|
name: getCopy("checks.rails-insecure-session-config.name"),
|
|
13267
13452
|
description: getCopy("checks.rails-insecure-session-config.description"),
|
|
13268
13453
|
category: "security",
|
|
@@ -13282,8 +13467,8 @@ registry.register({
|
|
|
13282
13467
|
if (!hasSameSiteFlag(content)) missing.push("same_site: :strict or :lax");
|
|
13283
13468
|
if (missing.length > 0) {
|
|
13284
13469
|
findings.push({
|
|
13285
|
-
id: `${
|
|
13286
|
-
checkId:
|
|
13470
|
+
id: `${CHECK_ID47}:${file.relativePath}:1`,
|
|
13471
|
+
checkId: CHECK_ID47,
|
|
13287
13472
|
category: "security",
|
|
13288
13473
|
severity: "medium",
|
|
13289
13474
|
message: getCopy(
|
|
@@ -13300,7 +13485,7 @@ registry.register({
|
|
|
13300
13485
|
return findings;
|
|
13301
13486
|
}
|
|
13302
13487
|
});
|
|
13303
|
-
var
|
|
13488
|
+
var CHECK_ID48 = "security.xxe-surface";
|
|
13304
13489
|
var LIBXML_PARSE_QUERY = `
|
|
13305
13490
|
(call_expression
|
|
13306
13491
|
function: (member_expression
|
|
@@ -13347,7 +13532,7 @@ var QUERIES12 = {
|
|
|
13347
13532
|
};
|
|
13348
13533
|
registry.register(
|
|
13349
13534
|
createTreeSitterCheck({
|
|
13350
|
-
id:
|
|
13535
|
+
id: CHECK_ID48,
|
|
13351
13536
|
nameKey: "checks.xxe-surface.name",
|
|
13352
13537
|
descriptionKey: "checks.xxe-surface.description",
|
|
13353
13538
|
category: "security",
|
|
@@ -13546,12 +13731,13 @@ registry.register({
|
|
|
13546
13731
|
return runLLMCheck(ctx, promptTemplate22);
|
|
13547
13732
|
}
|
|
13548
13733
|
});
|
|
13549
|
-
var PROMPT_VERSION23 = "1.
|
|
13734
|
+
var PROMPT_VERSION23 = "1.1.0";
|
|
13550
13735
|
var fallbackPatterns23 = [
|
|
13551
13736
|
{
|
|
13552
13737
|
regex: /while\s*\(\s*(?:true|1)\s*\)|while\s+True\s*:/i,
|
|
13553
13738
|
message: "Infinite loop found \u2014 verify retry logic has a maximum attempt limit",
|
|
13554
|
-
severity: "medium"
|
|
13739
|
+
severity: "medium",
|
|
13740
|
+
skipCommentLines: true
|
|
13555
13741
|
}
|
|
13556
13742
|
];
|
|
13557
13743
|
async function buildPrompt23(sourceFiles) {
|
|
@@ -13613,12 +13799,13 @@ registry.register({
|
|
|
13613
13799
|
return [...bashFindings, ...llmFindings];
|
|
13614
13800
|
}
|
|
13615
13801
|
});
|
|
13616
|
-
var PROMPT_VERSION24 = "1.
|
|
13802
|
+
var PROMPT_VERSION24 = "1.2.0";
|
|
13617
13803
|
var fallbackPatterns24 = [
|
|
13618
13804
|
{
|
|
13619
13805
|
regex: /console\.(?:log|error|warn)\b/,
|
|
13620
13806
|
message: "Unstructured logging detected \u2014 verify observability hooks are present",
|
|
13621
|
-
severity: "medium"
|
|
13807
|
+
severity: "medium",
|
|
13808
|
+
skipCommentLines: true
|
|
13622
13809
|
}
|
|
13623
13810
|
];
|
|
13624
13811
|
async function buildPrompt24(sourceFiles) {
|
|
@@ -13841,12 +14028,13 @@ registry.register({
|
|
|
13841
14028
|
return runLLMCheck(ctx, promptTemplate27);
|
|
13842
14029
|
}
|
|
13843
14030
|
});
|
|
13844
|
-
var PROMPT_VERSION28 = "1.
|
|
14031
|
+
var PROMPT_VERSION28 = "1.1.0";
|
|
13845
14032
|
var fallbackPatterns28 = [
|
|
13846
14033
|
{
|
|
13847
14034
|
regex: /\b(?:fingerprint|deviceId|visitorId|trackingId|correlationId|analyticsId)\b/i,
|
|
13848
14035
|
message: "Derived identifier may link records across sessions or users",
|
|
13849
|
-
severity: "medium"
|
|
14036
|
+
severity: "medium",
|
|
14037
|
+
skipCommentLines: true
|
|
13850
14038
|
},
|
|
13851
14039
|
{
|
|
13852
14040
|
regex: /\bhash\s*\(\s*(?:email|phone|userId|id)\s*\)/i,
|
|
@@ -13880,15 +14068,64 @@ var promptTemplate28 = {
|
|
|
13880
14068
|
parseResponse: parseResponse28,
|
|
13881
14069
|
fallbackPatterns: fallbackPatterns28
|
|
13882
14070
|
};
|
|
14071
|
+
var CHECK_ID49 = "data-exposure.derived-identifiers";
|
|
14072
|
+
var DERIVED_ID_REGEX = /\b(?:fingerprint|device.?id|visitor.?id|tracking.?id|correlation.?id|analytics.?id)\b/i;
|
|
14073
|
+
var DERIVED_TS_QUERIES = {
|
|
14074
|
+
javascript: `
|
|
14075
|
+
(lexical_declaration (variable_declarator name: (identifier) @name)) @match
|
|
14076
|
+
(pair key: (property_identifier) @name) @match
|
|
14077
|
+
(assignment_expression left: (identifier) @name) @match
|
|
14078
|
+
`,
|
|
14079
|
+
typescript: `
|
|
14080
|
+
(lexical_declaration (variable_declarator name: (identifier) @name)) @match
|
|
14081
|
+
(pair key: (property_identifier) @name) @match
|
|
14082
|
+
(property_signature name: (property_identifier) @name) @match
|
|
14083
|
+
`,
|
|
14084
|
+
tsx: `
|
|
14085
|
+
(lexical_declaration (variable_declarator name: (identifier) @name)) @match
|
|
14086
|
+
(pair key: (property_identifier) @name) @match
|
|
14087
|
+
(property_signature name: (property_identifier) @name) @match
|
|
14088
|
+
`,
|
|
14089
|
+
go: `
|
|
14090
|
+
(short_var_declaration left: (expression_list (identifier) @name)) @match
|
|
14091
|
+
(var_declaration (var_spec name: (identifier) @name)) @match
|
|
14092
|
+
`,
|
|
14093
|
+
python: `
|
|
14094
|
+
(assignment left: (identifier) @name) @match
|
|
14095
|
+
`,
|
|
14096
|
+
java: `
|
|
14097
|
+
(local_variable_declaration declarator: (variable_declarator name: (identifier) @name)) @match
|
|
14098
|
+
(field_declaration declarator: (variable_declarator name: (identifier) @name)) @match
|
|
14099
|
+
`
|
|
14100
|
+
};
|
|
14101
|
+
var derivedIdAstCheck = createTreeSitterCheck({
|
|
14102
|
+
id: CHECK_ID49,
|
|
14103
|
+
nameKey: "checks.derived-identifiers.name",
|
|
14104
|
+
descriptionKey: "checks.derived-identifiers.description",
|
|
14105
|
+
category: "data-exposure",
|
|
14106
|
+
severity: "medium",
|
|
14107
|
+
minTier: "pro",
|
|
14108
|
+
queries: DERIVED_TS_QUERIES,
|
|
14109
|
+
messageKey: "checks.derived-identifiers.message",
|
|
14110
|
+
degradedMessageKey: "checks.derived-identifiers.degraded",
|
|
14111
|
+
remediationKey: "remediation.data-exposure.derived-identifiers",
|
|
14112
|
+
fileFilter: (file) => !isMechanicalCheckExcludedPath(file.relativePath),
|
|
14113
|
+
matchFilter: (captures) => DERIVED_ID_REGEX.test(captures.name ?? ""),
|
|
14114
|
+
messageFactory: (captures) => getCopy("checks.derived-identifiers.message", captures.name ?? "unknown")
|
|
14115
|
+
});
|
|
13883
14116
|
registry.register({
|
|
13884
|
-
id:
|
|
14117
|
+
id: CHECK_ID49,
|
|
13885
14118
|
name: getCopy("checks.derived-identifiers.name"),
|
|
13886
14119
|
description: getCopy("checks.derived-identifiers.description"),
|
|
13887
14120
|
category: "data-exposure",
|
|
13888
14121
|
severity: "medium",
|
|
13889
14122
|
minTier: "pro",
|
|
13890
14123
|
async run(ctx) {
|
|
13891
|
-
|
|
14124
|
+
const [llmFindings, astFindings] = await Promise.all([
|
|
14125
|
+
runLLMCheck(ctx, promptTemplate28),
|
|
14126
|
+
derivedIdAstCheck.run(ctx)
|
|
14127
|
+
]);
|
|
14128
|
+
return mergeFindings(astFindings, llmFindings);
|
|
13892
14129
|
}
|
|
13893
14130
|
});
|
|
13894
14131
|
var PROMPT_VERSION29 = "1.0.0";
|
|
@@ -13954,12 +14191,12 @@ registry.register({
|
|
|
13954
14191
|
return [...bashFindings, ...llmFindings];
|
|
13955
14192
|
}
|
|
13956
14193
|
});
|
|
13957
|
-
var
|
|
14194
|
+
var CHECK_ID50 = "security.permissive-grants";
|
|
13958
14195
|
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
14196
|
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
14197
|
var REMEDIATION38 = getCopy("remediation.security.permissive-grants");
|
|
13961
14198
|
var permissiveGrantsCheck = {
|
|
13962
|
-
id:
|
|
14199
|
+
id: CHECK_ID50,
|
|
13963
14200
|
name: getCopy("checks.permissive-grants.name"),
|
|
13964
14201
|
description: getCopy("checks.permissive-grants.description"),
|
|
13965
14202
|
category: "security",
|
|
@@ -13976,8 +14213,8 @@ var permissiveGrantsCheck = {
|
|
|
13976
14213
|
const beforeMatch = content.slice(0, match.index);
|
|
13977
14214
|
const lineNum = beforeMatch.split("\n").length;
|
|
13978
14215
|
findings.push({
|
|
13979
|
-
id: `${
|
|
13980
|
-
checkId:
|
|
14216
|
+
id: `${CHECK_ID50}:${file.relativePath}:${lineNum}:1`,
|
|
14217
|
+
checkId: CHECK_ID50,
|
|
13981
14218
|
category: "security",
|
|
13982
14219
|
severity: "high",
|
|
13983
14220
|
message: getCopy("checks.permissive-grants.message", match[0].trim()),
|
|
@@ -13994,8 +14231,8 @@ var permissiveGrantsCheck = {
|
|
|
13994
14231
|
const beforeMatch = content.slice(0, altMatch.index);
|
|
13995
14232
|
const lineNum = beforeMatch.split("\n").length;
|
|
13996
14233
|
findings.push({
|
|
13997
|
-
id: `${
|
|
13998
|
-
checkId:
|
|
14234
|
+
id: `${CHECK_ID50}:${file.relativePath}:${lineNum}:1`,
|
|
14235
|
+
checkId: CHECK_ID50,
|
|
13999
14236
|
category: "security",
|
|
14000
14237
|
severity: "high",
|
|
14001
14238
|
message: getCopy("checks.permissive-grants.message", altMatch[0].trim()),
|
|
@@ -14035,10 +14272,10 @@ function classifySqlDialect(content) {
|
|
|
14035
14272
|
}
|
|
14036
14273
|
return "unknown";
|
|
14037
14274
|
}
|
|
14038
|
-
var
|
|
14275
|
+
var CHECK_ID51 = "security.sql-rls-static";
|
|
14039
14276
|
var REMEDIATION39 = getCopy("remediation.security.sql-rls-static");
|
|
14040
14277
|
var sqlRlsStaticCheck = {
|
|
14041
|
-
id:
|
|
14278
|
+
id: CHECK_ID51,
|
|
14042
14279
|
name: getCopy("checks.sql-rls-static.name"),
|
|
14043
14280
|
description: getCopy("checks.sql-rls-static.description"),
|
|
14044
14281
|
category: "security",
|
|
@@ -14093,8 +14330,8 @@ var sqlRlsStaticCheck = {
|
|
|
14093
14330
|
const isRlsEnabled = enabledTables.has(table.name);
|
|
14094
14331
|
if (!isRlsEnabled) {
|
|
14095
14332
|
findings.push({
|
|
14096
|
-
id: `${
|
|
14097
|
-
checkId:
|
|
14333
|
+
id: `${CHECK_ID51}:${file.relativePath}:${table.line}:1:missing-rls`,
|
|
14334
|
+
checkId: CHECK_ID51,
|
|
14098
14335
|
category: "security",
|
|
14099
14336
|
severity: "high",
|
|
14100
14337
|
message: getCopy("checks.sql-rls-static.message", `Table "${table.name}" contains tenant columns but Row-Level Security is not enabled.`),
|
|
@@ -14108,8 +14345,8 @@ var sqlRlsStaticCheck = {
|
|
|
14108
14345
|
const isRlsForced = forcedTables.has(table.name);
|
|
14109
14346
|
if (!isRlsForced) {
|
|
14110
14347
|
findings.push({
|
|
14111
|
-
id: `${
|
|
14112
|
-
checkId:
|
|
14348
|
+
id: `${CHECK_ID51}:${file.relativePath}:${table.line}:1:missing-force-rls`,
|
|
14349
|
+
checkId: CHECK_ID51,
|
|
14113
14350
|
category: "security",
|
|
14114
14351
|
severity: "high",
|
|
14115
14352
|
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 +14370,8 @@ var sqlRlsStaticCheck = {
|
|
|
14133
14370
|
const beforeMatch = content.slice(0, policyMatch.index);
|
|
14134
14371
|
const lineNum = beforeMatch.split("\n").length;
|
|
14135
14372
|
findings.push({
|
|
14136
|
-
id: `${
|
|
14137
|
-
checkId:
|
|
14373
|
+
id: `${CHECK_ID51}:${file.relativePath}:${lineNum}:1:weak-policy`,
|
|
14374
|
+
checkId: CHECK_ID51,
|
|
14138
14375
|
category: "security",
|
|
14139
14376
|
severity: "high",
|
|
14140
14377
|
message: getCopy("checks.sql-rls-static.message", `Policy ${policyName} on table "${tableName}" uses an unrestrictive tautology (like true or 1=1).`),
|
|
@@ -14152,10 +14389,10 @@ var sqlRlsStaticCheck = {
|
|
|
14152
14389
|
}
|
|
14153
14390
|
};
|
|
14154
14391
|
registry.register(sqlRlsStaticCheck);
|
|
14155
|
-
var
|
|
14392
|
+
var CHECK_ID52 = "security.sql-security-definer-search-path";
|
|
14156
14393
|
var REMEDIATION40 = getCopy("remediation.security.sql-security-definer-search-path");
|
|
14157
14394
|
var sqlSecurityDefinerSearchPathCheck = {
|
|
14158
|
-
id:
|
|
14395
|
+
id: CHECK_ID52,
|
|
14159
14396
|
name: getCopy("checks.sql-security-definer-search-path.name"),
|
|
14160
14397
|
description: getCopy("checks.sql-security-definer-search-path.description"),
|
|
14161
14398
|
category: "security",
|
|
@@ -14181,8 +14418,8 @@ var sqlSecurityDefinerSearchPathCheck = {
|
|
|
14181
14418
|
const beforeMatch = content.slice(0, match.index);
|
|
14182
14419
|
const lineNum = beforeMatch.split("\n").length;
|
|
14183
14420
|
findings.push({
|
|
14184
|
-
id: `${
|
|
14185
|
-
checkId:
|
|
14421
|
+
id: `${CHECK_ID52}:${file.relativePath}:${lineNum}:1`,
|
|
14422
|
+
checkId: CHECK_ID52,
|
|
14186
14423
|
category: "security",
|
|
14187
14424
|
severity: "high",
|
|
14188
14425
|
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 +14438,7 @@ var sqlSecurityDefinerSearchPathCheck = {
|
|
|
14201
14438
|
}
|
|
14202
14439
|
};
|
|
14203
14440
|
registry.register(sqlSecurityDefinerSearchPathCheck);
|
|
14204
|
-
var
|
|
14441
|
+
var CHECK_ID53 = "resilience.missing-transaction-guard";
|
|
14205
14442
|
var REMEDIATION41 = getCopy("remediation.resilience.missing-transaction-guard");
|
|
14206
14443
|
var MUTATION_KEYWORDS = [
|
|
14207
14444
|
/\binsert\s+into\b/i,
|
|
@@ -14214,7 +14451,7 @@ var MUTATION_KEYWORDS = [
|
|
|
14214
14451
|
/\bcreate\s+table\s+\w+\s+as\b/i
|
|
14215
14452
|
];
|
|
14216
14453
|
var missingTransactionGuardCheck = {
|
|
14217
|
-
id:
|
|
14454
|
+
id: CHECK_ID53,
|
|
14218
14455
|
name: getCopy("checks.missing-transaction-guard.name"),
|
|
14219
14456
|
description: getCopy("checks.missing-transaction-guard.description"),
|
|
14220
14457
|
category: "resilience",
|
|
@@ -14244,8 +14481,8 @@ var missingTransactionGuardCheck = {
|
|
|
14244
14481
|
}
|
|
14245
14482
|
if (mutationCount > 5) {
|
|
14246
14483
|
findings.push({
|
|
14247
|
-
id: `${
|
|
14248
|
-
checkId:
|
|
14484
|
+
id: `${CHECK_ID53}:${file.relativePath}:1:1`,
|
|
14485
|
+
checkId: CHECK_ID53,
|
|
14249
14486
|
category: "resilience",
|
|
14250
14487
|
severity: "medium",
|
|
14251
14488
|
message: getCopy("checks.missing-transaction-guard.message"),
|
|
@@ -14260,10 +14497,10 @@ var missingTransactionGuardCheck = {
|
|
|
14260
14497
|
}
|
|
14261
14498
|
};
|
|
14262
14499
|
registry.register(missingTransactionGuardCheck);
|
|
14263
|
-
var
|
|
14500
|
+
var CHECK_ID54 = "resilience.destructive-sql-migration";
|
|
14264
14501
|
var REMEDIATION42 = getCopy("remediation.resilience.destructive-sql-migration");
|
|
14265
14502
|
var destructiveSqlMigrationCheck = {
|
|
14266
|
-
id:
|
|
14503
|
+
id: CHECK_ID54,
|
|
14267
14504
|
name: getCopy("checks.destructive-sql-migration.name"),
|
|
14268
14505
|
description: getCopy("checks.destructive-sql-migration.description"),
|
|
14269
14506
|
category: "resilience",
|
|
@@ -14284,8 +14521,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14284
14521
|
const highMatch = highRiskRegex.exec(line);
|
|
14285
14522
|
if (highMatch) {
|
|
14286
14523
|
findings.push({
|
|
14287
|
-
id: `${
|
|
14288
|
-
checkId:
|
|
14524
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(highMatch.index ?? 0) + 1}:high`,
|
|
14525
|
+
checkId: CHECK_ID54,
|
|
14289
14526
|
category: "resilience",
|
|
14290
14527
|
severity: "high",
|
|
14291
14528
|
message: getCopy("checks.destructive-sql-migration.message", highMatch[0]),
|
|
@@ -14298,8 +14535,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14298
14535
|
const truncateMatch = truncateRegex.exec(line);
|
|
14299
14536
|
if (truncateMatch) {
|
|
14300
14537
|
findings.push({
|
|
14301
|
-
id: `${
|
|
14302
|
-
checkId:
|
|
14538
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(truncateMatch.index ?? 0) + 1}:high`,
|
|
14539
|
+
checkId: CHECK_ID54,
|
|
14303
14540
|
category: "resilience",
|
|
14304
14541
|
severity: "high",
|
|
14305
14542
|
message: getCopy("checks.destructive-sql-migration.message", truncateMatch[0]),
|
|
@@ -14315,8 +14552,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14315
14552
|
const colMatch = dropColumnRegex.exec(line);
|
|
14316
14553
|
if (colMatch) {
|
|
14317
14554
|
findings.push({
|
|
14318
|
-
id: `${
|
|
14319
|
-
checkId:
|
|
14555
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(colMatch.index ?? 0) + 1}:col`,
|
|
14556
|
+
checkId: CHECK_ID54,
|
|
14320
14557
|
category: "resilience",
|
|
14321
14558
|
severity: "medium",
|
|
14322
14559
|
message: getCopy("checks.destructive-sql-migration.message", colMatch[0]),
|
|
@@ -14329,8 +14566,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14329
14566
|
const policyMatch = dropPolicyRegex.exec(line);
|
|
14330
14567
|
if (policyMatch) {
|
|
14331
14568
|
findings.push({
|
|
14332
|
-
id: `${
|
|
14333
|
-
checkId:
|
|
14569
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(policyMatch.index ?? 0) + 1}:policy`,
|
|
14570
|
+
checkId: CHECK_ID54,
|
|
14334
14571
|
category: "resilience",
|
|
14335
14572
|
severity: "medium",
|
|
14336
14573
|
message: getCopy("checks.destructive-sql-migration.message", policyMatch[0]),
|
|
@@ -14343,8 +14580,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14343
14580
|
const indexMatch = dropIndexRegex.exec(line);
|
|
14344
14581
|
if (indexMatch) {
|
|
14345
14582
|
findings.push({
|
|
14346
|
-
id: `${
|
|
14347
|
-
checkId:
|
|
14583
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(indexMatch.index ?? 0) + 1}:index`,
|
|
14584
|
+
checkId: CHECK_ID54,
|
|
14348
14585
|
category: "resilience",
|
|
14349
14586
|
severity: "medium",
|
|
14350
14587
|
message: getCopy("checks.destructive-sql-migration.message", indexMatch[0]),
|
|
@@ -14360,7 +14597,7 @@ var destructiveSqlMigrationCheck = {
|
|
|
14360
14597
|
}
|
|
14361
14598
|
};
|
|
14362
14599
|
registry.register(destructiveSqlMigrationCheck);
|
|
14363
|
-
var
|
|
14600
|
+
var CHECK_ID55 = "resilience.sql-broad-mutation";
|
|
14364
14601
|
var REMEDIATION43 = getCopy("remediation.resilience.sql-broad-mutation");
|
|
14365
14602
|
function splitSqlStatements(content) {
|
|
14366
14603
|
const statements = [];
|
|
@@ -14398,7 +14635,7 @@ function splitSqlStatements(content) {
|
|
|
14398
14635
|
return statements;
|
|
14399
14636
|
}
|
|
14400
14637
|
var sqlBroadMutationCheck = {
|
|
14401
|
-
id:
|
|
14638
|
+
id: CHECK_ID55,
|
|
14402
14639
|
name: getCopy("checks.sql-broad-mutation.name"),
|
|
14403
14640
|
description: getCopy("checks.sql-broad-mutation.description"),
|
|
14404
14641
|
category: "resilience",
|
|
@@ -14444,8 +14681,8 @@ var sqlBroadMutationCheck = {
|
|
|
14444
14681
|
const beforeStatement = content.slice(0, charOffset + leadingWhitespace);
|
|
14445
14682
|
const lineNum = beforeStatement.split("\n").length;
|
|
14446
14683
|
findings.push({
|
|
14447
|
-
id: `${
|
|
14448
|
-
checkId:
|
|
14684
|
+
id: `${CHECK_ID55}:${file.relativePath}:${lineNum}:1`,
|
|
14685
|
+
checkId: CHECK_ID55,
|
|
14449
14686
|
category: "resilience",
|
|
14450
14687
|
severity: "high",
|
|
14451
14688
|
message: getCopy("checks.sql-broad-mutation.message", matchedKeyword),
|
|
@@ -14463,7 +14700,7 @@ var sqlBroadMutationCheck = {
|
|
|
14463
14700
|
}
|
|
14464
14701
|
};
|
|
14465
14702
|
registry.register(sqlBroadMutationCheck);
|
|
14466
|
-
var
|
|
14703
|
+
var CHECK_ID56 = "data-exposure.unprotected-sensitive-sql-columns";
|
|
14467
14704
|
var REMEDIATION44 = getCopy("remediation.data-exposure.unprotected-sensitive-sql-columns");
|
|
14468
14705
|
var SENSITIVE_KEYWORDS = [
|
|
14469
14706
|
"ssn",
|
|
@@ -14495,7 +14732,7 @@ var PROTECTED_SUFFIXES = [
|
|
|
14495
14732
|
"_key_id"
|
|
14496
14733
|
];
|
|
14497
14734
|
var unprotectedSensitiveSqlColumnsCheck = {
|
|
14498
|
-
id:
|
|
14735
|
+
id: CHECK_ID56,
|
|
14499
14736
|
name: getCopy("checks.unprotected-sensitive-sql-columns.name"),
|
|
14500
14737
|
description: getCopy("checks.unprotected-sensitive-sql-columns.description"),
|
|
14501
14738
|
category: "data-exposure",
|
|
@@ -14525,8 +14762,8 @@ var unprotectedSensitiveSqlColumnsCheck = {
|
|
|
14525
14762
|
const isProtected = PROTECTED_SUFFIXES.some((suf) => colName.endsWith(suf));
|
|
14526
14763
|
if (isSensitive && isPlainText && !isProtected) {
|
|
14527
14764
|
findings.push({
|
|
14528
|
-
id: `${
|
|
14529
|
-
checkId:
|
|
14765
|
+
id: `${CHECK_ID56}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 1}`,
|
|
14766
|
+
checkId: CHECK_ID56,
|
|
14530
14767
|
category: "data-exposure",
|
|
14531
14768
|
severity: "high",
|
|
14532
14769
|
message: getCopy("checks.unprotected-sensitive-sql-columns.message", `Column "${colNameRaw}" with type "${colTypeRaw}"`),
|
|
@@ -14590,9 +14827,9 @@ registry.register({
|
|
|
14590
14827
|
return runLLMCheck(ctx, promptTemplate30);
|
|
14591
14828
|
}
|
|
14592
14829
|
});
|
|
14593
|
-
var
|
|
14830
|
+
var CHECK_ID57 = "security.rust-unsafe-blocks";
|
|
14594
14831
|
registry.register({
|
|
14595
|
-
id:
|
|
14832
|
+
id: CHECK_ID57,
|
|
14596
14833
|
name: getCopy("checks.rust-unsafe-blocks.name"),
|
|
14597
14834
|
description: getCopy("checks.rust-unsafe-blocks.description"),
|
|
14598
14835
|
category: "security",
|
|
@@ -14679,8 +14916,8 @@ registry.register({
|
|
|
14679
14916
|
}
|
|
14680
14917
|
if (!foundSafety) {
|
|
14681
14918
|
const finding = {
|
|
14682
|
-
id: `${
|
|
14683
|
-
checkId:
|
|
14919
|
+
id: `${CHECK_ID57}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 0}:${i}`,
|
|
14920
|
+
checkId: CHECK_ID57,
|
|
14684
14921
|
category: "security",
|
|
14685
14922
|
severity: "medium",
|
|
14686
14923
|
message: getCopy("checks.rust-unsafe-blocks.message", match.nodeText),
|
|
@@ -14698,7 +14935,7 @@ registry.register({
|
|
|
14698
14935
|
return findings;
|
|
14699
14936
|
}
|
|
14700
14937
|
});
|
|
14701
|
-
var
|
|
14938
|
+
var CHECK_ID58 = "resilience.rust-handler-panics";
|
|
14702
14939
|
var RUST_QUERY = `
|
|
14703
14940
|
(call_expression
|
|
14704
14941
|
function: (field_expression
|
|
@@ -14724,7 +14961,7 @@ function hasAsyncModifier(node) {
|
|
|
14724
14961
|
return false;
|
|
14725
14962
|
}
|
|
14726
14963
|
registry.register({
|
|
14727
|
-
id:
|
|
14964
|
+
id: CHECK_ID58,
|
|
14728
14965
|
name: getCopy("checks.rust-handler-panics.name"),
|
|
14729
14966
|
description: getCopy("checks.rust-handler-panics.description"),
|
|
14730
14967
|
category: "resilience",
|
|
@@ -14790,8 +15027,8 @@ registry.register({
|
|
|
14790
15027
|
const isAsync = hasAsyncModifier(enclosingFunction);
|
|
14791
15028
|
if (isAsync) {
|
|
14792
15029
|
const finding = {
|
|
14793
|
-
id: `${
|
|
14794
|
-
checkId:
|
|
15030
|
+
id: `${CHECK_ID58}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 0}:${i}`,
|
|
15031
|
+
checkId: CHECK_ID58,
|
|
14795
15032
|
category: "resilience",
|
|
14796
15033
|
severity: "high",
|
|
14797
15034
|
message: getCopy("checks.rust-handler-panics.message", match.nodeText),
|
|
@@ -16229,6 +16466,7 @@ async function executeScan(args, ctx) {
|
|
|
16229
16466
|
debug: args.debug,
|
|
16230
16467
|
baseline: args.baseline,
|
|
16231
16468
|
minConfidence: args.minConfidence,
|
|
16469
|
+
minSeverity: args.minSeverity,
|
|
16232
16470
|
onCheckComplete: (completed, total, checkName) => {
|
|
16233
16471
|
heartbeat();
|
|
16234
16472
|
if (showProgress) {
|
|
@@ -16608,6 +16846,7 @@ async function runCli(options) {
|
|
|
16608
16846
|
debug: options.debug,
|
|
16609
16847
|
baseline: options.baseline,
|
|
16610
16848
|
minConfidence: options.minConfidence,
|
|
16849
|
+
minSeverity: options.minSeverity,
|
|
16611
16850
|
showIds: options.showIds,
|
|
16612
16851
|
all: options.all
|
|
16613
16852
|
};
|
|
@@ -16786,12 +17025,13 @@ var require5 = createRequire4(import.meta.url);
|
|
|
16786
17025
|
var { version } = require5("../package.json");
|
|
16787
17026
|
var cli = cac("seaworthycode");
|
|
16788
17027
|
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) => {
|
|
17028
|
+
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
17029
|
const maxFileSize = options.maxFileSize ? Number(options.maxFileSize) : void 0;
|
|
16791
17030
|
const tier = options.tier;
|
|
16792
17031
|
const baseline = options.baseline === "save" ? "save" : options.baseline;
|
|
16793
17032
|
const minConfidence = options.minConfidence;
|
|
16794
|
-
const
|
|
17033
|
+
const minSeverity = options.minSeverity;
|
|
17034
|
+
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
17035
|
if (result.updateMessage) {
|
|
16796
17036
|
console.error(result.updateMessage);
|
|
16797
17037
|
}
|
|
@@ -16859,6 +17099,7 @@ cli.help(() => {
|
|
|
16859
17099
|
--max-file-size <n> Skip files larger than n bytes (default: 1 MB)
|
|
16860
17100
|
--baseline [ref] Compare against baseline file or save (--baseline save)
|
|
16861
17101
|
--min-confidence <level> Hide findings below confidence (high, medium, low)
|
|
17102
|
+
--min-severity <level> Hide findings below severity (critical, high, medium, low, info)
|
|
16862
17103
|
--show-ids Show check IDs alongside names
|
|
16863
17104
|
--all List every finding; do not collapse low-severity findings
|
|
16864
17105
|
--debug Enable debug logging`
|