seaworthycode 1.2.18 → 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 +478 -222
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -207,6 +207,7 @@ var registry = new CheckRegistry();
|
|
|
207
207
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
208
208
|
"node_modules",
|
|
209
209
|
".git",
|
|
210
|
+
".claude",
|
|
210
211
|
"dist",
|
|
211
212
|
"build",
|
|
212
213
|
".next",
|
|
@@ -640,6 +641,14 @@ function filterByMinConfidence(findings, minConfidence) {
|
|
|
640
641
|
if (!minConfidence) return findings;
|
|
641
642
|
return findings.filter((f) => meetsMinConfidence(f.confidence, minConfidence));
|
|
642
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
|
+
}
|
|
643
652
|
var SITE_URL = process.env.SEAWORTHY_SITE_URL ?? "https://seaworthycode.com";
|
|
644
653
|
var whyCopy = {
|
|
645
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).",
|
|
@@ -1059,6 +1068,8 @@ var copy = {
|
|
|
1059
1068
|
"checks.verbose-errors.description": "Error responses include stack traces or internal details, leaking implementation information to clients.",
|
|
1060
1069
|
"checks.pii-in-responses.name": "PII in API responses",
|
|
1061
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.`,
|
|
1062
1073
|
"checks.no-response-filtering.name": "No response filtering",
|
|
1063
1074
|
"checks.no-response-filtering.description": "API responses return full database rows without filtering sensitive or unnecessary fields.",
|
|
1064
1075
|
"checks.missing-lockfile.name": "Missing lockfile",
|
|
@@ -1727,6 +1738,7 @@ var ScanRunner = class {
|
|
|
1727
1738
|
dedupeDegradedFindings(dedupeCrossCheck(dedupeFindings(results)))
|
|
1728
1739
|
);
|
|
1729
1740
|
findings = filterByMinConfidence(findings, options.minConfidence);
|
|
1741
|
+
findings = filterByMinSeverity(findings, options.minSeverity);
|
|
1730
1742
|
if (options.baseline) {
|
|
1731
1743
|
if (options.baseline === "save") {
|
|
1732
1744
|
const baselinePath = resolveBaselinePath(options.targetDir);
|
|
@@ -3903,6 +3915,14 @@ function setAnalysisType(finding, analysisType = "pattern") {
|
|
|
3903
3915
|
finding.properties.analysisType = analysisType;
|
|
3904
3916
|
}
|
|
3905
3917
|
var resolveCopy = getCopy;
|
|
3918
|
+
function getTreeSitterMaxFiles() {
|
|
3919
|
+
const env2 = process.env.SEAWORTHY_MECHANICAL_MAX_FILES;
|
|
3920
|
+
if (env2 !== void 0) {
|
|
3921
|
+
const n = parseInt(env2, 10);
|
|
3922
|
+
if (!isNaN(n) && n > 0) return n;
|
|
3923
|
+
}
|
|
3924
|
+
return 2e3;
|
|
3925
|
+
}
|
|
3906
3926
|
function createTreeSitterCheck(options) {
|
|
3907
3927
|
const {
|
|
3908
3928
|
id,
|
|
@@ -3935,13 +3955,19 @@ function createTreeSitterCheck(options) {
|
|
|
3935
3955
|
async run(ctx) {
|
|
3936
3956
|
const findings = [];
|
|
3937
3957
|
const degradedLanguages = /* @__PURE__ */ new Set();
|
|
3938
|
-
const
|
|
3958
|
+
const allEligibleFiles = ctx.files.filter((file) => {
|
|
3939
3959
|
const query = queries[file.language];
|
|
3940
3960
|
if (!query) return false;
|
|
3941
3961
|
if (isMechanicalCheckExcludedPath(file.relativePath)) return false;
|
|
3942
3962
|
if (fileFilter && !fileFilter(file)) return false;
|
|
3943
3963
|
return true;
|
|
3944
3964
|
});
|
|
3965
|
+
const maxFiles = getTreeSitterMaxFiles();
|
|
3966
|
+
let eligibleFiles = allEligibleFiles;
|
|
3967
|
+
if (allEligibleFiles.length > maxFiles) {
|
|
3968
|
+
console.error(`[seaworthy] ${id}: ${allEligibleFiles.length} eligible files \u2014 capping tree-sitter scan to ${maxFiles}`);
|
|
3969
|
+
eligibleFiles = allEligibleFiles.slice(0, maxFiles);
|
|
3970
|
+
}
|
|
3945
3971
|
const fileFindings = await runWithConcurrencyCollect(
|
|
3946
3972
|
eligibleFiles,
|
|
3947
3973
|
async (file) => {
|
|
@@ -6189,6 +6215,10 @@ async function runMechanicalCheck(ctx, template) {
|
|
|
6189
6215
|
}
|
|
6190
6216
|
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
6191
6217
|
const line = lines[lineNum];
|
|
6218
|
+
if (pattern.skipCommentLines) {
|
|
6219
|
+
const trimmed = line.trimStart();
|
|
6220
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*")) continue;
|
|
6221
|
+
}
|
|
6192
6222
|
if (pattern.regex.test(line)) {
|
|
6193
6223
|
results.push(
|
|
6194
6224
|
makeMechanicalFinding(template, pattern, file.relativePath, lineNum + 1)
|
|
@@ -7082,12 +7112,13 @@ registry.register({
|
|
|
7082
7112
|
return runLLMCheck(ctx, promptTemplate8);
|
|
7083
7113
|
}
|
|
7084
7114
|
});
|
|
7085
|
-
var PROMPT_VERSION9 = "1.
|
|
7115
|
+
var PROMPT_VERSION9 = "1.3.0";
|
|
7086
7116
|
var fallbackPatterns9 = [
|
|
7087
7117
|
{
|
|
7088
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,
|
|
7089
7119
|
message: "Outbound HTTP call found \u2014 verify timeout is configured",
|
|
7090
|
-
severity: "medium"
|
|
7120
|
+
severity: "medium",
|
|
7121
|
+
skipCommentLines: true
|
|
7091
7122
|
}
|
|
7092
7123
|
];
|
|
7093
7124
|
async function buildPrompt9(sourceFiles) {
|
|
@@ -7361,12 +7392,13 @@ registry.register({
|
|
|
7361
7392
|
return runLLMCheck(ctx, promptTemplate12);
|
|
7362
7393
|
}
|
|
7363
7394
|
});
|
|
7364
|
-
var PROMPT_VERSION13 = "1.
|
|
7395
|
+
var PROMPT_VERSION13 = "1.2.0";
|
|
7365
7396
|
var fallbackPatterns13 = [
|
|
7366
7397
|
{
|
|
7367
7398
|
regex: /console\.(?:log|error|warn)\b/i,
|
|
7368
7399
|
message: "Console logging used \u2014 structured logging recommended",
|
|
7369
|
-
severity: "low"
|
|
7400
|
+
severity: "low",
|
|
7401
|
+
skipCommentLines: true
|
|
7370
7402
|
}
|
|
7371
7403
|
];
|
|
7372
7404
|
async function buildPrompt13(sourceFiles) {
|
|
@@ -7503,16 +7535,18 @@ registry.register({
|
|
|
7503
7535
|
return runLLMCheck(ctx, promptTemplate15);
|
|
7504
7536
|
}
|
|
7505
7537
|
});
|
|
7506
|
-
var PROMPT_VERSION16 = "1.
|
|
7538
|
+
var PROMPT_VERSION16 = "1.1.0";
|
|
7507
7539
|
var fallbackPatterns16 = [
|
|
7508
7540
|
{
|
|
7509
|
-
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,
|
|
7510
7542
|
message: "Default credentials possibly in use",
|
|
7511
7543
|
severity: "high"
|
|
7512
7544
|
}
|
|
7513
7545
|
];
|
|
7514
7546
|
async function buildPrompt16(sourceFiles) {
|
|
7515
|
-
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
|
+
];
|
|
7516
7550
|
for (const file of sourceFiles) {
|
|
7517
7551
|
const content = await file.content();
|
|
7518
7552
|
parts.push(`--- ${file.relativePath} ---
|
|
@@ -7559,7 +7593,13 @@ function isDefaultCredential(password2) {
|
|
|
7559
7593
|
"secret",
|
|
7560
7594
|
"test",
|
|
7561
7595
|
"postgres",
|
|
7562
|
-
"root"
|
|
7596
|
+
"root",
|
|
7597
|
+
"default",
|
|
7598
|
+
"pass",
|
|
7599
|
+
"1234",
|
|
7600
|
+
"12345",
|
|
7601
|
+
"qwerty",
|
|
7602
|
+
"guest"
|
|
7563
7603
|
]);
|
|
7564
7604
|
return defaults.has(normalized);
|
|
7565
7605
|
}
|
|
@@ -7691,6 +7731,115 @@ async function scanBashDefaultCredentials(ctx) {
|
|
|
7691
7731
|
return lineMatch(line, index, /(?:^|\s)(?:export\s+)?[A-Z0-9_]*(?:PASS|PASSWORD|PWD|SECRET|TOKEN)[A-Z0-9_]*=(["'])([^"']{1,64})\1/i);
|
|
7692
7732
|
});
|
|
7693
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
|
+
});
|
|
7694
7843
|
var sqlCheck = {
|
|
7695
7844
|
id: CHECK_ID8,
|
|
7696
7845
|
name: getCopy("checks.default-credentials.name"),
|
|
@@ -7699,12 +7848,14 @@ var sqlCheck = {
|
|
|
7699
7848
|
severity: "high",
|
|
7700
7849
|
minTier: "pro",
|
|
7701
7850
|
async run(ctx) {
|
|
7702
|
-
const [sqlFindings, bashFindings, llmFindings] = await Promise.all([
|
|
7851
|
+
const [sqlFindings, bashFindings, llmFindings, astFindings] = await Promise.all([
|
|
7703
7852
|
scanSqlDefaultCredentials(ctx),
|
|
7704
7853
|
scanBashDefaultCredentials(ctx),
|
|
7705
|
-
runLLMCheck(ctx, promptTemplate16)
|
|
7854
|
+
runLLMCheck(ctx, promptTemplate16),
|
|
7855
|
+
astCheck.run(ctx)
|
|
7706
7856
|
]);
|
|
7707
|
-
|
|
7857
|
+
const nonAstFindings = [...sqlFindings, ...bashFindings, ...llmFindings];
|
|
7858
|
+
return mergeFindings(astFindings, nonAstFindings);
|
|
7708
7859
|
}
|
|
7709
7860
|
};
|
|
7710
7861
|
registry.register(sqlCheck);
|
|
@@ -7766,16 +7917,17 @@ registry.register({
|
|
|
7766
7917
|
return [...bashFindings, ...llmFindings];
|
|
7767
7918
|
}
|
|
7768
7919
|
});
|
|
7769
|
-
var PROMPT_VERSION18 = "1.
|
|
7920
|
+
var PROMPT_VERSION18 = "1.1.0";
|
|
7770
7921
|
var fallbackPatterns18 = [
|
|
7771
7922
|
{
|
|
7772
7923
|
regex: /\b(?:ssn|social_security|date_of_birth|dob|passport|national_id)\b/i,
|
|
7773
7924
|
message: "Potentially sensitive field name found \u2014 verify it is not exposed in API responses",
|
|
7774
|
-
severity: "high"
|
|
7925
|
+
severity: "high",
|
|
7926
|
+
skipCommentLines: true
|
|
7775
7927
|
}
|
|
7776
7928
|
];
|
|
7777
7929
|
async function buildPrompt18(sourceFiles) {
|
|
7778
|
-
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."];
|
|
7779
7931
|
for (const file of sourceFiles) {
|
|
7780
7932
|
const content = await file.content();
|
|
7781
7933
|
parts.push(`--- ${file.relativePath} ---
|
|
@@ -7793,15 +7945,63 @@ var promptTemplate18 = {
|
|
|
7793
7945
|
parseResponse: parseResponse18,
|
|
7794
7946
|
fallbackPatterns: fallbackPatterns18
|
|
7795
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
|
+
});
|
|
7796
7992
|
registry.register({
|
|
7797
|
-
id:
|
|
7798
|
-
name: "
|
|
7799
|
-
description: "
|
|
7993
|
+
id: CHECK_ID9,
|
|
7994
|
+
name: getCopy("checks.pii-in-responses.name"),
|
|
7995
|
+
description: getCopy("checks.pii-in-responses.description"),
|
|
7800
7996
|
category: "data-exposure",
|
|
7801
7997
|
severity: "high",
|
|
7802
7998
|
minTier: "pro",
|
|
7803
7999
|
async run(ctx) {
|
|
7804
|
-
|
|
8000
|
+
const [llmFindings, astFindings] = await Promise.all([
|
|
8001
|
+
runLLMCheck(ctx, promptTemplate18),
|
|
8002
|
+
piiAstCheck.run(ctx)
|
|
8003
|
+
]);
|
|
8004
|
+
return mergeFindings(astFindings, llmFindings);
|
|
7805
8005
|
}
|
|
7806
8006
|
});
|
|
7807
8007
|
var PROMPT_VERSION19 = "1.0.0";
|
|
@@ -7847,7 +8047,7 @@ registry.register({
|
|
|
7847
8047
|
return runLLMCheck(ctx, promptTemplate19);
|
|
7848
8048
|
}
|
|
7849
8049
|
});
|
|
7850
|
-
var
|
|
8050
|
+
var CHECK_ID10 = "data-exposure.html-form-pii-exposure";
|
|
7851
8051
|
var REMEDIATION12 = getCopy("remediation.data-exposure.html-form-pii-exposure");
|
|
7852
8052
|
var INPUT_PATTERN = /<input\b[^>]*?>/gi;
|
|
7853
8053
|
var AUTOCOMPLETE_PATTERN = /\bautocomplete\s*=\s*["']([^"']+)["']/i;
|
|
@@ -7874,8 +8074,8 @@ function lineAndColumn4(content, index) {
|
|
|
7874
8074
|
function pushFinding2(findings, filePath, content, index, matchText) {
|
|
7875
8075
|
const { line, column } = lineAndColumn4(content, index);
|
|
7876
8076
|
findings.push({
|
|
7877
|
-
id: `${
|
|
7878
|
-
checkId:
|
|
8077
|
+
id: `${CHECK_ID10}:${filePath}:${line}:${column}`,
|
|
8078
|
+
checkId: CHECK_ID10,
|
|
7879
8079
|
category: "data-exposure",
|
|
7880
8080
|
severity: "medium",
|
|
7881
8081
|
message: getCopy("checks.html-form-pii-exposure.message", matchText),
|
|
@@ -7901,7 +8101,7 @@ function hasSensitiveField(tagText) {
|
|
|
7901
8101
|
return SENSITIVE_TYPES.test(type.trim()) || SENSITIVE_NAME.test(name);
|
|
7902
8102
|
}
|
|
7903
8103
|
registry.register({
|
|
7904
|
-
id:
|
|
8104
|
+
id: CHECK_ID10,
|
|
7905
8105
|
name: getCopy("checks.html-form-pii-exposure.name"),
|
|
7906
8106
|
description: getCopy("checks.html-form-pii-exposure.description"),
|
|
7907
8107
|
category: "data-exposure",
|
|
@@ -7930,7 +8130,7 @@ registry.register({
|
|
|
7930
8130
|
return findings;
|
|
7931
8131
|
}
|
|
7932
8132
|
});
|
|
7933
|
-
var
|
|
8133
|
+
var CHECK_ID11 = "security.xss-surface";
|
|
7934
8134
|
var CGI_PATH_HINT = new RegExp("(^|[/_-])cgi([/_.-]|$)", "i");
|
|
7935
8135
|
function isLikelyCgiBashScript(file) {
|
|
7936
8136
|
return CGI_PATH_HINT.test(file.relativePath);
|
|
@@ -8209,7 +8409,7 @@ var QUERIES = {
|
|
|
8209
8409
|
};
|
|
8210
8410
|
registry.register(
|
|
8211
8411
|
createTreeSitterCheck({
|
|
8212
|
-
id:
|
|
8412
|
+
id: CHECK_ID11,
|
|
8213
8413
|
nameKey: "checks.xss-surface.name",
|
|
8214
8414
|
descriptionKey: "checks.xss-surface.description",
|
|
8215
8415
|
category: "security",
|
|
@@ -8224,7 +8424,7 @@ registry.register(
|
|
|
8224
8424
|
fileFilter: (file) => file.language !== "bash" || isLikelyCgiBashScript(file)
|
|
8225
8425
|
})
|
|
8226
8426
|
);
|
|
8227
|
-
var
|
|
8427
|
+
var CHECK_ID12 = "security.os-command-injection";
|
|
8228
8428
|
function isStaticShellLikeLiteral(text22) {
|
|
8229
8429
|
const t = text22.trim();
|
|
8230
8430
|
if (t.length < 2) return false;
|
|
@@ -8354,7 +8554,7 @@ var QUERIES2 = {
|
|
|
8354
8554
|
};
|
|
8355
8555
|
registry.register(
|
|
8356
8556
|
createTreeSitterCheck({
|
|
8357
|
-
id:
|
|
8557
|
+
id: CHECK_ID12,
|
|
8358
8558
|
nameKey: "checks.os-command-injection.name",
|
|
8359
8559
|
descriptionKey: "checks.os-command-injection.description",
|
|
8360
8560
|
category: "security",
|
|
@@ -8371,7 +8571,7 @@ registry.register(
|
|
|
8371
8571
|
}
|
|
8372
8572
|
})
|
|
8373
8573
|
);
|
|
8374
|
-
var
|
|
8574
|
+
var CHECK_ID13 = "security.shell-arbitrary-evaluation";
|
|
8375
8575
|
var QUERY = `
|
|
8376
8576
|
(command
|
|
8377
8577
|
name: (command_name (word) @cmd)
|
|
@@ -8385,7 +8585,7 @@ var QUERY = `
|
|
|
8385
8585
|
`;
|
|
8386
8586
|
registry.register(
|
|
8387
8587
|
createTreeSitterCheck({
|
|
8388
|
-
id:
|
|
8588
|
+
id: CHECK_ID13,
|
|
8389
8589
|
nameKey: "checks.shell-arbitrary-evaluation.name",
|
|
8390
8590
|
descriptionKey: "checks.shell-arbitrary-evaluation.description",
|
|
8391
8591
|
category: "security",
|
|
@@ -8397,7 +8597,7 @@ registry.register(
|
|
|
8397
8597
|
remediationKey: "remediation.security.shell-arbitrary-evaluation"
|
|
8398
8598
|
})
|
|
8399
8599
|
);
|
|
8400
|
-
var
|
|
8600
|
+
var CHECK_ID14 = "security.shell-unescaped-path-deletion";
|
|
8401
8601
|
var QUERY2 = `
|
|
8402
8602
|
(command
|
|
8403
8603
|
name: (command_name (word) @cmd)
|
|
@@ -8427,7 +8627,7 @@ var QUERY2 = `
|
|
|
8427
8627
|
`;
|
|
8428
8628
|
registry.register(
|
|
8429
8629
|
createTreeSitterCheck({
|
|
8430
|
-
id:
|
|
8630
|
+
id: CHECK_ID14,
|
|
8431
8631
|
nameKey: "checks.shell-unescaped-path-deletion.name",
|
|
8432
8632
|
descriptionKey: "checks.shell-unescaped-path-deletion.description",
|
|
8433
8633
|
category: "security",
|
|
@@ -8439,7 +8639,7 @@ registry.register(
|
|
|
8439
8639
|
remediationKey: "remediation.security.shell-unescaped-path-deletion"
|
|
8440
8640
|
})
|
|
8441
8641
|
);
|
|
8442
|
-
var
|
|
8642
|
+
var CHECK_ID15 = "security.file-inclusion";
|
|
8443
8643
|
var QUERIES3 = {
|
|
8444
8644
|
php: `
|
|
8445
8645
|
(include_expression
|
|
@@ -8469,7 +8669,7 @@ var QUERIES3 = {
|
|
|
8469
8669
|
};
|
|
8470
8670
|
registry.register(
|
|
8471
8671
|
createTreeSitterCheck({
|
|
8472
|
-
id:
|
|
8672
|
+
id: CHECK_ID15,
|
|
8473
8673
|
nameKey: "checks.file-inclusion.name",
|
|
8474
8674
|
descriptionKey: "checks.file-inclusion.description",
|
|
8475
8675
|
category: "security",
|
|
@@ -8481,7 +8681,7 @@ registry.register(
|
|
|
8481
8681
|
remediationKey: "remediation.security.file-inclusion"
|
|
8482
8682
|
})
|
|
8483
8683
|
);
|
|
8484
|
-
var
|
|
8684
|
+
var CHECK_ID16 = "security.open-redirect";
|
|
8485
8685
|
var QUERIES4 = {
|
|
8486
8686
|
php: `
|
|
8487
8687
|
(function_call_expression
|
|
@@ -8651,7 +8851,7 @@ var QUERIES4 = {
|
|
|
8651
8851
|
};
|
|
8652
8852
|
registry.register(
|
|
8653
8853
|
createTreeSitterCheck({
|
|
8654
|
-
id:
|
|
8854
|
+
id: CHECK_ID16,
|
|
8655
8855
|
nameKey: "checks.open-redirect.name",
|
|
8656
8856
|
descriptionKey: "checks.open-redirect.description",
|
|
8657
8857
|
category: "security",
|
|
@@ -8663,7 +8863,7 @@ registry.register(
|
|
|
8663
8863
|
remediationKey: "remediation.security.open-redirect"
|
|
8664
8864
|
})
|
|
8665
8865
|
);
|
|
8666
|
-
var
|
|
8866
|
+
var CHECK_ID17 = "security.unsafe-file-upload";
|
|
8667
8867
|
var QUERIES5 = {
|
|
8668
8868
|
javascript: `
|
|
8669
8869
|
(call_expression
|
|
@@ -8835,7 +9035,7 @@ var QUERIES5 = {
|
|
|
8835
9035
|
};
|
|
8836
9036
|
registry.register(
|
|
8837
9037
|
createTreeSitterCheck({
|
|
8838
|
-
id:
|
|
9038
|
+
id: CHECK_ID17,
|
|
8839
9039
|
nameKey: "checks.unsafe-file-upload.name",
|
|
8840
9040
|
descriptionKey: "checks.unsafe-file-upload.description",
|
|
8841
9041
|
category: "security",
|
|
@@ -8851,7 +9051,7 @@ registry.register(
|
|
|
8851
9051
|
}
|
|
8852
9052
|
})
|
|
8853
9053
|
);
|
|
8854
|
-
var
|
|
9054
|
+
var CHECK_ID18 = "security.unsafe-deserialization";
|
|
8855
9055
|
var QUERIES6 = {
|
|
8856
9056
|
javascript: `
|
|
8857
9057
|
(call_expression
|
|
@@ -8988,7 +9188,7 @@ var QUERIES6 = {
|
|
|
8988
9188
|
};
|
|
8989
9189
|
registry.register(
|
|
8990
9190
|
createTreeSitterCheck({
|
|
8991
|
-
id:
|
|
9191
|
+
id: CHECK_ID18,
|
|
8992
9192
|
nameKey: "checks.unsafe-deserialization.name",
|
|
8993
9193
|
descriptionKey: "checks.unsafe-deserialization.description",
|
|
8994
9194
|
category: "security",
|
|
@@ -9006,7 +9206,7 @@ registry.register(
|
|
|
9006
9206
|
}
|
|
9007
9207
|
})
|
|
9008
9208
|
);
|
|
9009
|
-
var
|
|
9209
|
+
var CHECK_ID19 = "security.hardcoded-passwords";
|
|
9010
9210
|
var PASSWORD_NAME_REGEX = /^(\$)?(password|pwd|passwd|secret|pass)$/i;
|
|
9011
9211
|
var REMEDIATION13 = getCopy("remediation.security.hardcoded-passwords");
|
|
9012
9212
|
var HTML_CSS_PASSWORD_PATTERNS = [
|
|
@@ -9087,7 +9287,7 @@ var QUERIES7 = {
|
|
|
9087
9287
|
dockerfile: "((identifier) @name) @match"
|
|
9088
9288
|
};
|
|
9089
9289
|
var regexCheck4 = {
|
|
9090
|
-
id:
|
|
9290
|
+
id: CHECK_ID19,
|
|
9091
9291
|
name: getCopy("checks.hardcoded-passwords.name"),
|
|
9092
9292
|
description: getCopy("checks.hardcoded-passwords.description"),
|
|
9093
9293
|
category: "security",
|
|
@@ -9112,8 +9312,8 @@ var regexCheck4 = {
|
|
|
9112
9312
|
const value = match[2] ?? match[1] ?? "";
|
|
9113
9313
|
if (match[2] === void 0 && line.toLowerCase().includes('type="password"')) {
|
|
9114
9314
|
findings.push({
|
|
9115
|
-
id: `${
|
|
9116
|
-
checkId:
|
|
9315
|
+
id: `${CHECK_ID19}:${file.relativePath}:${lineNum + 1}:${(match.index ?? 0) + 1}:${matchIdx}`,
|
|
9316
|
+
checkId: CHECK_ID19,
|
|
9117
9317
|
category: "security",
|
|
9118
9318
|
severity: "critical",
|
|
9119
9319
|
message: getCopy("checks.hardcoded-passwords.message", "password"),
|
|
@@ -9128,8 +9328,8 @@ var regexCheck4 = {
|
|
|
9128
9328
|
if (!PASSWORD_NAME_REGEX.test(name)) continue;
|
|
9129
9329
|
if ((value ?? "").trim().length === 0) continue;
|
|
9130
9330
|
findings.push({
|
|
9131
|
-
id: `${
|
|
9132
|
-
checkId:
|
|
9331
|
+
id: `${CHECK_ID19}:${file.relativePath}:${lineNum + 1}:${(match.index ?? 0) + 1}:${matchIdx}`,
|
|
9332
|
+
checkId: CHECK_ID19,
|
|
9133
9333
|
category: "security",
|
|
9134
9334
|
severity: "critical",
|
|
9135
9335
|
message: getCopy("checks.hardcoded-passwords.message", name),
|
|
@@ -9147,7 +9347,7 @@ var regexCheck4 = {
|
|
|
9147
9347
|
}
|
|
9148
9348
|
};
|
|
9149
9349
|
var treeSitterCheck = createTreeSitterCheck({
|
|
9150
|
-
id:
|
|
9350
|
+
id: CHECK_ID19,
|
|
9151
9351
|
nameKey: "checks.hardcoded-passwords.name",
|
|
9152
9352
|
descriptionKey: "checks.hardcoded-passwords.description",
|
|
9153
9353
|
category: "security",
|
|
@@ -9343,7 +9543,7 @@ function isFullHtmlDocument(content) {
|
|
|
9343
9543
|
function hasHeadElement(content) {
|
|
9344
9544
|
return /<head\b/i.test(content);
|
|
9345
9545
|
}
|
|
9346
|
-
var
|
|
9546
|
+
var CHECK_ID20 = "security.html-missing-csp";
|
|
9347
9547
|
var REMEDIATION14 = getCopy("remediation.security.html-missing-csp");
|
|
9348
9548
|
var META_CSP_PATTERN = /<meta\b[^>]*\bhttp-equiv\s*=\s*["']content-security-policy["'][^>]*>/gi;
|
|
9349
9549
|
var CSP_CONTENT_PATTERN = /\bcontent\s*=\s*(["'])([\s\S]*?)\1/i;
|
|
@@ -9351,7 +9551,7 @@ function firstHtmlAnchor(files) {
|
|
|
9351
9551
|
return files.filter((file) => file.language === "html" && !isMechanicalCheckExcludedPath(file.relativePath)).map((file) => file.relativePath).sort()[0];
|
|
9352
9552
|
}
|
|
9353
9553
|
registry.register({
|
|
9354
|
-
id:
|
|
9554
|
+
id: CHECK_ID20,
|
|
9355
9555
|
name: getCopy("checks.html-missing-csp.name"),
|
|
9356
9556
|
description: getCopy("checks.html-missing-csp.description"),
|
|
9357
9557
|
category: "security",
|
|
@@ -9381,8 +9581,8 @@ registry.register({
|
|
|
9381
9581
|
if (/default-src\s+\*|unsafe-inline|unsafe-eval/i.test(cspValue)) {
|
|
9382
9582
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9383
9583
|
findings.push({
|
|
9384
|
-
id: `${
|
|
9385
|
-
checkId:
|
|
9584
|
+
id: `${CHECK_ID20}:${file.relativePath}:${line}:${column}`,
|
|
9585
|
+
checkId: CHECK_ID20,
|
|
9386
9586
|
category: "security",
|
|
9387
9587
|
severity: "medium",
|
|
9388
9588
|
message: getCopy("checks.html-missing-csp.message", "weak Content Security Policy"),
|
|
@@ -9399,8 +9599,8 @@ registry.register({
|
|
|
9399
9599
|
}
|
|
9400
9600
|
if (findings.length === 0 && sawFullHtmlDoc && !sawValidMetaCsp) {
|
|
9401
9601
|
findings.push({
|
|
9402
|
-
id: `${
|
|
9403
|
-
checkId:
|
|
9602
|
+
id: `${CHECK_ID20}:${anchor}:1:1`,
|
|
9603
|
+
checkId: CHECK_ID20,
|
|
9404
9604
|
category: "security",
|
|
9405
9605
|
severity: "medium",
|
|
9406
9606
|
message: getCopy("checks.html-missing-csp.message", "no Content Security Policy found"),
|
|
@@ -9414,10 +9614,10 @@ registry.register({
|
|
|
9414
9614
|
return findings;
|
|
9415
9615
|
}
|
|
9416
9616
|
});
|
|
9417
|
-
var
|
|
9617
|
+
var CHECK_ID21 = "security.html-static-host-headers";
|
|
9418
9618
|
var REMEDIATION15 = getCopy("remediation.security.html-static-host-headers");
|
|
9419
9619
|
registry.register({
|
|
9420
|
-
id:
|
|
9620
|
+
id: CHECK_ID21,
|
|
9421
9621
|
name: getCopy("checks.html-static-host-headers.name"),
|
|
9422
9622
|
description: getCopy("checks.html-static-host-headers.description"),
|
|
9423
9623
|
category: "security",
|
|
@@ -9437,8 +9637,8 @@ registry.register({
|
|
|
9437
9637
|
if (!anchor) return [];
|
|
9438
9638
|
return [
|
|
9439
9639
|
{
|
|
9440
|
-
id: `${
|
|
9441
|
-
checkId:
|
|
9640
|
+
id: `${CHECK_ID21}:${anchor}:1:1`,
|
|
9641
|
+
checkId: CHECK_ID21,
|
|
9442
9642
|
category: "security",
|
|
9443
9643
|
severity: "medium",
|
|
9444
9644
|
message: getCopy("checks.html-static-host-headers.message", missing.join(", ")),
|
|
@@ -9451,7 +9651,7 @@ registry.register({
|
|
|
9451
9651
|
];
|
|
9452
9652
|
}
|
|
9453
9653
|
});
|
|
9454
|
-
var
|
|
9654
|
+
var CHECK_ID22 = "security.html-inline-unsafe-script";
|
|
9455
9655
|
var REMEDIATION16 = getCopy("remediation.security.html-inline-unsafe-script");
|
|
9456
9656
|
var SCRIPT_BLOCK_PATTERN2 = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
9457
9657
|
var EVENT_HANDLER_PATTERN = /\son[a-z]+\s*=\s*["']([^"']+)["']/gi;
|
|
@@ -9508,7 +9708,7 @@ function findEventHandlerMatches(content) {
|
|
|
9508
9708
|
return results;
|
|
9509
9709
|
}
|
|
9510
9710
|
registry.register({
|
|
9511
|
-
id:
|
|
9711
|
+
id: CHECK_ID22,
|
|
9512
9712
|
name: getCopy("checks.html-inline-unsafe-script.name"),
|
|
9513
9713
|
description: getCopy("checks.html-inline-unsafe-script.description"),
|
|
9514
9714
|
category: "security",
|
|
@@ -9523,8 +9723,8 @@ registry.register({
|
|
|
9523
9723
|
for (const match of findScriptMatches(content)) {
|
|
9524
9724
|
const { line, column } = lineAndColumn6(content, match.index);
|
|
9525
9725
|
findings.push({
|
|
9526
|
-
id: `${
|
|
9527
|
-
checkId:
|
|
9726
|
+
id: `${CHECK_ID22}:${file.relativePath}:${line}:${column}`,
|
|
9727
|
+
checkId: CHECK_ID22,
|
|
9528
9728
|
category: "security",
|
|
9529
9729
|
severity: "high",
|
|
9530
9730
|
message: getCopy("checks.html-inline-unsafe-script.message", match.text),
|
|
@@ -9538,8 +9738,8 @@ registry.register({
|
|
|
9538
9738
|
for (const match of findEventHandlerMatches(content)) {
|
|
9539
9739
|
const { line, column } = lineAndColumn6(content, match.index);
|
|
9540
9740
|
findings.push({
|
|
9541
|
-
id: `${
|
|
9542
|
-
checkId:
|
|
9741
|
+
id: `${CHECK_ID22}:${file.relativePath}:${line}:${column}:event`,
|
|
9742
|
+
checkId: CHECK_ID22,
|
|
9543
9743
|
category: "security",
|
|
9544
9744
|
severity: "high",
|
|
9545
9745
|
message: getCopy("checks.html-inline-unsafe-script.message", match.text),
|
|
@@ -9554,10 +9754,10 @@ registry.register({
|
|
|
9554
9754
|
return findings;
|
|
9555
9755
|
}
|
|
9556
9756
|
});
|
|
9557
|
-
var
|
|
9757
|
+
var CHECK_ID23 = "security.html-referrer-leak";
|
|
9558
9758
|
var REMEDIATION17 = getCopy("remediation.security.html-referrer-leak");
|
|
9559
9759
|
registry.register({
|
|
9560
|
-
id:
|
|
9760
|
+
id: CHECK_ID23,
|
|
9561
9761
|
name: getCopy("checks.html-referrer-leak.name"),
|
|
9562
9762
|
description: getCopy("checks.html-referrer-leak.description"),
|
|
9563
9763
|
category: "security",
|
|
@@ -9573,8 +9773,8 @@ registry.register({
|
|
|
9573
9773
|
if (target.hasRel) continue;
|
|
9574
9774
|
const { line, column } = lineAndColumn5(content, target.index);
|
|
9575
9775
|
findings.push({
|
|
9576
|
-
id: `${
|
|
9577
|
-
checkId:
|
|
9776
|
+
id: `${CHECK_ID23}:${file.relativePath}:${line}:${column}`,
|
|
9777
|
+
checkId: CHECK_ID23,
|
|
9578
9778
|
category: "security",
|
|
9579
9779
|
severity: "medium",
|
|
9580
9780
|
message: getCopy("checks.html-referrer-leak.message", target.text),
|
|
@@ -9589,7 +9789,7 @@ registry.register({
|
|
|
9589
9789
|
return findings;
|
|
9590
9790
|
}
|
|
9591
9791
|
});
|
|
9592
|
-
var
|
|
9792
|
+
var CHECK_ID24 = "security.html-css-comments-secrets";
|
|
9593
9793
|
var REMEDIATION18 = getCopy("remediation.security.html-css-comments-secrets");
|
|
9594
9794
|
var SECRET_MARKER = /(?:api[_-]?key|firebase[_-]?config|google[_-]?api|token|secret|password|private[_-]?key|access[_-]?key)/i;
|
|
9595
9795
|
var SECRET_VALUE = /(?:[:=]\s*|["'`])([A-Za-z0-9+/=_-]{8,}|sk_[A-Za-z0-9/+=_-]{8,})/i;
|
|
@@ -9604,8 +9804,8 @@ function scanPattern(content, filePath, checkPrefix, pattern, messageText, findi
|
|
|
9604
9804
|
const value = valueMatch?.[1] ?? text22;
|
|
9605
9805
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9606
9806
|
findings.push({
|
|
9607
|
-
id: `${
|
|
9608
|
-
checkId:
|
|
9807
|
+
id: `${CHECK_ID24}:${filePath}:${line}:${column}:${checkPrefix}`,
|
|
9808
|
+
checkId: CHECK_ID24,
|
|
9609
9809
|
category: "security",
|
|
9610
9810
|
severity: "critical",
|
|
9611
9811
|
message: getCopy("checks.html-css-comments-secrets.message", messageText),
|
|
@@ -9618,7 +9818,7 @@ function scanPattern(content, filePath, checkPrefix, pattern, messageText, findi
|
|
|
9618
9818
|
}
|
|
9619
9819
|
}
|
|
9620
9820
|
registry.register({
|
|
9621
|
-
id:
|
|
9821
|
+
id: CHECK_ID24,
|
|
9622
9822
|
name: getCopy("checks.html-css-comments-secrets.name"),
|
|
9623
9823
|
description: getCopy("checks.html-css-comments-secrets.description"),
|
|
9624
9824
|
category: "security",
|
|
@@ -9637,7 +9837,7 @@ registry.register({
|
|
|
9637
9837
|
return findings;
|
|
9638
9838
|
}
|
|
9639
9839
|
});
|
|
9640
|
-
var
|
|
9840
|
+
var CHECK_ID25 = "security.css-insecure-import";
|
|
9641
9841
|
var REMEDIATION19 = getCopy("remediation.security.css-insecure-import");
|
|
9642
9842
|
var HTTP_URL_PATTERN = /^http:\/\//i;
|
|
9643
9843
|
var IMPORT_PATTERN = /@import\s+(?:url\(\s*)?(["']?)([^"')\s;]+)\1\s*\)?\s*;/gi;
|
|
@@ -9650,8 +9850,8 @@ function lineText(content, index) {
|
|
|
9650
9850
|
function pushFinding3(findings, filePath, content, matchText, index) {
|
|
9651
9851
|
const { line, column } = lineAndColumn5(content, index);
|
|
9652
9852
|
findings.push({
|
|
9653
|
-
id: `${
|
|
9654
|
-
checkId:
|
|
9853
|
+
id: `${CHECK_ID25}:${filePath}:${line}:${column}`,
|
|
9854
|
+
checkId: CHECK_ID25,
|
|
9655
9855
|
category: "security",
|
|
9656
9856
|
severity: "medium",
|
|
9657
9857
|
message: getCopy("checks.css-insecure-import.message", matchText),
|
|
@@ -9663,7 +9863,7 @@ function pushFinding3(findings, filePath, content, matchText, index) {
|
|
|
9663
9863
|
});
|
|
9664
9864
|
}
|
|
9665
9865
|
registry.register({
|
|
9666
|
-
id:
|
|
9866
|
+
id: CHECK_ID25,
|
|
9667
9867
|
name: getCopy("checks.css-insecure-import.name"),
|
|
9668
9868
|
description: getCopy("checks.css-insecure-import.description"),
|
|
9669
9869
|
category: "security",
|
|
@@ -9690,7 +9890,7 @@ registry.register({
|
|
|
9690
9890
|
return findings;
|
|
9691
9891
|
}
|
|
9692
9892
|
});
|
|
9693
|
-
var
|
|
9893
|
+
var CHECK_ID26 = "security.html-mixed-content";
|
|
9694
9894
|
var REMEDIATION20 = getCopy("remediation.security.html-mixed-content");
|
|
9695
9895
|
var TAG_RESOURCE_PATTERN = /<(script|link|iframe|img|form|object|embed)\b[^>]*?\b(src|href|action|data)\s*=\s*["'](http:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9696
9896
|
var STYLE_ATTR_PATTERN = /\bstyle\s*=\s*["']([\s\S]*?http:\/\/[\s\S]*?)["']/gi;
|
|
@@ -9699,8 +9899,8 @@ var CSS_URL_PATTERN = /url\(\s*(["']?)(http:\/\/[^"')]+)\1\s*\)/gi;
|
|
|
9699
9899
|
function pushFinding4(findings, filePath, content, index, matchText) {
|
|
9700
9900
|
const { line, column } = lineAndColumn5(content, index);
|
|
9701
9901
|
findings.push({
|
|
9702
|
-
id: `${
|
|
9703
|
-
checkId:
|
|
9902
|
+
id: `${CHECK_ID26}:${filePath}:${line}:${column}`,
|
|
9903
|
+
checkId: CHECK_ID26,
|
|
9704
9904
|
category: "security",
|
|
9705
9905
|
severity: "medium",
|
|
9706
9906
|
message: getCopy("checks.html-mixed-content.message", matchText),
|
|
@@ -9712,7 +9912,7 @@ function pushFinding4(findings, filePath, content, index, matchText) {
|
|
|
9712
9912
|
});
|
|
9713
9913
|
}
|
|
9714
9914
|
registry.register({
|
|
9715
|
-
id:
|
|
9915
|
+
id: CHECK_ID26,
|
|
9716
9916
|
name: getCopy("checks.html-mixed-content.name"),
|
|
9717
9917
|
description: getCopy("checks.html-mixed-content.description"),
|
|
9718
9918
|
category: "security",
|
|
@@ -9750,7 +9950,7 @@ registry.register({
|
|
|
9750
9950
|
return findings;
|
|
9751
9951
|
}
|
|
9752
9952
|
});
|
|
9753
|
-
var
|
|
9953
|
+
var CHECK_ID27 = "security.html-external-asset-integrity";
|
|
9754
9954
|
var REMEDIATION21 = getCopy("remediation.security.html-external-asset-integrity");
|
|
9755
9955
|
var SCRIPT_PATTERN = /<script\b[^>]*?\bsrc\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9756
9956
|
var STYLESHEET_PATTERN = /<link\b(?=[^>]*\brel\s*=\s*["'][^"']*\bstylesheet\b[^"']*["'])[^>]*?\bhref\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
@@ -9765,7 +9965,7 @@ function isLocalDevelopmentUrl(rawUrl) {
|
|
|
9765
9965
|
}
|
|
9766
9966
|
}
|
|
9767
9967
|
registry.register({
|
|
9768
|
-
id:
|
|
9968
|
+
id: CHECK_ID27,
|
|
9769
9969
|
name: getCopy("checks.html-external-asset-integrity.name"),
|
|
9770
9970
|
description: getCopy("checks.html-external-asset-integrity.description"),
|
|
9771
9971
|
category: "security",
|
|
@@ -9786,8 +9986,8 @@ registry.register({
|
|
|
9786
9986
|
if (hasIntegrity && hasCrossorigin) continue;
|
|
9787
9987
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9788
9988
|
findings.push({
|
|
9789
|
-
id: `${
|
|
9790
|
-
checkId:
|
|
9989
|
+
id: `${CHECK_ID27}:${file.relativePath}:${line}:${column}`,
|
|
9990
|
+
checkId: CHECK_ID27,
|
|
9791
9991
|
category: "security",
|
|
9792
9992
|
severity: "medium",
|
|
9793
9993
|
message: getCopy("checks.html-external-asset-integrity.message", tagText),
|
|
@@ -9807,8 +10007,8 @@ registry.register({
|
|
|
9807
10007
|
if (hasIntegrity && hasCrossorigin) continue;
|
|
9808
10008
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9809
10009
|
findings.push({
|
|
9810
|
-
id: `${
|
|
9811
|
-
checkId:
|
|
10010
|
+
id: `${CHECK_ID27}:${file.relativePath}:${line}:${column}`,
|
|
10011
|
+
checkId: CHECK_ID27,
|
|
9812
10012
|
category: "security",
|
|
9813
10013
|
severity: "medium",
|
|
9814
10014
|
message: getCopy("checks.html-external-asset-integrity.message", tagText),
|
|
@@ -9823,7 +10023,7 @@ registry.register({
|
|
|
9823
10023
|
return findings;
|
|
9824
10024
|
}
|
|
9825
10025
|
});
|
|
9826
|
-
var
|
|
10026
|
+
var CHECK_ID28 = "security.html-insecure-form-action";
|
|
9827
10027
|
var REMEDIATION22 = getCopy("remediation.security.html-insecure-form-action");
|
|
9828
10028
|
var FORM_ACTION_PATTERN = /<form\b[^>]*?\baction\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))[^>]*?>/gi;
|
|
9829
10029
|
var LOCAL_DEV_URL_PATTERN = /^(?:https?:)?\/\/(?:localhost|127\.0\.0\.1|\[::1\]|::1)(?::\d+)?(?:\/|$)/i;
|
|
@@ -9837,7 +10037,7 @@ function lineAndColumn7(content, index) {
|
|
|
9837
10037
|
};
|
|
9838
10038
|
}
|
|
9839
10039
|
registry.register({
|
|
9840
|
-
id:
|
|
10040
|
+
id: CHECK_ID28,
|
|
9841
10041
|
name: getCopy("checks.html-insecure-form-action.name"),
|
|
9842
10042
|
description: getCopy("checks.html-insecure-form-action.description"),
|
|
9843
10043
|
category: "security",
|
|
@@ -9855,8 +10055,8 @@ registry.register({
|
|
|
9855
10055
|
if (LOCAL_DEV_URL_PATTERN.test(action)) continue;
|
|
9856
10056
|
const { line, column } = lineAndColumn7(content, match.index ?? 0);
|
|
9857
10057
|
findings.push({
|
|
9858
|
-
id: `${
|
|
9859
|
-
checkId:
|
|
10058
|
+
id: `${CHECK_ID28}:${file.relativePath}:${line}:${column}`,
|
|
10059
|
+
checkId: CHECK_ID28,
|
|
9860
10060
|
category: "security",
|
|
9861
10061
|
severity: "medium",
|
|
9862
10062
|
message: getCopy("checks.html-insecure-form-action.message", match[0] ?? action),
|
|
@@ -9871,7 +10071,7 @@ registry.register({
|
|
|
9871
10071
|
return findings;
|
|
9872
10072
|
}
|
|
9873
10073
|
});
|
|
9874
|
-
var
|
|
10074
|
+
var CHECK_ID29 = "security.html-iframe-sandbox";
|
|
9875
10075
|
var REMEDIATION23 = getCopy("remediation.security.html-iframe-sandbox");
|
|
9876
10076
|
var IFRAME_PATTERN = /<iframe\b[^>]*?>/gi;
|
|
9877
10077
|
var SRC_PATTERN = /\bsrc\s*=\s*["']([^"']+)["']/i;
|
|
@@ -9886,7 +10086,7 @@ function hasSandboxAttribute(tagText) {
|
|
|
9886
10086
|
return { present: true, value: match[1] ?? match[2] ?? match[3] ?? "" };
|
|
9887
10087
|
}
|
|
9888
10088
|
registry.register({
|
|
9889
|
-
id:
|
|
10089
|
+
id: CHECK_ID29,
|
|
9890
10090
|
name: getCopy("checks.html-iframe-sandbox.name"),
|
|
9891
10091
|
description: getCopy("checks.html-iframe-sandbox.description"),
|
|
9892
10092
|
category: "security",
|
|
@@ -9909,8 +10109,8 @@ registry.register({
|
|
|
9909
10109
|
if (sandbox.present && !hasBroadEscape) continue;
|
|
9910
10110
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
9911
10111
|
findings.push({
|
|
9912
|
-
id: `${
|
|
9913
|
-
checkId:
|
|
10112
|
+
id: `${CHECK_ID29}:${file.relativePath}:${line}:${column}`,
|
|
10113
|
+
checkId: CHECK_ID29,
|
|
9914
10114
|
category: "security",
|
|
9915
10115
|
severity: "medium",
|
|
9916
10116
|
message: getCopy("checks.html-iframe-sandbox.message", tagText),
|
|
@@ -9925,7 +10125,7 @@ registry.register({
|
|
|
9925
10125
|
return findings;
|
|
9926
10126
|
}
|
|
9927
10127
|
});
|
|
9928
|
-
var
|
|
10128
|
+
var CHECK_ID30 = "security.html-unsafe-embed";
|
|
9929
10129
|
var REMEDIATION24 = getCopy("remediation.security.html-unsafe-embed");
|
|
9930
10130
|
var OBJECT_PATTERN = /<object\b[^>]*?\bdata\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
9931
10131
|
var EMBED_PATTERN = /<embed\b[^>]*?\bsrc\s*=\s*["'](https:\/\/[^"']+)["'][^>]*?>/gi;
|
|
@@ -9946,8 +10146,8 @@ function getTypeValue(tagText) {
|
|
|
9946
10146
|
function pushFindings(findings, filePath, content, index, tagText) {
|
|
9947
10147
|
const { line, column } = lineAndColumn8(content, index);
|
|
9948
10148
|
findings.push({
|
|
9949
|
-
id: `${
|
|
9950
|
-
checkId:
|
|
10149
|
+
id: `${CHECK_ID30}:${filePath}:${line}:${column}`,
|
|
10150
|
+
checkId: CHECK_ID30,
|
|
9951
10151
|
category: "security",
|
|
9952
10152
|
severity: "medium",
|
|
9953
10153
|
message: getCopy("checks.html-unsafe-embed.message", tagText),
|
|
@@ -9959,7 +10159,7 @@ function pushFindings(findings, filePath, content, index, tagText) {
|
|
|
9959
10159
|
});
|
|
9960
10160
|
}
|
|
9961
10161
|
registry.register({
|
|
9962
|
-
id:
|
|
10162
|
+
id: CHECK_ID30,
|
|
9963
10163
|
name: getCopy("checks.html-unsafe-embed.name"),
|
|
9964
10164
|
description: getCopy("checks.html-unsafe-embed.description"),
|
|
9965
10165
|
category: "security",
|
|
@@ -9991,7 +10191,7 @@ registry.register({
|
|
|
9991
10191
|
return findings;
|
|
9992
10192
|
}
|
|
9993
10193
|
});
|
|
9994
|
-
var
|
|
10194
|
+
var CHECK_ID31 = "security.html-meta-redirect";
|
|
9995
10195
|
var REMEDIATION25 = getCopy("remediation.security.html-meta-redirect");
|
|
9996
10196
|
var META_REFRESH_PATTERN = /<meta\b[^>]*\bhttp-equiv\s*=\s*["']refresh["'][^>]*>/gi;
|
|
9997
10197
|
var CONTENT_PATTERN = /\bcontent\s*=\s*["']([^"']+)["']/i;
|
|
@@ -10008,7 +10208,7 @@ function parseRefreshContent(value) {
|
|
|
10008
10208
|
};
|
|
10009
10209
|
}
|
|
10010
10210
|
registry.register({
|
|
10011
|
-
id:
|
|
10211
|
+
id: CHECK_ID31,
|
|
10012
10212
|
name: getCopy("checks.html-meta-redirect.name"),
|
|
10013
10213
|
description: getCopy("checks.html-meta-redirect.description"),
|
|
10014
10214
|
category: "security",
|
|
@@ -10031,8 +10231,8 @@ registry.register({
|
|
|
10031
10231
|
if (LOCAL_DEV_URL_PATTERN4.test(parsed.url)) continue;
|
|
10032
10232
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10033
10233
|
findings.push({
|
|
10034
|
-
id: `${
|
|
10035
|
-
checkId:
|
|
10234
|
+
id: `${CHECK_ID31}:${file.relativePath}:${line}:${column}`,
|
|
10235
|
+
checkId: CHECK_ID31,
|
|
10036
10236
|
category: "security",
|
|
10037
10237
|
severity: "medium",
|
|
10038
10238
|
message: getCopy("checks.html-meta-redirect.message", tagText),
|
|
@@ -10047,7 +10247,7 @@ registry.register({
|
|
|
10047
10247
|
return findings;
|
|
10048
10248
|
}
|
|
10049
10249
|
});
|
|
10050
|
-
var
|
|
10250
|
+
var CHECK_ID32 = "security.html-base-tag-hijack";
|
|
10051
10251
|
var REMEDIATION26 = getCopy("remediation.security.html-base-tag-hijack");
|
|
10052
10252
|
var BASE_PATTERN = /<base\b[^>]*?>/gi;
|
|
10053
10253
|
var HREF_PATTERN = /\bhref\s*=\s*["']([^"']+)["']/i;
|
|
@@ -10063,7 +10263,7 @@ function isOutsideHead(content, index) {
|
|
|
10063
10263
|
return false;
|
|
10064
10264
|
}
|
|
10065
10265
|
registry.register({
|
|
10066
|
-
id:
|
|
10266
|
+
id: CHECK_ID32,
|
|
10067
10267
|
name: getCopy("checks.html-base-tag-hijack.name"),
|
|
10068
10268
|
description: getCopy("checks.html-base-tag-hijack.description"),
|
|
10069
10269
|
category: "security",
|
|
@@ -10085,8 +10285,8 @@ registry.register({
|
|
|
10085
10285
|
if (!externalHref && !targetBlank && !outsideHead) continue;
|
|
10086
10286
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10087
10287
|
findings.push({
|
|
10088
|
-
id: `${
|
|
10089
|
-
checkId:
|
|
10288
|
+
id: `${CHECK_ID32}:${file.relativePath}:${line}:${column}`,
|
|
10289
|
+
checkId: CHECK_ID32,
|
|
10090
10290
|
category: "security",
|
|
10091
10291
|
severity: "medium",
|
|
10092
10292
|
message: getCopy("checks.html-base-tag-hijack.message", tagText),
|
|
@@ -10101,12 +10301,12 @@ registry.register({
|
|
|
10101
10301
|
return findings;
|
|
10102
10302
|
}
|
|
10103
10303
|
});
|
|
10104
|
-
var
|
|
10304
|
+
var CHECK_ID33 = "security.html-javascript-uri";
|
|
10105
10305
|
var REMEDIATION27 = getCopy("remediation.security.html-javascript-uri");
|
|
10106
10306
|
var TAG_PATTERN = /<(a|form|iframe|object|embed)\b[^>]*?\b(href|action|src|data)\s*=\s*["'](javascript:[^"']+)["'][^>]*?>/gi;
|
|
10107
10307
|
var VOID_PATTERN = /^javascript:\s*(?:void\s*\(?\s*0\s*\)?)(?:\s*;)?\s*$/i;
|
|
10108
10308
|
registry.register({
|
|
10109
|
-
id:
|
|
10309
|
+
id: CHECK_ID33,
|
|
10110
10310
|
name: getCopy("checks.html-javascript-uri.name"),
|
|
10111
10311
|
description: getCopy("checks.html-javascript-uri.description"),
|
|
10112
10312
|
category: "security",
|
|
@@ -10124,8 +10324,8 @@ registry.register({
|
|
|
10124
10324
|
if (VOID_PATTERN.test(jsUrl)) continue;
|
|
10125
10325
|
const { line, column } = lineAndColumn5(content, match.index ?? 0);
|
|
10126
10326
|
findings.push({
|
|
10127
|
-
id: `${
|
|
10128
|
-
checkId:
|
|
10327
|
+
id: `${CHECK_ID33}:${file.relativePath}:${line}:${column}`,
|
|
10328
|
+
checkId: CHECK_ID33,
|
|
10129
10329
|
category: "security",
|
|
10130
10330
|
severity: "low",
|
|
10131
10331
|
message: getCopy("checks.html-javascript-uri.message", tagText),
|
|
@@ -10140,7 +10340,7 @@ registry.register({
|
|
|
10140
10340
|
return findings;
|
|
10141
10341
|
}
|
|
10142
10342
|
});
|
|
10143
|
-
var
|
|
10343
|
+
var CHECK_ID34 = "security.html-import-map-integrity";
|
|
10144
10344
|
var REMEDIATION28 = getCopy("remediation.security.html-import-map-integrity");
|
|
10145
10345
|
var IMPORTMAP_SCRIPT_PATTERN = /<script\b[^>]*\btype\s*=\s*["']importmap["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
10146
10346
|
var IMPORTMAP_SRC_PATTERN = /<script\b[^>]*\btype\s*=\s*["']importmap["'][^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi;
|
|
@@ -10161,8 +10361,8 @@ function isExternalUrl(value) {
|
|
|
10161
10361
|
function pushFinding5(findings, filePath, content, index, matchText) {
|
|
10162
10362
|
const { line, column } = lineAndColumn9(content, index);
|
|
10163
10363
|
findings.push({
|
|
10164
|
-
id: `${
|
|
10165
|
-
checkId:
|
|
10364
|
+
id: `${CHECK_ID34}:${filePath}:${line}:${column}`,
|
|
10365
|
+
checkId: CHECK_ID34,
|
|
10166
10366
|
category: "security",
|
|
10167
10367
|
severity: "medium",
|
|
10168
10368
|
message: getCopy("checks.html-import-map-integrity.message", matchText),
|
|
@@ -10187,7 +10387,7 @@ function scanImportMapValue(findings, filePath, content, value, contextLabel, in
|
|
|
10187
10387
|
}
|
|
10188
10388
|
}
|
|
10189
10389
|
registry.register({
|
|
10190
|
-
id:
|
|
10390
|
+
id: CHECK_ID34,
|
|
10191
10391
|
name: getCopy("checks.html-import-map-integrity.name"),
|
|
10192
10392
|
description: getCopy("checks.html-import-map-integrity.description"),
|
|
10193
10393
|
category: "security",
|
|
@@ -10220,7 +10420,7 @@ registry.register({
|
|
|
10220
10420
|
return findings;
|
|
10221
10421
|
}
|
|
10222
10422
|
});
|
|
10223
|
-
var
|
|
10423
|
+
var CHECK_ID35 = "security.css-data-exfiltration";
|
|
10224
10424
|
var REMEDIATION29 = getCopy("remediation.security.css-data-exfiltration");
|
|
10225
10425
|
var ATTR_SELECTOR_PATTERN = /\[[^\]]*(?:value|type|name|email|tel|address|cc-number|cc-exp)[^\]]*(?:\^=|\$=|\*=|~=|\|=)[^\]]*\]/i;
|
|
10226
10426
|
var EXTERNAL_HTTP_URL_PATTERN = /url\(\s*(["']?)http:\/\/[^"')]+\1\s*\)/i;
|
|
@@ -10238,8 +10438,8 @@ function lineAndColumn10(content, index) {
|
|
|
10238
10438
|
function pushFinding6(findings, filePath, content, index, matchText) {
|
|
10239
10439
|
const { line, column } = lineAndColumn10(content, index);
|
|
10240
10440
|
findings.push({
|
|
10241
|
-
id: `${
|
|
10242
|
-
checkId:
|
|
10441
|
+
id: `${CHECK_ID35}:${filePath}:${line}:${column}`,
|
|
10442
|
+
checkId: CHECK_ID35,
|
|
10243
10443
|
category: "security",
|
|
10244
10444
|
severity: "medium",
|
|
10245
10445
|
message: getCopy("checks.css-data-exfiltration.message", matchText),
|
|
@@ -10260,7 +10460,7 @@ function scanCss(findings, filePath, scanContent, originalContent, offset = 0) {
|
|
|
10260
10460
|
}
|
|
10261
10461
|
}
|
|
10262
10462
|
registry.register({
|
|
10263
|
-
id:
|
|
10463
|
+
id: CHECK_ID35,
|
|
10264
10464
|
name: getCopy("checks.css-data-exfiltration.name"),
|
|
10265
10465
|
description: getCopy("checks.css-data-exfiltration.description"),
|
|
10266
10466
|
category: "security",
|
|
@@ -10292,7 +10492,7 @@ registry.register({
|
|
|
10292
10492
|
return findings;
|
|
10293
10493
|
}
|
|
10294
10494
|
});
|
|
10295
|
-
var
|
|
10495
|
+
var CHECK_ID36 = "security.css-external-font-integrity";
|
|
10296
10496
|
var REMEDIATION30 = getCopy("remediation.security.css-external-font-integrity");
|
|
10297
10497
|
var FONT_FACE_PATTERN = /@font-face\b[\s\S]*?\}/gi;
|
|
10298
10498
|
var URL_PATTERN = /url\(\s*(["']?)([^"')]+)\1\s*\)/gi;
|
|
@@ -10324,7 +10524,7 @@ function isPublicCdnFont(url) {
|
|
|
10324
10524
|
}
|
|
10325
10525
|
}
|
|
10326
10526
|
registry.register({
|
|
10327
|
-
id:
|
|
10527
|
+
id: CHECK_ID36,
|
|
10328
10528
|
name: getCopy("checks.css-external-font-integrity.name"),
|
|
10329
10529
|
description: getCopy("checks.css-external-font-integrity.description"),
|
|
10330
10530
|
category: "security",
|
|
@@ -10345,8 +10545,8 @@ registry.register({
|
|
|
10345
10545
|
if (!isPublicCdnFont(url)) continue;
|
|
10346
10546
|
const { line, column } = lineAndColumn11(content, face.index ?? 0);
|
|
10347
10547
|
findings.push({
|
|
10348
|
-
id: `${
|
|
10349
|
-
checkId:
|
|
10548
|
+
id: `${CHECK_ID36}:${file.relativePath}:${line}:${column}`,
|
|
10549
|
+
checkId: CHECK_ID36,
|
|
10350
10550
|
category: "security",
|
|
10351
10551
|
severity: "medium",
|
|
10352
10552
|
message: getCopy("checks.css-external-font-integrity.message", match[0] ?? url),
|
|
@@ -10362,7 +10562,7 @@ registry.register({
|
|
|
10362
10562
|
return findings;
|
|
10363
10563
|
}
|
|
10364
10564
|
});
|
|
10365
|
-
var
|
|
10565
|
+
var CHECK_ID37 = "security.insecure-random";
|
|
10366
10566
|
var QUERIES8 = {
|
|
10367
10567
|
javascript: `
|
|
10368
10568
|
(call_expression
|
|
@@ -10431,7 +10631,7 @@ var QUERIES8 = {
|
|
|
10431
10631
|
};
|
|
10432
10632
|
registry.register(
|
|
10433
10633
|
createTreeSitterCheck({
|
|
10434
|
-
id:
|
|
10634
|
+
id: CHECK_ID37,
|
|
10435
10635
|
nameKey: "checks.insecure-random.name",
|
|
10436
10636
|
descriptionKey: "checks.insecure-random.description",
|
|
10437
10637
|
category: "security",
|
|
@@ -10443,7 +10643,7 @@ registry.register(
|
|
|
10443
10643
|
remediationKey: "remediation.security.insecure-random"
|
|
10444
10644
|
})
|
|
10445
10645
|
);
|
|
10446
|
-
var
|
|
10646
|
+
var CHECK_ID38 = "security.weak-key-entropy";
|
|
10447
10647
|
var REMEDIATION31 = getCopy("remediation.security.weak-key-entropy");
|
|
10448
10648
|
var RANDOM_BYTES_PATTERN = /randomBytes\s*\(\s*(\d+)\s*\)/g;
|
|
10449
10649
|
var WEAK_KEY_CONTEXT = /(?:key|token|secret|id|license|password|session|csrf|nonce|salt|iv)/i;
|
|
@@ -10456,7 +10656,7 @@ function lineAndColumn12(content, index) {
|
|
|
10456
10656
|
};
|
|
10457
10657
|
}
|
|
10458
10658
|
registry.register({
|
|
10459
|
-
id:
|
|
10659
|
+
id: CHECK_ID38,
|
|
10460
10660
|
name: getCopy("checks.weak-key-entropy.name"),
|
|
10461
10661
|
description: getCopy("checks.weak-key-entropy.description"),
|
|
10462
10662
|
category: "security",
|
|
@@ -10478,8 +10678,8 @@ registry.register({
|
|
|
10478
10678
|
if (!WEAK_KEY_CONTEXT.test(lineContent) && !WEAK_KEY_CONTEXT.test(content.slice(Math.max(0, matchIndex - 200), matchIndex))) continue;
|
|
10479
10679
|
const { line, column } = lineAndColumn12(content, matchIndex);
|
|
10480
10680
|
findings.push({
|
|
10481
|
-
id: `${
|
|
10482
|
-
checkId:
|
|
10681
|
+
id: `${CHECK_ID38}:${file.relativePath}:${line}:${column}`,
|
|
10682
|
+
checkId: CHECK_ID38,
|
|
10483
10683
|
category: "security",
|
|
10484
10684
|
severity: "high",
|
|
10485
10685
|
message: getCopy("checks.weak-key-entropy.message", match[0], String(byteCount)),
|
|
@@ -10494,7 +10694,7 @@ registry.register({
|
|
|
10494
10694
|
return findings;
|
|
10495
10695
|
}
|
|
10496
10696
|
});
|
|
10497
|
-
var
|
|
10697
|
+
var CHECK_ID39 = "security.insecure-api-url-default";
|
|
10498
10698
|
var REMEDIATION32 = getCopy("remediation.security.insecure-api-url-default");
|
|
10499
10699
|
var HTTP_URL_DEFAULT_PATTERN = /(?:['"`])(https?:\/\/[^\s'"`]+)(?:['"`])/g;
|
|
10500
10700
|
var API_CONTEXT = /(?:api[_-]?url|base[_-]?url|endpoint|server|host|webhook|resend|paddle|stripe|sendgrid|mailgun|postmark)/i;
|
|
@@ -10509,7 +10709,7 @@ function lineAndColumn13(content, index) {
|
|
|
10509
10709
|
};
|
|
10510
10710
|
}
|
|
10511
10711
|
registry.register({
|
|
10512
|
-
id:
|
|
10712
|
+
id: CHECK_ID39,
|
|
10513
10713
|
name: getCopy("checks.insecure-api-url-default.name"),
|
|
10514
10714
|
description: getCopy("checks.insecure-api-url-default.description"),
|
|
10515
10715
|
category: "security",
|
|
@@ -10534,8 +10734,8 @@ registry.register({
|
|
|
10534
10734
|
if (!isEnvDefault && !API_CONTEXT.test(matchLine)) continue;
|
|
10535
10735
|
const { line, column } = lineAndColumn13(content, matchIndex);
|
|
10536
10736
|
findings.push({
|
|
10537
|
-
id: `${
|
|
10538
|
-
checkId:
|
|
10737
|
+
id: `${CHECK_ID39}:${file.relativePath}:${line}:${column}`,
|
|
10738
|
+
checkId: CHECK_ID39,
|
|
10539
10739
|
category: "security",
|
|
10540
10740
|
severity: "high",
|
|
10541
10741
|
message: getCopy("checks.insecure-api-url-default.message", url),
|
|
@@ -10550,7 +10750,7 @@ registry.register({
|
|
|
10550
10750
|
return findings;
|
|
10551
10751
|
}
|
|
10552
10752
|
});
|
|
10553
|
-
var
|
|
10753
|
+
var CHECK_ID40 = "security.dynamic-sql-columns";
|
|
10554
10754
|
var REMEDIATION33 = getCopy("remediation.security.dynamic-sql-columns");
|
|
10555
10755
|
var SQL_SET_INTERPOLATION = /\bSET\s+.*\$\{[^}]*\}/gis;
|
|
10556
10756
|
var SQL_SET_TEMPLATE = /\bSET\s+.*`[^`]*\$\{[^}]*\}[^`]*`/gis;
|
|
@@ -10564,7 +10764,7 @@ function lineAndColumn14(content, index) {
|
|
|
10564
10764
|
};
|
|
10565
10765
|
}
|
|
10566
10766
|
registry.register({
|
|
10567
|
-
id:
|
|
10767
|
+
id: CHECK_ID40,
|
|
10568
10768
|
name: getCopy("checks.dynamic-sql-columns.name"),
|
|
10569
10769
|
description: getCopy("checks.dynamic-sql-columns.description"),
|
|
10570
10770
|
category: "security",
|
|
@@ -10585,11 +10785,11 @@ registry.register({
|
|
|
10585
10785
|
for (const match of content.matchAll(regex)) {
|
|
10586
10786
|
const matchIndex = match.index ?? 0;
|
|
10587
10787
|
const { line, column } = lineAndColumn14(content, matchIndex);
|
|
10588
|
-
const dedupKey = `${
|
|
10788
|
+
const dedupKey = `${CHECK_ID40}:${file.relativePath}:${line}`;
|
|
10589
10789
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10590
10790
|
findings.push({
|
|
10591
10791
|
id: dedupKey,
|
|
10592
|
-
checkId:
|
|
10792
|
+
checkId: CHECK_ID40,
|
|
10593
10793
|
category: "security",
|
|
10594
10794
|
severity: "medium",
|
|
10595
10795
|
message: getCopy("checks.dynamic-sql-columns.message", label),
|
|
@@ -10605,7 +10805,7 @@ registry.register({
|
|
|
10605
10805
|
return findings;
|
|
10606
10806
|
}
|
|
10607
10807
|
});
|
|
10608
|
-
var
|
|
10808
|
+
var CHECK_ID41 = "security.weak-email-validation";
|
|
10609
10809
|
var REMEDIATION34 = getCopy("remediation.security.weak-email-validation");
|
|
10610
10810
|
var WEAK_EMAIL_REGEX_LINE = /\/\^[^/]*@[^/]*\\\.[^/]{0,4}\$\/[gimsuy]*/g;
|
|
10611
10811
|
var LOOSE_REGEX_CLASS = /\[\^?\\?s@\]\+@\[\^?\\?s@\]\+\\\.\[\^?\\?s@\]\+/g;
|
|
@@ -10619,7 +10819,7 @@ function lineAndColumn15(content, index) {
|
|
|
10619
10819
|
};
|
|
10620
10820
|
}
|
|
10621
10821
|
registry.register({
|
|
10622
|
-
id:
|
|
10822
|
+
id: CHECK_ID41,
|
|
10623
10823
|
name: getCopy("checks.weak-email-validation.name"),
|
|
10624
10824
|
description: getCopy("checks.weak-email-validation.description"),
|
|
10625
10825
|
category: "security",
|
|
@@ -10640,11 +10840,11 @@ registry.register({
|
|
|
10640
10840
|
for (const match of content.matchAll(regex)) {
|
|
10641
10841
|
const matchIndex = match.index ?? 0;
|
|
10642
10842
|
const { line, column } = lineAndColumn15(content, matchIndex);
|
|
10643
|
-
const dedupKey = `${
|
|
10843
|
+
const dedupKey = `${CHECK_ID41}:${file.relativePath}:${line}`;
|
|
10644
10844
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10645
10845
|
findings.push({
|
|
10646
10846
|
id: dedupKey,
|
|
10647
|
-
checkId:
|
|
10847
|
+
checkId: CHECK_ID41,
|
|
10648
10848
|
category: "security",
|
|
10649
10849
|
severity: "medium",
|
|
10650
10850
|
message: getCopy("checks.weak-email-validation.message", `${match[0]} (${label})`),
|
|
@@ -10660,7 +10860,7 @@ registry.register({
|
|
|
10660
10860
|
return findings;
|
|
10661
10861
|
}
|
|
10662
10862
|
});
|
|
10663
|
-
var
|
|
10863
|
+
var CHECK_ID42 = "security.idempotency-key-leak";
|
|
10664
10864
|
var REMEDIATION35 = getCopy("remediation.security.idempotency-key-leak");
|
|
10665
10865
|
var IDEMPOTENCY_CONTEXT = /idempotency[_-]?key|idempotency[_-]?token/i;
|
|
10666
10866
|
var TEMPLATE_LITERAL_INTERPOLATION = new RegExp("`[^`]*\\$\\{[^}]+\\}[^`]*`", "g");
|
|
@@ -10674,7 +10874,7 @@ function lineAndColumn16(content, index) {
|
|
|
10674
10874
|
};
|
|
10675
10875
|
}
|
|
10676
10876
|
registry.register({
|
|
10677
|
-
id:
|
|
10877
|
+
id: CHECK_ID42,
|
|
10678
10878
|
name: getCopy("checks.idempotency-key-leak.name"),
|
|
10679
10879
|
description: getCopy("checks.idempotency-key-leak.description"),
|
|
10680
10880
|
category: "security",
|
|
@@ -10693,11 +10893,11 @@ registry.register({
|
|
|
10693
10893
|
if (!STRUCTURED_ID_PATTERN.test(templateStr)) continue;
|
|
10694
10894
|
const matchIndex = match.index ?? 0;
|
|
10695
10895
|
const { line, column } = lineAndColumn16(content, matchIndex);
|
|
10696
|
-
const dedupKey = `${
|
|
10896
|
+
const dedupKey = `${CHECK_ID42}:${file.relativePath}:${line}`;
|
|
10697
10897
|
if (findings.some((f) => f.id === dedupKey)) continue;
|
|
10698
10898
|
findings.push({
|
|
10699
10899
|
id: dedupKey,
|
|
10700
|
-
checkId:
|
|
10900
|
+
checkId: CHECK_ID42,
|
|
10701
10901
|
category: "security",
|
|
10702
10902
|
severity: "medium",
|
|
10703
10903
|
message: getCopy("checks.idempotency-key-leak.message", templateStr.slice(0, 80)),
|
|
@@ -10712,7 +10912,7 @@ registry.register({
|
|
|
10712
10912
|
return findings;
|
|
10713
10913
|
}
|
|
10714
10914
|
});
|
|
10715
|
-
var
|
|
10915
|
+
var CHECK_ID43 = "data-exposure.plaintext-secret-in-email";
|
|
10716
10916
|
var REMEDIATION36 = getCopy("remediation.data-exposure.plaintext-secret-in-email");
|
|
10717
10917
|
var EMAIL_FILE_PATTERN = /email|mail|notify|notification/i;
|
|
10718
10918
|
var SECRET_IN_TEXT_PATTERN = /(?:license[_-]?key|api[_-]?key|secret[_-]?key|token|password|auth[_-]?code|verification[_-]?code)\b/i;
|
|
@@ -10726,7 +10926,7 @@ function lineAndColumn17(content, index) {
|
|
|
10726
10926
|
};
|
|
10727
10927
|
}
|
|
10728
10928
|
registry.register({
|
|
10729
|
-
id:
|
|
10929
|
+
id: CHECK_ID43,
|
|
10730
10930
|
name: getCopy("checks.plaintext-secret-in-email.name"),
|
|
10731
10931
|
description: getCopy("checks.plaintext-secret-in-email.description"),
|
|
10732
10932
|
category: "data-exposure",
|
|
@@ -10748,8 +10948,8 @@ registry.register({
|
|
|
10748
10948
|
const { line: lineNum, column } = lineAndColumn17(content, lineIndex);
|
|
10749
10949
|
const secretMatch = SECRET_IN_TEXT_PATTERN.exec(lineText2);
|
|
10750
10950
|
findings.push({
|
|
10751
|
-
id: `${
|
|
10752
|
-
checkId:
|
|
10951
|
+
id: `${CHECK_ID43}:${file.relativePath}:${lineNum}:${column}`,
|
|
10952
|
+
checkId: CHECK_ID43,
|
|
10753
10953
|
category: "data-exposure",
|
|
10754
10954
|
severity: "medium",
|
|
10755
10955
|
message: getCopy("checks.plaintext-secret-in-email.message", secretMatch?.[0] ?? "secret"),
|
|
@@ -12959,7 +13159,7 @@ registry.register(
|
|
|
12959
13159
|
attribution: "CWE-94"
|
|
12960
13160
|
})
|
|
12961
13161
|
);
|
|
12962
|
-
var
|
|
13162
|
+
var CHECK_ID44 = "security.missing-csrf-protection";
|
|
12963
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;
|
|
12964
13164
|
var QUERIES9 = {
|
|
12965
13165
|
php: `
|
|
@@ -13016,7 +13216,7 @@ async function hasProjectCsrfProtection(files) {
|
|
|
13016
13216
|
return false;
|
|
13017
13217
|
}
|
|
13018
13218
|
var check = {
|
|
13019
|
-
id:
|
|
13219
|
+
id: CHECK_ID44,
|
|
13020
13220
|
name: resolveCopy3("checks.missing-csrf-protection.name"),
|
|
13021
13221
|
description: resolveCopy3("checks.missing-csrf-protection.description"),
|
|
13022
13222
|
category: "security",
|
|
@@ -13056,8 +13256,8 @@ var check = {
|
|
|
13056
13256
|
const sample = eligible.find((f) => f.language === lang);
|
|
13057
13257
|
if (!sample) continue;
|
|
13058
13258
|
findings.push({
|
|
13059
|
-
id: `${
|
|
13060
|
-
checkId:
|
|
13259
|
+
id: `${CHECK_ID44}:${sample.relativePath}:degraded:${lang}`,
|
|
13260
|
+
checkId: CHECK_ID44,
|
|
13061
13261
|
category: "security",
|
|
13062
13262
|
severity: "info",
|
|
13063
13263
|
message: resolveCopy3("checks.missing-csrf-protection.degraded", sample.relativePath, lang),
|
|
@@ -13078,8 +13278,8 @@ var check = {
|
|
|
13078
13278
|
handlerCount > 1 ? `${handlerCount} state-changing handlers (e.g. ${exampleRoute})` : exampleRoute
|
|
13079
13279
|
);
|
|
13080
13280
|
const finding = {
|
|
13081
|
-
id: `${
|
|
13082
|
-
checkId:
|
|
13281
|
+
id: `${CHECK_ID44}:project:${anchor.relativePath}:1`,
|
|
13282
|
+
checkId: CHECK_ID44,
|
|
13083
13283
|
category: "security",
|
|
13084
13284
|
severity: "medium",
|
|
13085
13285
|
message,
|
|
@@ -13094,7 +13294,7 @@ var check = {
|
|
|
13094
13294
|
}
|
|
13095
13295
|
};
|
|
13096
13296
|
registry.register(check);
|
|
13097
|
-
var
|
|
13297
|
+
var CHECK_ID45 = "security.weak-session-id";
|
|
13098
13298
|
var QUERIES10 = {
|
|
13099
13299
|
php: `
|
|
13100
13300
|
(function_call_expression
|
|
@@ -13181,7 +13381,7 @@ var QUERIES10 = {
|
|
|
13181
13381
|
};
|
|
13182
13382
|
registry.register(
|
|
13183
13383
|
createTreeSitterCheck({
|
|
13184
|
-
id:
|
|
13384
|
+
id: CHECK_ID45,
|
|
13185
13385
|
nameKey: "checks.weak-session-id.name",
|
|
13186
13386
|
descriptionKey: "checks.weak-session-id.description",
|
|
13187
13387
|
category: "security",
|
|
@@ -13199,7 +13399,7 @@ registry.register(
|
|
|
13199
13399
|
}
|
|
13200
13400
|
})
|
|
13201
13401
|
);
|
|
13202
|
-
var
|
|
13402
|
+
var CHECK_ID46 = "security.rails-mass-assignment";
|
|
13203
13403
|
var QUERIES11 = {
|
|
13204
13404
|
ruby: `
|
|
13205
13405
|
(call
|
|
@@ -13222,7 +13422,7 @@ var QUERIES11 = {
|
|
|
13222
13422
|
};
|
|
13223
13423
|
registry.register(
|
|
13224
13424
|
createTreeSitterCheck({
|
|
13225
|
-
id:
|
|
13425
|
+
id: CHECK_ID46,
|
|
13226
13426
|
nameKey: "checks.rails-mass-assignment.name",
|
|
13227
13427
|
descriptionKey: "checks.rails-mass-assignment.description",
|
|
13228
13428
|
category: "security",
|
|
@@ -13234,7 +13434,7 @@ registry.register(
|
|
|
13234
13434
|
remediationKey: "remediation.security.rails-mass-assignment"
|
|
13235
13435
|
})
|
|
13236
13436
|
);
|
|
13237
|
-
var
|
|
13437
|
+
var CHECK_ID47 = "security.rails-insecure-session-config";
|
|
13238
13438
|
var REMEDIATION37 = getCopy("remediation.security.rails-insecure-session-config");
|
|
13239
13439
|
var SESSION_STORE_PATH = /config\/initializers\/session_store\.rb$/;
|
|
13240
13440
|
function hasSecureFlag(content) {
|
|
@@ -13247,7 +13447,7 @@ function hasSameSiteFlag(content) {
|
|
|
13247
13447
|
return /\bsame_site:\s*:(strict|lax)\b/.test(content);
|
|
13248
13448
|
}
|
|
13249
13449
|
registry.register({
|
|
13250
|
-
id:
|
|
13450
|
+
id: CHECK_ID47,
|
|
13251
13451
|
name: getCopy("checks.rails-insecure-session-config.name"),
|
|
13252
13452
|
description: getCopy("checks.rails-insecure-session-config.description"),
|
|
13253
13453
|
category: "security",
|
|
@@ -13267,8 +13467,8 @@ registry.register({
|
|
|
13267
13467
|
if (!hasSameSiteFlag(content)) missing.push("same_site: :strict or :lax");
|
|
13268
13468
|
if (missing.length > 0) {
|
|
13269
13469
|
findings.push({
|
|
13270
|
-
id: `${
|
|
13271
|
-
checkId:
|
|
13470
|
+
id: `${CHECK_ID47}:${file.relativePath}:1`,
|
|
13471
|
+
checkId: CHECK_ID47,
|
|
13272
13472
|
category: "security",
|
|
13273
13473
|
severity: "medium",
|
|
13274
13474
|
message: getCopy(
|
|
@@ -13285,7 +13485,7 @@ registry.register({
|
|
|
13285
13485
|
return findings;
|
|
13286
13486
|
}
|
|
13287
13487
|
});
|
|
13288
|
-
var
|
|
13488
|
+
var CHECK_ID48 = "security.xxe-surface";
|
|
13289
13489
|
var LIBXML_PARSE_QUERY = `
|
|
13290
13490
|
(call_expression
|
|
13291
13491
|
function: (member_expression
|
|
@@ -13332,7 +13532,7 @@ var QUERIES12 = {
|
|
|
13332
13532
|
};
|
|
13333
13533
|
registry.register(
|
|
13334
13534
|
createTreeSitterCheck({
|
|
13335
|
-
id:
|
|
13535
|
+
id: CHECK_ID48,
|
|
13336
13536
|
nameKey: "checks.xxe-surface.name",
|
|
13337
13537
|
descriptionKey: "checks.xxe-surface.description",
|
|
13338
13538
|
category: "security",
|
|
@@ -13531,12 +13731,13 @@ registry.register({
|
|
|
13531
13731
|
return runLLMCheck(ctx, promptTemplate22);
|
|
13532
13732
|
}
|
|
13533
13733
|
});
|
|
13534
|
-
var PROMPT_VERSION23 = "1.
|
|
13734
|
+
var PROMPT_VERSION23 = "1.1.0";
|
|
13535
13735
|
var fallbackPatterns23 = [
|
|
13536
13736
|
{
|
|
13537
13737
|
regex: /while\s*\(\s*(?:true|1)\s*\)|while\s+True\s*:/i,
|
|
13538
13738
|
message: "Infinite loop found \u2014 verify retry logic has a maximum attempt limit",
|
|
13539
|
-
severity: "medium"
|
|
13739
|
+
severity: "medium",
|
|
13740
|
+
skipCommentLines: true
|
|
13540
13741
|
}
|
|
13541
13742
|
];
|
|
13542
13743
|
async function buildPrompt23(sourceFiles) {
|
|
@@ -13598,12 +13799,13 @@ registry.register({
|
|
|
13598
13799
|
return [...bashFindings, ...llmFindings];
|
|
13599
13800
|
}
|
|
13600
13801
|
});
|
|
13601
|
-
var PROMPT_VERSION24 = "1.
|
|
13802
|
+
var PROMPT_VERSION24 = "1.2.0";
|
|
13602
13803
|
var fallbackPatterns24 = [
|
|
13603
13804
|
{
|
|
13604
13805
|
regex: /console\.(?:log|error|warn)\b/,
|
|
13605
13806
|
message: "Unstructured logging detected \u2014 verify observability hooks are present",
|
|
13606
|
-
severity: "medium"
|
|
13807
|
+
severity: "medium",
|
|
13808
|
+
skipCommentLines: true
|
|
13607
13809
|
}
|
|
13608
13810
|
];
|
|
13609
13811
|
async function buildPrompt24(sourceFiles) {
|
|
@@ -13826,12 +14028,13 @@ registry.register({
|
|
|
13826
14028
|
return runLLMCheck(ctx, promptTemplate27);
|
|
13827
14029
|
}
|
|
13828
14030
|
});
|
|
13829
|
-
var PROMPT_VERSION28 = "1.
|
|
14031
|
+
var PROMPT_VERSION28 = "1.1.0";
|
|
13830
14032
|
var fallbackPatterns28 = [
|
|
13831
14033
|
{
|
|
13832
14034
|
regex: /\b(?:fingerprint|deviceId|visitorId|trackingId|correlationId|analyticsId)\b/i,
|
|
13833
14035
|
message: "Derived identifier may link records across sessions or users",
|
|
13834
|
-
severity: "medium"
|
|
14036
|
+
severity: "medium",
|
|
14037
|
+
skipCommentLines: true
|
|
13835
14038
|
},
|
|
13836
14039
|
{
|
|
13837
14040
|
regex: /\bhash\s*\(\s*(?:email|phone|userId|id)\s*\)/i,
|
|
@@ -13865,15 +14068,64 @@ var promptTemplate28 = {
|
|
|
13865
14068
|
parseResponse: parseResponse28,
|
|
13866
14069
|
fallbackPatterns: fallbackPatterns28
|
|
13867
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
|
+
});
|
|
13868
14116
|
registry.register({
|
|
13869
|
-
id:
|
|
14117
|
+
id: CHECK_ID49,
|
|
13870
14118
|
name: getCopy("checks.derived-identifiers.name"),
|
|
13871
14119
|
description: getCopy("checks.derived-identifiers.description"),
|
|
13872
14120
|
category: "data-exposure",
|
|
13873
14121
|
severity: "medium",
|
|
13874
14122
|
minTier: "pro",
|
|
13875
14123
|
async run(ctx) {
|
|
13876
|
-
|
|
14124
|
+
const [llmFindings, astFindings] = await Promise.all([
|
|
14125
|
+
runLLMCheck(ctx, promptTemplate28),
|
|
14126
|
+
derivedIdAstCheck.run(ctx)
|
|
14127
|
+
]);
|
|
14128
|
+
return mergeFindings(astFindings, llmFindings);
|
|
13877
14129
|
}
|
|
13878
14130
|
});
|
|
13879
14131
|
var PROMPT_VERSION29 = "1.0.0";
|
|
@@ -13939,12 +14191,12 @@ registry.register({
|
|
|
13939
14191
|
return [...bashFindings, ...llmFindings];
|
|
13940
14192
|
}
|
|
13941
14193
|
});
|
|
13942
|
-
var
|
|
14194
|
+
var CHECK_ID50 = "security.permissive-grants";
|
|
13943
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;
|
|
13944
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;
|
|
13945
14197
|
var REMEDIATION38 = getCopy("remediation.security.permissive-grants");
|
|
13946
14198
|
var permissiveGrantsCheck = {
|
|
13947
|
-
id:
|
|
14199
|
+
id: CHECK_ID50,
|
|
13948
14200
|
name: getCopy("checks.permissive-grants.name"),
|
|
13949
14201
|
description: getCopy("checks.permissive-grants.description"),
|
|
13950
14202
|
category: "security",
|
|
@@ -13961,8 +14213,8 @@ var permissiveGrantsCheck = {
|
|
|
13961
14213
|
const beforeMatch = content.slice(0, match.index);
|
|
13962
14214
|
const lineNum = beforeMatch.split("\n").length;
|
|
13963
14215
|
findings.push({
|
|
13964
|
-
id: `${
|
|
13965
|
-
checkId:
|
|
14216
|
+
id: `${CHECK_ID50}:${file.relativePath}:${lineNum}:1`,
|
|
14217
|
+
checkId: CHECK_ID50,
|
|
13966
14218
|
category: "security",
|
|
13967
14219
|
severity: "high",
|
|
13968
14220
|
message: getCopy("checks.permissive-grants.message", match[0].trim()),
|
|
@@ -13979,8 +14231,8 @@ var permissiveGrantsCheck = {
|
|
|
13979
14231
|
const beforeMatch = content.slice(0, altMatch.index);
|
|
13980
14232
|
const lineNum = beforeMatch.split("\n").length;
|
|
13981
14233
|
findings.push({
|
|
13982
|
-
id: `${
|
|
13983
|
-
checkId:
|
|
14234
|
+
id: `${CHECK_ID50}:${file.relativePath}:${lineNum}:1`,
|
|
14235
|
+
checkId: CHECK_ID50,
|
|
13984
14236
|
category: "security",
|
|
13985
14237
|
severity: "high",
|
|
13986
14238
|
message: getCopy("checks.permissive-grants.message", altMatch[0].trim()),
|
|
@@ -14020,10 +14272,10 @@ function classifySqlDialect(content) {
|
|
|
14020
14272
|
}
|
|
14021
14273
|
return "unknown";
|
|
14022
14274
|
}
|
|
14023
|
-
var
|
|
14275
|
+
var CHECK_ID51 = "security.sql-rls-static";
|
|
14024
14276
|
var REMEDIATION39 = getCopy("remediation.security.sql-rls-static");
|
|
14025
14277
|
var sqlRlsStaticCheck = {
|
|
14026
|
-
id:
|
|
14278
|
+
id: CHECK_ID51,
|
|
14027
14279
|
name: getCopy("checks.sql-rls-static.name"),
|
|
14028
14280
|
description: getCopy("checks.sql-rls-static.description"),
|
|
14029
14281
|
category: "security",
|
|
@@ -14078,8 +14330,8 @@ var sqlRlsStaticCheck = {
|
|
|
14078
14330
|
const isRlsEnabled = enabledTables.has(table.name);
|
|
14079
14331
|
if (!isRlsEnabled) {
|
|
14080
14332
|
findings.push({
|
|
14081
|
-
id: `${
|
|
14082
|
-
checkId:
|
|
14333
|
+
id: `${CHECK_ID51}:${file.relativePath}:${table.line}:1:missing-rls`,
|
|
14334
|
+
checkId: CHECK_ID51,
|
|
14083
14335
|
category: "security",
|
|
14084
14336
|
severity: "high",
|
|
14085
14337
|
message: getCopy("checks.sql-rls-static.message", `Table "${table.name}" contains tenant columns but Row-Level Security is not enabled.`),
|
|
@@ -14093,8 +14345,8 @@ var sqlRlsStaticCheck = {
|
|
|
14093
14345
|
const isRlsForced = forcedTables.has(table.name);
|
|
14094
14346
|
if (!isRlsForced) {
|
|
14095
14347
|
findings.push({
|
|
14096
|
-
id: `${
|
|
14097
|
-
checkId:
|
|
14348
|
+
id: `${CHECK_ID51}:${file.relativePath}:${table.line}:1:missing-force-rls`,
|
|
14349
|
+
checkId: CHECK_ID51,
|
|
14098
14350
|
category: "security",
|
|
14099
14351
|
severity: "high",
|
|
14100
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.`),
|
|
@@ -14118,8 +14370,8 @@ var sqlRlsStaticCheck = {
|
|
|
14118
14370
|
const beforeMatch = content.slice(0, policyMatch.index);
|
|
14119
14371
|
const lineNum = beforeMatch.split("\n").length;
|
|
14120
14372
|
findings.push({
|
|
14121
|
-
id: `${
|
|
14122
|
-
checkId:
|
|
14373
|
+
id: `${CHECK_ID51}:${file.relativePath}:${lineNum}:1:weak-policy`,
|
|
14374
|
+
checkId: CHECK_ID51,
|
|
14123
14375
|
category: "security",
|
|
14124
14376
|
severity: "high",
|
|
14125
14377
|
message: getCopy("checks.sql-rls-static.message", `Policy ${policyName} on table "${tableName}" uses an unrestrictive tautology (like true or 1=1).`),
|
|
@@ -14137,10 +14389,10 @@ var sqlRlsStaticCheck = {
|
|
|
14137
14389
|
}
|
|
14138
14390
|
};
|
|
14139
14391
|
registry.register(sqlRlsStaticCheck);
|
|
14140
|
-
var
|
|
14392
|
+
var CHECK_ID52 = "security.sql-security-definer-search-path";
|
|
14141
14393
|
var REMEDIATION40 = getCopy("remediation.security.sql-security-definer-search-path");
|
|
14142
14394
|
var sqlSecurityDefinerSearchPathCheck = {
|
|
14143
|
-
id:
|
|
14395
|
+
id: CHECK_ID52,
|
|
14144
14396
|
name: getCopy("checks.sql-security-definer-search-path.name"),
|
|
14145
14397
|
description: getCopy("checks.sql-security-definer-search-path.description"),
|
|
14146
14398
|
category: "security",
|
|
@@ -14166,8 +14418,8 @@ var sqlSecurityDefinerSearchPathCheck = {
|
|
|
14166
14418
|
const beforeMatch = content.slice(0, match.index);
|
|
14167
14419
|
const lineNum = beforeMatch.split("\n").length;
|
|
14168
14420
|
findings.push({
|
|
14169
|
-
id: `${
|
|
14170
|
-
checkId:
|
|
14421
|
+
id: `${CHECK_ID52}:${file.relativePath}:${lineNum}:1`,
|
|
14422
|
+
checkId: CHECK_ID52,
|
|
14171
14423
|
category: "security",
|
|
14172
14424
|
severity: "high",
|
|
14173
14425
|
message: getCopy("checks.sql-security-definer-search-path.message", `Function "${functionName}" is defined as SECURITY DEFINER but is missing a locked search_path.`),
|
|
@@ -14186,7 +14438,7 @@ var sqlSecurityDefinerSearchPathCheck = {
|
|
|
14186
14438
|
}
|
|
14187
14439
|
};
|
|
14188
14440
|
registry.register(sqlSecurityDefinerSearchPathCheck);
|
|
14189
|
-
var
|
|
14441
|
+
var CHECK_ID53 = "resilience.missing-transaction-guard";
|
|
14190
14442
|
var REMEDIATION41 = getCopy("remediation.resilience.missing-transaction-guard");
|
|
14191
14443
|
var MUTATION_KEYWORDS = [
|
|
14192
14444
|
/\binsert\s+into\b/i,
|
|
@@ -14199,7 +14451,7 @@ var MUTATION_KEYWORDS = [
|
|
|
14199
14451
|
/\bcreate\s+table\s+\w+\s+as\b/i
|
|
14200
14452
|
];
|
|
14201
14453
|
var missingTransactionGuardCheck = {
|
|
14202
|
-
id:
|
|
14454
|
+
id: CHECK_ID53,
|
|
14203
14455
|
name: getCopy("checks.missing-transaction-guard.name"),
|
|
14204
14456
|
description: getCopy("checks.missing-transaction-guard.description"),
|
|
14205
14457
|
category: "resilience",
|
|
@@ -14229,8 +14481,8 @@ var missingTransactionGuardCheck = {
|
|
|
14229
14481
|
}
|
|
14230
14482
|
if (mutationCount > 5) {
|
|
14231
14483
|
findings.push({
|
|
14232
|
-
id: `${
|
|
14233
|
-
checkId:
|
|
14484
|
+
id: `${CHECK_ID53}:${file.relativePath}:1:1`,
|
|
14485
|
+
checkId: CHECK_ID53,
|
|
14234
14486
|
category: "resilience",
|
|
14235
14487
|
severity: "medium",
|
|
14236
14488
|
message: getCopy("checks.missing-transaction-guard.message"),
|
|
@@ -14245,10 +14497,10 @@ var missingTransactionGuardCheck = {
|
|
|
14245
14497
|
}
|
|
14246
14498
|
};
|
|
14247
14499
|
registry.register(missingTransactionGuardCheck);
|
|
14248
|
-
var
|
|
14500
|
+
var CHECK_ID54 = "resilience.destructive-sql-migration";
|
|
14249
14501
|
var REMEDIATION42 = getCopy("remediation.resilience.destructive-sql-migration");
|
|
14250
14502
|
var destructiveSqlMigrationCheck = {
|
|
14251
|
-
id:
|
|
14503
|
+
id: CHECK_ID54,
|
|
14252
14504
|
name: getCopy("checks.destructive-sql-migration.name"),
|
|
14253
14505
|
description: getCopy("checks.destructive-sql-migration.description"),
|
|
14254
14506
|
category: "resilience",
|
|
@@ -14269,8 +14521,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14269
14521
|
const highMatch = highRiskRegex.exec(line);
|
|
14270
14522
|
if (highMatch) {
|
|
14271
14523
|
findings.push({
|
|
14272
|
-
id: `${
|
|
14273
|
-
checkId:
|
|
14524
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(highMatch.index ?? 0) + 1}:high`,
|
|
14525
|
+
checkId: CHECK_ID54,
|
|
14274
14526
|
category: "resilience",
|
|
14275
14527
|
severity: "high",
|
|
14276
14528
|
message: getCopy("checks.destructive-sql-migration.message", highMatch[0]),
|
|
@@ -14283,8 +14535,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14283
14535
|
const truncateMatch = truncateRegex.exec(line);
|
|
14284
14536
|
if (truncateMatch) {
|
|
14285
14537
|
findings.push({
|
|
14286
|
-
id: `${
|
|
14287
|
-
checkId:
|
|
14538
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(truncateMatch.index ?? 0) + 1}:high`,
|
|
14539
|
+
checkId: CHECK_ID54,
|
|
14288
14540
|
category: "resilience",
|
|
14289
14541
|
severity: "high",
|
|
14290
14542
|
message: getCopy("checks.destructive-sql-migration.message", truncateMatch[0]),
|
|
@@ -14300,8 +14552,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14300
14552
|
const colMatch = dropColumnRegex.exec(line);
|
|
14301
14553
|
if (colMatch) {
|
|
14302
14554
|
findings.push({
|
|
14303
|
-
id: `${
|
|
14304
|
-
checkId:
|
|
14555
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(colMatch.index ?? 0) + 1}:col`,
|
|
14556
|
+
checkId: CHECK_ID54,
|
|
14305
14557
|
category: "resilience",
|
|
14306
14558
|
severity: "medium",
|
|
14307
14559
|
message: getCopy("checks.destructive-sql-migration.message", colMatch[0]),
|
|
@@ -14314,8 +14566,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14314
14566
|
const policyMatch = dropPolicyRegex.exec(line);
|
|
14315
14567
|
if (policyMatch) {
|
|
14316
14568
|
findings.push({
|
|
14317
|
-
id: `${
|
|
14318
|
-
checkId:
|
|
14569
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(policyMatch.index ?? 0) + 1}:policy`,
|
|
14570
|
+
checkId: CHECK_ID54,
|
|
14319
14571
|
category: "resilience",
|
|
14320
14572
|
severity: "medium",
|
|
14321
14573
|
message: getCopy("checks.destructive-sql-migration.message", policyMatch[0]),
|
|
@@ -14328,8 +14580,8 @@ var destructiveSqlMigrationCheck = {
|
|
|
14328
14580
|
const indexMatch = dropIndexRegex.exec(line);
|
|
14329
14581
|
if (indexMatch) {
|
|
14330
14582
|
findings.push({
|
|
14331
|
-
id: `${
|
|
14332
|
-
checkId:
|
|
14583
|
+
id: `${CHECK_ID54}:${file.relativePath}:${lineNum + 1}:${(indexMatch.index ?? 0) + 1}:index`,
|
|
14584
|
+
checkId: CHECK_ID54,
|
|
14333
14585
|
category: "resilience",
|
|
14334
14586
|
severity: "medium",
|
|
14335
14587
|
message: getCopy("checks.destructive-sql-migration.message", indexMatch[0]),
|
|
@@ -14345,7 +14597,7 @@ var destructiveSqlMigrationCheck = {
|
|
|
14345
14597
|
}
|
|
14346
14598
|
};
|
|
14347
14599
|
registry.register(destructiveSqlMigrationCheck);
|
|
14348
|
-
var
|
|
14600
|
+
var CHECK_ID55 = "resilience.sql-broad-mutation";
|
|
14349
14601
|
var REMEDIATION43 = getCopy("remediation.resilience.sql-broad-mutation");
|
|
14350
14602
|
function splitSqlStatements(content) {
|
|
14351
14603
|
const statements = [];
|
|
@@ -14383,7 +14635,7 @@ function splitSqlStatements(content) {
|
|
|
14383
14635
|
return statements;
|
|
14384
14636
|
}
|
|
14385
14637
|
var sqlBroadMutationCheck = {
|
|
14386
|
-
id:
|
|
14638
|
+
id: CHECK_ID55,
|
|
14387
14639
|
name: getCopy("checks.sql-broad-mutation.name"),
|
|
14388
14640
|
description: getCopy("checks.sql-broad-mutation.description"),
|
|
14389
14641
|
category: "resilience",
|
|
@@ -14429,8 +14681,8 @@ var sqlBroadMutationCheck = {
|
|
|
14429
14681
|
const beforeStatement = content.slice(0, charOffset + leadingWhitespace);
|
|
14430
14682
|
const lineNum = beforeStatement.split("\n").length;
|
|
14431
14683
|
findings.push({
|
|
14432
|
-
id: `${
|
|
14433
|
-
checkId:
|
|
14684
|
+
id: `${CHECK_ID55}:${file.relativePath}:${lineNum}:1`,
|
|
14685
|
+
checkId: CHECK_ID55,
|
|
14434
14686
|
category: "resilience",
|
|
14435
14687
|
severity: "high",
|
|
14436
14688
|
message: getCopy("checks.sql-broad-mutation.message", matchedKeyword),
|
|
@@ -14448,7 +14700,7 @@ var sqlBroadMutationCheck = {
|
|
|
14448
14700
|
}
|
|
14449
14701
|
};
|
|
14450
14702
|
registry.register(sqlBroadMutationCheck);
|
|
14451
|
-
var
|
|
14703
|
+
var CHECK_ID56 = "data-exposure.unprotected-sensitive-sql-columns";
|
|
14452
14704
|
var REMEDIATION44 = getCopy("remediation.data-exposure.unprotected-sensitive-sql-columns");
|
|
14453
14705
|
var SENSITIVE_KEYWORDS = [
|
|
14454
14706
|
"ssn",
|
|
@@ -14480,7 +14732,7 @@ var PROTECTED_SUFFIXES = [
|
|
|
14480
14732
|
"_key_id"
|
|
14481
14733
|
];
|
|
14482
14734
|
var unprotectedSensitiveSqlColumnsCheck = {
|
|
14483
|
-
id:
|
|
14735
|
+
id: CHECK_ID56,
|
|
14484
14736
|
name: getCopy("checks.unprotected-sensitive-sql-columns.name"),
|
|
14485
14737
|
description: getCopy("checks.unprotected-sensitive-sql-columns.description"),
|
|
14486
14738
|
category: "data-exposure",
|
|
@@ -14510,8 +14762,8 @@ var unprotectedSensitiveSqlColumnsCheck = {
|
|
|
14510
14762
|
const isProtected = PROTECTED_SUFFIXES.some((suf) => colName.endsWith(suf));
|
|
14511
14763
|
if (isSensitive && isPlainText && !isProtected) {
|
|
14512
14764
|
findings.push({
|
|
14513
|
-
id: `${
|
|
14514
|
-
checkId:
|
|
14765
|
+
id: `${CHECK_ID56}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 1}`,
|
|
14766
|
+
checkId: CHECK_ID56,
|
|
14515
14767
|
category: "data-exposure",
|
|
14516
14768
|
severity: "high",
|
|
14517
14769
|
message: getCopy("checks.unprotected-sensitive-sql-columns.message", `Column "${colNameRaw}" with type "${colTypeRaw}"`),
|
|
@@ -14575,9 +14827,9 @@ registry.register({
|
|
|
14575
14827
|
return runLLMCheck(ctx, promptTemplate30);
|
|
14576
14828
|
}
|
|
14577
14829
|
});
|
|
14578
|
-
var
|
|
14830
|
+
var CHECK_ID57 = "security.rust-unsafe-blocks";
|
|
14579
14831
|
registry.register({
|
|
14580
|
-
id:
|
|
14832
|
+
id: CHECK_ID57,
|
|
14581
14833
|
name: getCopy("checks.rust-unsafe-blocks.name"),
|
|
14582
14834
|
description: getCopy("checks.rust-unsafe-blocks.description"),
|
|
14583
14835
|
category: "security",
|
|
@@ -14664,8 +14916,8 @@ registry.register({
|
|
|
14664
14916
|
}
|
|
14665
14917
|
if (!foundSafety) {
|
|
14666
14918
|
const finding = {
|
|
14667
|
-
id: `${
|
|
14668
|
-
checkId:
|
|
14919
|
+
id: `${CHECK_ID57}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 0}:${i}`,
|
|
14920
|
+
checkId: CHECK_ID57,
|
|
14669
14921
|
category: "security",
|
|
14670
14922
|
severity: "medium",
|
|
14671
14923
|
message: getCopy("checks.rust-unsafe-blocks.message", match.nodeText),
|
|
@@ -14683,7 +14935,7 @@ registry.register({
|
|
|
14683
14935
|
return findings;
|
|
14684
14936
|
}
|
|
14685
14937
|
});
|
|
14686
|
-
var
|
|
14938
|
+
var CHECK_ID58 = "resilience.rust-handler-panics";
|
|
14687
14939
|
var RUST_QUERY = `
|
|
14688
14940
|
(call_expression
|
|
14689
14941
|
function: (field_expression
|
|
@@ -14709,7 +14961,7 @@ function hasAsyncModifier(node) {
|
|
|
14709
14961
|
return false;
|
|
14710
14962
|
}
|
|
14711
14963
|
registry.register({
|
|
14712
|
-
id:
|
|
14964
|
+
id: CHECK_ID58,
|
|
14713
14965
|
name: getCopy("checks.rust-handler-panics.name"),
|
|
14714
14966
|
description: getCopy("checks.rust-handler-panics.description"),
|
|
14715
14967
|
category: "resilience",
|
|
@@ -14775,8 +15027,8 @@ registry.register({
|
|
|
14775
15027
|
const isAsync = hasAsyncModifier(enclosingFunction);
|
|
14776
15028
|
if (isAsync) {
|
|
14777
15029
|
const finding = {
|
|
14778
|
-
id: `${
|
|
14779
|
-
checkId:
|
|
15030
|
+
id: `${CHECK_ID58}:${file.relativePath}:${match.startLine}:${match.startColumn ?? 0}:${i}`,
|
|
15031
|
+
checkId: CHECK_ID58,
|
|
14780
15032
|
category: "resilience",
|
|
14781
15033
|
severity: "high",
|
|
14782
15034
|
message: getCopy("checks.rust-handler-panics.message", match.nodeText),
|
|
@@ -16214,6 +16466,7 @@ async function executeScan(args, ctx) {
|
|
|
16214
16466
|
debug: args.debug,
|
|
16215
16467
|
baseline: args.baseline,
|
|
16216
16468
|
minConfidence: args.minConfidence,
|
|
16469
|
+
minSeverity: args.minSeverity,
|
|
16217
16470
|
onCheckComplete: (completed, total, checkName) => {
|
|
16218
16471
|
heartbeat();
|
|
16219
16472
|
if (showProgress) {
|
|
@@ -16593,6 +16846,7 @@ async function runCli(options) {
|
|
|
16593
16846
|
debug: options.debug,
|
|
16594
16847
|
baseline: options.baseline,
|
|
16595
16848
|
minConfidence: options.minConfidence,
|
|
16849
|
+
minSeverity: options.minSeverity,
|
|
16596
16850
|
showIds: options.showIds,
|
|
16597
16851
|
all: options.all
|
|
16598
16852
|
};
|
|
@@ -16771,12 +17025,13 @@ var require5 = createRequire4(import.meta.url);
|
|
|
16771
17025
|
var { version } = require5("../package.json");
|
|
16772
17026
|
var cli = cac("seaworthycode");
|
|
16773
17027
|
cli.version(version);
|
|
16774
|
-
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) => {
|
|
16775
17029
|
const maxFileSize = options.maxFileSize ? Number(options.maxFileSize) : void 0;
|
|
16776
17030
|
const tier = options.tier;
|
|
16777
17031
|
const baseline = options.baseline === "save" ? "save" : options.baseline;
|
|
16778
17032
|
const minConfidence = options.minConfidence;
|
|
16779
|
-
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 });
|
|
16780
17035
|
if (result.updateMessage) {
|
|
16781
17036
|
console.error(result.updateMessage);
|
|
16782
17037
|
}
|
|
@@ -16844,6 +17099,7 @@ cli.help(() => {
|
|
|
16844
17099
|
--max-file-size <n> Skip files larger than n bytes (default: 1 MB)
|
|
16845
17100
|
--baseline [ref] Compare against baseline file or save (--baseline save)
|
|
16846
17101
|
--min-confidence <level> Hide findings below confidence (high, medium, low)
|
|
17102
|
+
--min-severity <level> Hide findings below severity (critical, high, medium, low, info)
|
|
16847
17103
|
--show-ids Show check IDs alongside names
|
|
16848
17104
|
--all List every finding; do not collapse low-severity findings
|
|
16849
17105
|
--debug Enable debug logging`
|