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.js
CHANGED
|
@@ -815,9 +815,12 @@ var SERVER_SIDE_PATH_RE = new RegExp(
|
|
|
815
815
|
].join("|"),
|
|
816
816
|
"i"
|
|
817
817
|
);
|
|
818
|
-
|
|
818
|
+
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/;
|
|
819
|
+
function isServerSideFile(filePath, content) {
|
|
819
820
|
if (isTestFile(filePath)) return false;
|
|
820
|
-
|
|
821
|
+
if (SERVER_SIDE_PATH_RE.test(filePath)) return true;
|
|
822
|
+
if (content && SERVER_SIDE_CONTENT_RE.test(content)) return true;
|
|
823
|
+
return false;
|
|
821
824
|
}
|
|
822
825
|
var CONFIG_FILE_PATTERN = new RegExp(
|
|
823
826
|
[
|
|
@@ -838,7 +841,7 @@ function isCommentLine(content, matchIndex) {
|
|
|
838
841
|
function isInsideFixMessage(content, matchIndex) {
|
|
839
842
|
const lineStart = content.lastIndexOf("\n", matchIndex - 1) + 1;
|
|
840
843
|
const lineText = content.substring(lineStart, content.indexOf("\n", matchIndex));
|
|
841
|
-
return /(?:fix|description|message|suggestion|hint|help|example|doc|comment)\s*[:=
|
|
844
|
+
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);
|
|
842
845
|
}
|
|
843
846
|
function isInlineSilenced(content, matchIndex, ruleId) {
|
|
844
847
|
const lines = content.split("\n");
|
|
@@ -1030,7 +1033,7 @@ var missingAuthMiddleware = {
|
|
|
1030
1033
|
category: "Authentication",
|
|
1031
1034
|
description: "API routes without authentication checks allow unauthorized access.",
|
|
1032
1035
|
check(content, filePath) {
|
|
1033
|
-
if (!isServerSideFile(filePath)) return [];
|
|
1036
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
1034
1037
|
const routePatterns = [
|
|
1035
1038
|
// Express/Hono style
|
|
1036
1039
|
/\.(get|post|put|patch|delete)\s*\(\s*["'`][^"'`]+["'`]\s*,\s*(?:async\s+)?\(?(?:req|c|ctx)/gi,
|
|
@@ -2348,7 +2351,7 @@ var exposedStackTraces = {
|
|
|
2348
2351
|
category: "Information Leakage",
|
|
2349
2352
|
description: "Returning error.stack or detailed error messages in API responses reveals internal code paths, file structure, and dependencies to attackers.",
|
|
2350
2353
|
check(content, filePath) {
|
|
2351
|
-
if (!isServerSideFile(filePath)) return [];
|
|
2354
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
2352
2355
|
const matches = [];
|
|
2353
2356
|
const patterns = [
|
|
2354
2357
|
// Sending stack trace in response
|
|
@@ -2446,7 +2449,7 @@ var ssrfVulnerability = {
|
|
|
2446
2449
|
category: "Injection",
|
|
2447
2450
|
description: "Fetching URLs from user input without validation allows attackers to access internal services, cloud metadata endpoints (169.254.169.254), and private networks.",
|
|
2448
2451
|
check(content, filePath) {
|
|
2449
|
-
if (!isServerSideFile(filePath)) return [];
|
|
2452
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
2450
2453
|
const matches = [];
|
|
2451
2454
|
const hasValidation = /allowedHosts|allowedDomains|allowedUrls|safeDomain|whitelist|urlValidator|new URL.*hostname.*includes|isAllowedUrl|validateUrl|isValidUrl/i.test(content);
|
|
2452
2455
|
if (hasValidation) return [];
|
|
@@ -2503,7 +2506,7 @@ var massAssignment = {
|
|
|
2503
2506
|
category: "Authorization",
|
|
2504
2507
|
description: "Spreading or assigning request body directly into database models allows attackers to set fields they shouldn't (e.g., isAdmin, role, verified).",
|
|
2505
2508
|
check(content, filePath) {
|
|
2506
|
-
if (!isServerSideFile(filePath)) return [];
|
|
2509
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
2507
2510
|
const hasSanitization = /pick\(|omit\(|allowedFields|sanitize|whitelist|permit|strong_params/i.test(content);
|
|
2508
2511
|
if (hasSanitization) return [];
|
|
2509
2512
|
const matches = [];
|
|
@@ -2655,7 +2658,7 @@ var logInjection = {
|
|
|
2655
2658
|
category: "Injection",
|
|
2656
2659
|
description: "Logging unsanitized user input allows attackers to forge log entries, inject malicious content, or exploit log aggregation systems via newlines and special characters.",
|
|
2657
2660
|
check(content, filePath) {
|
|
2658
|
-
if (!isServerSideFile(filePath)) return [];
|
|
2661
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
2659
2662
|
const hasSanitization = /replace\s*\(\s*\/\[?\\r\\n\]|sanitizeLog|stripNewlines|sanitizeForLog/i.test(content);
|
|
2660
2663
|
if (hasSanitization) return [];
|
|
2661
2664
|
const matches = [];
|
|
@@ -2721,7 +2724,7 @@ var weakPasswordRequirements = {
|
|
|
2721
2724
|
if (!/(?:password|passwd|pwd)/i.test(content)) return [];
|
|
2722
2725
|
const isPasswordContext = /(?:register|signup|sign.up|createUser|create.user|changePassword|resetPassword|set.password|validatePassword|validate.password|passwordPolicy|password.policy)/i.test(
|
|
2723
2726
|
content
|
|
2724
|
-
) || isServerSideFile(filePath);
|
|
2727
|
+
) || isServerSideFile(filePath, content);
|
|
2725
2728
|
const matches = [];
|
|
2726
2729
|
if (isPasswordContext) {
|
|
2727
2730
|
const weakThresholdPatterns = [
|
|
@@ -3875,8 +3878,8 @@ var xxeVulnerability = {
|
|
|
3875
3878
|
if (!/xml|parseXml|parseXML|DOMParser|SAXParser|etree|lxml|libxml/i.test(content)) return [];
|
|
3876
3879
|
const matches = [];
|
|
3877
3880
|
const patterns = [
|
|
3878
|
-
/\.parseXm?l
|
|
3879
|
-
//
|
|
3881
|
+
/\.parseXm?l(?:String)?\s*\(/gi,
|
|
3882
|
+
// parseXml / parseXML / parseXmlString (libxmljs)
|
|
3880
3883
|
// NOTE: the browser `new DOMParser()` is intentionally NOT flagged.
|
|
3881
3884
|
// Per the HTML/XML spec, DOMParser.parseFromString does not resolve
|
|
3882
3885
|
// external entities, so it is not an XXE sink — flagging it produced a
|
|
@@ -4062,7 +4065,7 @@ var exposedAdminRoutes = {
|
|
|
4062
4065
|
category: "Information Leakage",
|
|
4063
4066
|
description: "Routes like /admin, /debug, /phpinfo, or /actuator without authentication expose sensitive controls and information to attackers.",
|
|
4064
4067
|
check(content, filePath) {
|
|
4065
|
-
if (!isServerSideFile(filePath)) return [];
|
|
4068
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
4066
4069
|
const matches = [];
|
|
4067
4070
|
const patterns = [
|
|
4068
4071
|
/[.'"]\s*(?:get|use|all)\s*\(\s*["'`]\/(?:admin|debug|_debug|__debug__|phpinfo|actuator|graphiql|playground|swagger|reset|seed|test|dev|mock)["'`]/gi
|
|
@@ -4154,7 +4157,7 @@ var missingContentDisposition = {
|
|
|
4154
4157
|
description: "File download endpoints without Content-Disposition headers may render files inline, leading to XSS if the file contains HTML/JS.",
|
|
4155
4158
|
check(content, filePath) {
|
|
4156
4159
|
if (!/(?:download|sendFile|send_file|pipe|createReadStream)/i.test(content)) return [];
|
|
4157
|
-
if (!isServerSideFile(filePath)) return [];
|
|
4160
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
4158
4161
|
if (/Content-Disposition|attachment|download/i.test(content)) return [];
|
|
4159
4162
|
return findMatches(
|
|
4160
4163
|
content,
|
|
@@ -4251,7 +4254,7 @@ var unprotectedDownload = {
|
|
|
4251
4254
|
description: "File download endpoints that accept user-controlled filenames without path validation allow directory traversal to read arbitrary files.",
|
|
4252
4255
|
check(content, filePath) {
|
|
4253
4256
|
if (!/(?:download|sendFile|send_file)/i.test(content)) return [];
|
|
4254
|
-
if (!isServerSideFile(filePath)) return [];
|
|
4257
|
+
if (!isServerSideFile(filePath, content)) return [];
|
|
4255
4258
|
const matches = [];
|
|
4256
4259
|
if (/(?:sendFile|download|send_file)\s*\([^)]*(?:req\.|params\.|query\.|body\.)/i.test(content)) {
|
|
4257
4260
|
const hasValidation = /path\.resolve|path\.normalize|path\.join.*__dirname|realpath|includes\s*\(\s*["']\.\./i.test(content);
|
|
@@ -4280,7 +4283,7 @@ var commandInjection = {
|
|
|
4280
4283
|
const matches = [];
|
|
4281
4284
|
const patterns = [
|
|
4282
4285
|
// Node.js — require standalone exec/execSync, not db.exec() or conn.exec()
|
|
4283
|
-
/(?<![.\w])(?:exec|execSync)\s*\(\s*(?:`[^`]*\$\{|["'][^"']*\+\s*(?:req\.|body\.|input|params|args|user))/gi,
|
|
4286
|
+
/(?<![.\w])(?:exec|execSync)\s*\(\s*(?:`[^`]*\$\{|["'][^"']*["']\s*\+\s*(?:req\.|body\.|input|params|args|user))/gi,
|
|
4284
4287
|
/child_process.*exec\s*\(\s*(?!["'`])/g,
|
|
4285
4288
|
// spawn / execFile / exec with shell: true AND a template literal
|
|
4286
4289
|
// first arg. `shell: true` converts the first argument into a string
|