xploitscan-shared-rules 1.10.0 → 1.11.1
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.cjs +18 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +18 -15
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1080,9 +1080,12 @@ var SERVER_SIDE_PATH_RE = new RegExp(
|
|
|
1080
1080
|
].join("|"),
|
|
1081
1081
|
"i"
|
|
1082
1082
|
);
|
|
1083
|
-
|
|
1083
|
+
var SERVER_SIDE_CONTENT_RE = /\b(?:req|request)\.(?:body|query|params|headers|cookies)\b|\b(?:app|router)\.(?:get|post|put|patch|delete|use|all)\s*\(|\bctx\.request\b|\bevent\.(?:body|queryStringParameters|pathParameters)\b|\(\s*req\s*,\s*res\b/;
|
|
1084
|
+
function isServerSideFile(filePath, content) {
|
|
1084
1085
|
if (isTestFile(filePath)) return false;
|
|
1085
|
-
|
|
1086
|
+
if (SERVER_SIDE_PATH_RE.test(filePath)) return true;
|
|
1087
|
+
if (content && SERVER_SIDE_CONTENT_RE.test(content)) return true;
|
|
1088
|
+
return false;
|
|
1086
1089
|
}
|
|
1087
1090
|
var CONFIG_FILE_PATTERN = new RegExp(
|
|
1088
1091
|
[
|
|
@@ -1103,7 +1106,7 @@ function isCommentLine(content, matchIndex) {
|
|
|
1103
1106
|
function isInsideFixMessage(content, matchIndex) {
|
|
1104
1107
|
const lineStart = content.lastIndexOf("\n", matchIndex - 1) + 1;
|
|
1105
1108
|
const lineText = content.substring(lineStart, content.indexOf("\n", matchIndex));
|
|
1106
|
-
return /(?:fix|description|message|suggestion|hint|help|example|doc|comment)\s*[:=
|
|
1109
|
+
return /(?:fix|description|message|suggestion|hint|help|example|doc|comment)\s*[:=]\s*["'`]/i.test(lineText) || /return\s*["'`].*\b(?:Use|Replace|Add|Move|Set|Enable|Disable|Never|Don't|Do not|Instead)\b/i.test(lineText);
|
|
1107
1110
|
}
|
|
1108
1111
|
function isInlineSilenced(content, matchIndex, ruleId) {
|
|
1109
1112
|
const lines = content.split("\n");
|
|
@@ -1295,7 +1298,7 @@ var missingAuthMiddleware = {
|
|
|
1295
1298
|
category: "Authentication",
|
|
1296
1299
|
description: "API routes without authentication checks allow unauthorized access.",
|
|
1297
1300
|
check(content, filePath) {
|
|
1298
|
-
if (!isServerSideFile(filePath)) return [];
|
|
1301
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
1299
1302
|
const routePatterns = [
|
|
1300
1303
|
// Express/Hono style
|
|
1301
1304
|
/\.(get|post|put|patch|delete)\s*\(\s*["'`][^"'`]+["'`]\s*,\s*(?:async\s+)?\(?(?:req|c|ctx)/gi,
|
|
@@ -2613,7 +2616,7 @@ var exposedStackTraces = {
|
|
|
2613
2616
|
category: "Information Leakage",
|
|
2614
2617
|
description: "Returning error.stack or detailed error messages in API responses reveals internal code paths, file structure, and dependencies to attackers.",
|
|
2615
2618
|
check(content, filePath) {
|
|
2616
|
-
if (!isServerSideFile(filePath)) return [];
|
|
2619
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
2617
2620
|
const matches = [];
|
|
2618
2621
|
const patterns = [
|
|
2619
2622
|
// Sending stack trace in response
|
|
@@ -2711,7 +2714,7 @@ var ssrfVulnerability = {
|
|
|
2711
2714
|
category: "Injection",
|
|
2712
2715
|
description: "Fetching URLs from user input without validation allows attackers to access internal services, cloud metadata endpoints (169.254.169.254), and private networks.",
|
|
2713
2716
|
check(content, filePath) {
|
|
2714
|
-
if (!isServerSideFile(filePath)) return [];
|
|
2717
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
2715
2718
|
const matches = [];
|
|
2716
2719
|
const hasValidation = /allowedHosts|allowedDomains|allowedUrls|safeDomain|whitelist|urlValidator|new URL.*hostname.*includes|isAllowedUrl|validateUrl|isValidUrl/i.test(content);
|
|
2717
2720
|
if (hasValidation) return [];
|
|
@@ -2768,7 +2771,7 @@ var massAssignment = {
|
|
|
2768
2771
|
category: "Authorization",
|
|
2769
2772
|
description: "Spreading or assigning request body directly into database models allows attackers to set fields they shouldn't (e.g., isAdmin, role, verified).",
|
|
2770
2773
|
check(content, filePath) {
|
|
2771
|
-
if (!isServerSideFile(filePath)) return [];
|
|
2774
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
2772
2775
|
const hasSanitization = /pick\(|omit\(|allowedFields|sanitize|whitelist|permit|strong_params/i.test(content);
|
|
2773
2776
|
if (hasSanitization) return [];
|
|
2774
2777
|
const matches = [];
|
|
@@ -2920,7 +2923,7 @@ var logInjection = {
|
|
|
2920
2923
|
category: "Injection",
|
|
2921
2924
|
description: "Logging unsanitized user input allows attackers to forge log entries, inject malicious content, or exploit log aggregation systems via newlines and special characters.",
|
|
2922
2925
|
check(content, filePath) {
|
|
2923
|
-
if (!isServerSideFile(filePath)) return [];
|
|
2926
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
2924
2927
|
const hasSanitization = /replace\s*\(\s*\/\[?\\r\\n\]|sanitizeLog|stripNewlines|sanitizeForLog/i.test(content);
|
|
2925
2928
|
if (hasSanitization) return [];
|
|
2926
2929
|
const matches = [];
|
|
@@ -2986,7 +2989,7 @@ var weakPasswordRequirements = {
|
|
|
2986
2989
|
if (!/(?:password|passwd|pwd)/i.test(content)) return [];
|
|
2987
2990
|
const isPasswordContext = /(?:register|signup|sign.up|createUser|create.user|changePassword|resetPassword|set.password|validatePassword|validate.password|passwordPolicy|password.policy)/i.test(
|
|
2988
2991
|
content
|
|
2989
|
-
) || isServerSideFile(filePath);
|
|
2992
|
+
) || isServerSideFile(filePath, content);
|
|
2990
2993
|
const matches = [];
|
|
2991
2994
|
if (isPasswordContext) {
|
|
2992
2995
|
const weakThresholdPatterns = [
|
|
@@ -4140,8 +4143,8 @@ var xxeVulnerability = {
|
|
|
4140
4143
|
if (!/xml|parseXml|parseXML|DOMParser|SAXParser|etree|lxml|libxml/i.test(content)) return [];
|
|
4141
4144
|
const matches = [];
|
|
4142
4145
|
const patterns = [
|
|
4143
|
-
/\.parseXm?l
|
|
4144
|
-
//
|
|
4146
|
+
/\.parseXm?l(?:String)?\s*\(/gi,
|
|
4147
|
+
// parseXml / parseXML / parseXmlString (libxmljs)
|
|
4145
4148
|
// NOTE: the browser `new DOMParser()` is intentionally NOT flagged.
|
|
4146
4149
|
// Per the HTML/XML spec, DOMParser.parseFromString does not resolve
|
|
4147
4150
|
// external entities, so it is not an XXE sink — flagging it produced a
|
|
@@ -4327,7 +4330,7 @@ var exposedAdminRoutes = {
|
|
|
4327
4330
|
category: "Information Leakage",
|
|
4328
4331
|
description: "Routes like /admin, /debug, /phpinfo, or /actuator without authentication expose sensitive controls and information to attackers.",
|
|
4329
4332
|
check(content, filePath) {
|
|
4330
|
-
if (!isServerSideFile(filePath)) return [];
|
|
4333
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
4331
4334
|
const matches = [];
|
|
4332
4335
|
const patterns = [
|
|
4333
4336
|
/[.'"]\s*(?:get|use|all)\s*\(\s*["'`]\/(?:admin|debug|_debug|__debug__|phpinfo|actuator|graphiql|playground|swagger|reset|seed|test|dev|mock)["'`]/gi
|
|
@@ -4419,7 +4422,7 @@ var missingContentDisposition = {
|
|
|
4419
4422
|
description: "File download endpoints without Content-Disposition headers may render files inline, leading to XSS if the file contains HTML/JS.",
|
|
4420
4423
|
check(content, filePath) {
|
|
4421
4424
|
if (!/(?:download|sendFile|send_file|pipe|createReadStream)/i.test(content)) return [];
|
|
4422
|
-
if (!isServerSideFile(filePath)) return [];
|
|
4425
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
4423
4426
|
if (/Content-Disposition|attachment|download/i.test(content)) return [];
|
|
4424
4427
|
return findMatches(
|
|
4425
4428
|
content,
|
|
@@ -4516,7 +4519,7 @@ var unprotectedDownload = {
|
|
|
4516
4519
|
description: "File download endpoints that accept user-controlled filenames without path validation allow directory traversal to read arbitrary files.",
|
|
4517
4520
|
check(content, filePath) {
|
|
4518
4521
|
if (!/(?:download|sendFile|send_file)/i.test(content)) return [];
|
|
4519
|
-
if (!isServerSideFile(filePath)) return [];
|
|
4522
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
4520
4523
|
const matches = [];
|
|
4521
4524
|
if (/(?:sendFile|download|send_file)\s*\([^)]*(?:req\.|params\.|query\.|body\.)/i.test(content)) {
|
|
4522
4525
|
const hasValidation = /path\.resolve|path\.normalize|path\.join.*__dirname|realpath|includes\s*\(\s*["']\.\./i.test(content);
|
|
@@ -4545,7 +4548,7 @@ var commandInjection = {
|
|
|
4545
4548
|
const matches = [];
|
|
4546
4549
|
const patterns = [
|
|
4547
4550
|
// Node.js — require standalone exec/execSync, not db.exec() or conn.exec()
|
|
4548
|
-
/(?<![.\w])(?:exec|execSync)\s*\(\s*(?:`[^`]*\$\{|["'][^"']*\+\s*(?:req\.|body\.|input|params|args|user))/gi,
|
|
4551
|
+
/(?<![.\w])(?:exec|execSync)\s*\(\s*(?:`[^`]*\$\{|["'][^"']*["']\s*\+\s*(?:req\.|body\.|input|params|args|user))/gi,
|
|
4549
4552
|
/child_process.*exec\s*\(\s*(?!["'`])/g,
|
|
4550
4553
|
// spawn / execFile / exec with shell: true AND a template literal
|
|
4551
4554
|
// first arg. `shell: true` converts the first argument into a string
|