eslint-plugin-secure-coding 3.0.0 → 3.0.2
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/AGENTS.md +1 -1
- package/CHANGELOG.md +1 -1
- package/README.md +90 -422
- package/package.json +6 -5
- package/src/index.d.ts +5 -14
- package/src/index.js +36 -265
- package/src/rules/detect-non-literal-regexp/index.d.ts +20 -1
- package/src/rules/detect-non-literal-regexp/index.js +5 -0
- package/src/rules/detect-object-injection/index.d.ts +25 -1
- package/src/rules/detect-object-injection/index.js +5 -0
- package/src/rules/detect-weak-password-validation/index.d.ts +8 -2
- package/src/rules/detect-weak-password-validation/index.js +6 -1
- package/src/rules/no-directive-injection/index.d.ts +27 -1
- package/src/rules/no-directive-injection/index.js +5 -0
- package/src/rules/no-electron-security-issues/index.d.ts +27 -1
- package/src/rules/no-electron-security-issues/index.js +5 -0
- package/src/rules/no-format-string-injection/index.d.ts +28 -1
- package/src/rules/no-format-string-injection/index.js +5 -0
- package/src/rules/no-graphql-injection/index.d.ts +29 -1
- package/src/rules/no-graphql-injection/index.js +5 -0
- package/src/rules/no-hardcoded-credentials/index.d.ts +19 -1
- package/src/rules/no-hardcoded-credentials/index.js +5 -0
- package/src/rules/no-hardcoded-session-tokens/index.d.ts +8 -2
- package/src/rules/no-hardcoded-session-tokens/index.js +6 -1
- package/src/rules/no-improper-sanitization/index.d.ts +27 -1
- package/src/rules/no-improper-sanitization/index.js +5 -0
- package/src/rules/no-improper-type-validation/index.d.ts +27 -1
- package/src/rules/no-improper-type-validation/index.js +5 -0
- package/src/rules/no-insecure-comparison/index.d.ts +20 -1
- package/src/rules/no-insecure-comparison/index.js +5 -0
- package/src/rules/no-ldap-injection/index.d.ts +30 -1
- package/src/rules/no-ldap-injection/index.js +5 -0
- package/src/rules/no-missing-authentication/index.d.ts +20 -1
- package/src/rules/no-missing-authentication/index.js +5 -1
- package/src/rules/no-pii-in-logs/index.d.ts +8 -4
- package/src/rules/no-pii-in-logs/index.js +15 -12
- package/src/rules/no-privilege-escalation/index.d.ts +20 -1
- package/src/rules/no-privilege-escalation/index.js +5 -0
- package/src/rules/no-redos-vulnerable-regex/index.d.ts +22 -1
- package/src/rules/no-redos-vulnerable-regex/index.js +5 -0
- package/src/rules/no-sensitive-data-exposure/index.d.ts +20 -1
- package/src/rules/no-sensitive-data-exposure/index.js +5 -0
- package/src/rules/no-unchecked-loop-condition/index.d.ts +27 -1
- package/src/rules/no-unchecked-loop-condition/index.js +5 -0
- package/src/rules/no-unlimited-resource-allocation/index.d.ts +27 -1
- package/src/rules/no-unlimited-resource-allocation/index.js +5 -0
- package/src/rules/no-unsafe-deserialization/index.d.ts +31 -1
- package/src/rules/no-unsafe-deserialization/index.js +5 -0
- package/src/rules/no-unsafe-regex-construction/index.d.ts +22 -1
- package/src/rules/no-unsafe-regex-construction/index.js +5 -0
- package/src/rules/no-weak-password-recovery/index.d.ts +27 -1
- package/src/rules/no-weak-password-recovery/index.js +5 -0
- package/src/rules/no-xpath-injection/index.d.ts +30 -1
- package/src/rules/no-xpath-injection/index.js +5 -0
- package/src/rules/no-xxe-injection/index.d.ts +30 -1
- package/src/rules/no-xxe-injection/index.js +5 -0
- package/src/rules/require-backend-authorization/index.d.ts +8 -2
- package/src/rules/require-backend-authorization/index.js +6 -1
- package/src/rules/require-secure-defaults/index.d.ts +8 -4
- package/src/rules/require-secure-defaults/index.js +7 -6
- package/src/types/index.d.ts +10 -52
- package/src/types/index.js +3 -12
- package/src/rules/detect-child-process/index.d.ts +0 -11
- package/src/rules/detect-child-process/index.js +0 -529
- package/src/rules/detect-eval-with-expression/index.d.ts +0 -9
- package/src/rules/detect-eval-with-expression/index.js +0 -392
- package/src/rules/detect-mixed-content/index.d.ts +0 -8
- package/src/rules/detect-mixed-content/index.js +0 -44
- package/src/rules/detect-non-literal-fs-filename/index.d.ts +0 -7
- package/src/rules/detect-non-literal-fs-filename/index.js +0 -454
- package/src/rules/detect-suspicious-dependencies/index.d.ts +0 -8
- package/src/rules/detect-suspicious-dependencies/index.js +0 -71
- package/src/rules/no-allow-arbitrary-loads/index.d.ts +0 -8
- package/src/rules/no-allow-arbitrary-loads/index.js +0 -47
- package/src/rules/no-arbitrary-file-access/index.d.ts +0 -13
- package/src/rules/no-arbitrary-file-access/index.js +0 -195
- package/src/rules/no-buffer-overread/index.d.ts +0 -29
- package/src/rules/no-buffer-overread/index.js +0 -606
- package/src/rules/no-clickjacking/index.d.ts +0 -10
- package/src/rules/no-clickjacking/index.js +0 -396
- package/src/rules/no-client-side-auth-logic/index.d.ts +0 -6
- package/src/rules/no-client-side-auth-logic/index.js +0 -69
- package/src/rules/no-credentials-in-query-params/index.d.ts +0 -8
- package/src/rules/no-credentials-in-query-params/index.js +0 -57
- package/src/rules/no-data-in-temp-storage/index.d.ts +0 -6
- package/src/rules/no-data-in-temp-storage/index.js +0 -64
- package/src/rules/no-debug-code-in-production/index.d.ts +0 -8
- package/src/rules/no-debug-code-in-production/index.js +0 -51
- package/src/rules/no-disabled-certificate-validation/index.d.ts +0 -6
- package/src/rules/no-disabled-certificate-validation/index.js +0 -61
- package/src/rules/no-dynamic-dependency-loading/index.d.ts +0 -8
- package/src/rules/no-dynamic-dependency-loading/index.js +0 -51
- package/src/rules/no-exposed-debug-endpoints/index.d.ts +0 -6
- package/src/rules/no-exposed-debug-endpoints/index.js +0 -62
- package/src/rules/no-exposed-sensitive-data/index.d.ts +0 -11
- package/src/rules/no-exposed-sensitive-data/index.js +0 -340
- package/src/rules/no-http-urls/index.d.ts +0 -12
- package/src/rules/no-http-urls/index.js +0 -114
- package/src/rules/no-insecure-redirects/index.d.ts +0 -7
- package/src/rules/no-insecure-redirects/index.js +0 -216
- package/src/rules/no-insecure-websocket/index.d.ts +0 -6
- package/src/rules/no-insecure-websocket/index.js +0 -61
- package/src/rules/no-missing-cors-check/index.d.ts +0 -9
- package/src/rules/no-missing-cors-check/index.js +0 -399
- package/src/rules/no-missing-csrf-protection/index.d.ts +0 -11
- package/src/rules/no-missing-csrf-protection/index.js +0 -180
- package/src/rules/no-missing-security-headers/index.d.ts +0 -7
- package/src/rules/no-missing-security-headers/index.js +0 -218
- package/src/rules/no-password-in-url/index.d.ts +0 -8
- package/src/rules/no-password-in-url/index.js +0 -54
- package/src/rules/no-permissive-cors/index.d.ts +0 -8
- package/src/rules/no-permissive-cors/index.js +0 -65
- package/src/rules/no-sensitive-data-in-analytics/index.d.ts +0 -8
- package/src/rules/no-sensitive-data-in-analytics/index.js +0 -62
- package/src/rules/no-sensitive-data-in-cache/index.d.ts +0 -8
- package/src/rules/no-sensitive-data-in-cache/index.js +0 -52
- package/src/rules/no-toctou-vulnerability/index.d.ts +0 -7
- package/src/rules/no-toctou-vulnerability/index.js +0 -208
- package/src/rules/no-tracking-without-consent/index.d.ts +0 -6
- package/src/rules/no-tracking-without-consent/index.js +0 -67
- package/src/rules/no-unencrypted-transmission/index.d.ts +0 -11
- package/src/rules/no-unencrypted-transmission/index.js +0 -236
- package/src/rules/no-unescaped-url-parameter/index.d.ts +0 -9
- package/src/rules/no-unescaped-url-parameter/index.js +0 -355
- package/src/rules/no-unsafe-dynamic-require/index.d.ts +0 -5
- package/src/rules/no-unsafe-dynamic-require/index.js +0 -106
- package/src/rules/no-unvalidated-deeplinks/index.d.ts +0 -6
- package/src/rules/no-unvalidated-deeplinks/index.js +0 -62
- package/src/rules/no-unvalidated-user-input/index.d.ts +0 -9
- package/src/rules/no-unvalidated-user-input/index.js +0 -420
- package/src/rules/no-verbose-error-messages/index.d.ts +0 -8
- package/src/rules/no-verbose-error-messages/index.js +0 -68
- package/src/rules/no-zip-slip/index.d.ts +0 -9
- package/src/rules/no-zip-slip/index.js +0 -445
- package/src/rules/require-code-minification/index.d.ts +0 -8
- package/src/rules/require-code-minification/index.js +0 -47
- package/src/rules/require-csp-headers/index.d.ts +0 -6
- package/src/rules/require-csp-headers/index.js +0 -64
- package/src/rules/require-data-minimization/index.d.ts +0 -8
- package/src/rules/require-data-minimization/index.js +0 -53
- package/src/rules/require-dependency-integrity/index.d.ts +0 -6
- package/src/rules/require-dependency-integrity/index.js +0 -64
- package/src/rules/require-https-only/index.d.ts +0 -8
- package/src/rules/require-https-only/index.js +0 -62
- package/src/rules/require-mime-type-validation/index.d.ts +0 -6
- package/src/rules/require-mime-type-validation/index.js +0 -66
- package/src/rules/require-network-timeout/index.d.ts +0 -8
- package/src/rules/require-network-timeout/index.js +0 -50
- package/src/rules/require-package-lock/index.d.ts +0 -8
- package/src/rules/require-package-lock/index.js +0 -63
- package/src/rules/require-secure-credential-storage/index.d.ts +0 -8
- package/src/rules/require-secure-credential-storage/index.js +0 -50
- package/src/rules/require-secure-deletion/index.d.ts +0 -8
- package/src/rules/require-secure-deletion/index.js +0 -44
- package/src/rules/require-storage-encryption/index.d.ts +0 -8
- package/src/rules/require-storage-encryption/index.js +0 -50
- package/src/rules/require-url-validation/index.d.ts +0 -6
- package/src/rules/require-url-validation/index.js +0 -72
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @fileoverview Disallow hardcoded HTTP URLs
|
|
4
|
-
* @see https://owasp.org/www-project-mobile-top-10/
|
|
5
|
-
* @see https://cwe.mitre.org/data/definitions/319.html
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.noHttpUrls = void 0;
|
|
9
|
-
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
-
exports.noHttpUrls = (0, eslint_devkit_1.createRule)({
|
|
11
|
-
name: 'no-http-urls',
|
|
12
|
-
meta: {
|
|
13
|
-
type: 'problem',
|
|
14
|
-
docs: {
|
|
15
|
-
description: 'Disallow hardcoded HTTP URLs (require HTTPS)',
|
|
16
|
-
},
|
|
17
|
-
messages: {
|
|
18
|
-
insecureHttp: (0, eslint_devkit_1.formatLLMMessage)({
|
|
19
|
-
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
20
|
-
issueName: 'Insecure HTTP URL',
|
|
21
|
-
cwe: 'CWE-319',
|
|
22
|
-
owasp: 'A02:2021',
|
|
23
|
-
cvss: 7.5,
|
|
24
|
-
description: 'Hardcoded HTTP URL detected: "{{url}}"',
|
|
25
|
-
severity: 'HIGH',
|
|
26
|
-
compliance: ['SOC2', 'PCI-DSS', 'HIPAA'],
|
|
27
|
-
fix: 'Use HTTPS instead: const url = "https://..."',
|
|
28
|
-
documentationLink: 'https://cwe.mitre.org/data/definitions/319.html',
|
|
29
|
-
}),
|
|
30
|
-
insecureHttpWithException: (0, eslint_devkit_1.formatLLMMessage)({
|
|
31
|
-
icon: eslint_devkit_1.MessageIcons.WARNING,
|
|
32
|
-
issueName: 'Insecure HTTP URL',
|
|
33
|
-
cwe: 'CWE-319',
|
|
34
|
-
owasp: 'A02:2021',
|
|
35
|
-
cvss: 5.3,
|
|
36
|
-
description: 'HTTP URL detected: "{{url}}"',
|
|
37
|
-
severity: 'MEDIUM',
|
|
38
|
-
fix: 'Use HTTPS or add to allowedHosts config',
|
|
39
|
-
documentationLink: 'https://cwe.mitre.org/data/definitions/319.html',
|
|
40
|
-
}),
|
|
41
|
-
},
|
|
42
|
-
schema: [
|
|
43
|
-
{
|
|
44
|
-
type: 'object',
|
|
45
|
-
properties: {
|
|
46
|
-
allowedHosts: {
|
|
47
|
-
type: 'array',
|
|
48
|
-
items: { type: 'string' },
|
|
49
|
-
description: 'List of hostnames allowed to use HTTP (e.g., localhost, 127.0.0.1)',
|
|
50
|
-
},
|
|
51
|
-
allowedPorts: {
|
|
52
|
-
type: 'array',
|
|
53
|
-
items: { type: 'number' },
|
|
54
|
-
description: 'List of ports allowed for HTTP (e.g., 3000, 8080 for development)',
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
additionalProperties: false,
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
|
-
defaultOptions: [
|
|
62
|
-
{
|
|
63
|
-
allowedHosts: ['localhost', '127.0.0.1'],
|
|
64
|
-
allowedPorts: [],
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
create(context) {
|
|
68
|
-
const [options = {}] = context.options;
|
|
69
|
-
const allowedHosts = options.allowedHosts ?? ['localhost', '127.0.0.1'];
|
|
70
|
-
const allowedPorts = options.allowedPorts ?? [];
|
|
71
|
-
function isAllowedException(url) {
|
|
72
|
-
try {
|
|
73
|
-
const parsedUrl = new URL(url);
|
|
74
|
-
// Check if host is in allowed list
|
|
75
|
-
if (allowedHosts.includes(parsedUrl.hostname)) {
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
// Check if port is in allowed list
|
|
79
|
-
if (parsedUrl.port && allowedPorts.includes(parseInt(parsedUrl.port, 10))) {
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
// If URL parsing fails, treat as pattern match
|
|
86
|
-
return allowedHosts.some(host => url.includes(host));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function checkStringValue(node, value) {
|
|
90
|
-
const httpPattern = /^http:\/\//i;
|
|
91
|
-
if (httpPattern.test(value) && !isAllowedException(value)) {
|
|
92
|
-
context.report({
|
|
93
|
-
node,
|
|
94
|
-
messageId: allowedHosts.length > 0 || allowedPorts.length > 0
|
|
95
|
-
? 'insecureHttpWithException'
|
|
96
|
-
: 'insecureHttp',
|
|
97
|
-
data: { url: value },
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
Literal(node) {
|
|
103
|
-
if (typeof node.value === 'string') {
|
|
104
|
-
checkStringValue(node, node.value);
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
TemplateElement(node) {
|
|
108
|
-
if (node.value.cooked) {
|
|
109
|
-
checkStringValue(node, node.value.cooked);
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export interface Options {
|
|
2
|
-
/** Ignore in test files. Default: true */
|
|
3
|
-
ignoreInTests?: boolean;
|
|
4
|
-
/** Allowed redirect domains. Default: [] */
|
|
5
|
-
allowedDomains?: string[];
|
|
6
|
-
}
|
|
7
|
-
export declare const noInsecureRedirects: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noInsecureRedirects = void 0;
|
|
4
|
-
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
5
|
-
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
6
|
-
/**
|
|
7
|
-
* Check if redirect URL is validated
|
|
8
|
-
*/
|
|
9
|
-
function isRedirectValidated(node, sourceCode) {
|
|
10
|
-
const callText = sourceCode.getText(node);
|
|
11
|
-
// Check if URL is from user input (req.query, req.body, req.params)
|
|
12
|
-
const userInputPattern = /\b(req\.(query|body|params)|window\.location|document\.location)\b/;
|
|
13
|
-
if (!userInputPattern.test(callText)) {
|
|
14
|
-
// Not from user input, assume safe
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
// Look for validation patterns in the surrounding code
|
|
18
|
-
// This is a simplified static analysis - in practice, would need data flow analysis
|
|
19
|
-
const program = sourceCode.ast;
|
|
20
|
-
const nodeStart = node.loc?.start;
|
|
21
|
-
const nodeEnd = node.loc?.end;
|
|
22
|
-
/* c8 ignore start -- Guard clause: loc is always present in RuleTester */
|
|
23
|
-
if (!nodeStart || !nodeEnd || !program) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
/* c8 ignore stop */
|
|
27
|
-
// Check for validation function calls before this redirect
|
|
28
|
-
const validationPatterns = [
|
|
29
|
-
/\b(validateUrl|validateRedirect|isValidUrl|isSafeUrl)\s*\(/,
|
|
30
|
-
/\b(whitelist|allowedDomains|permittedUrls)\s*\./,
|
|
31
|
-
/\b(url\.hostname|url\.host)\s*===/,
|
|
32
|
-
/\ballowedDomains\.includes\s*\(/,
|
|
33
|
-
/\bstartsWith\s*\(\s*['"]/,
|
|
34
|
-
/\bendsWith\s*\(\s*['"]/,
|
|
35
|
-
/\bindexOf\s*\(\s*['"]/,
|
|
36
|
-
/\bmatch\s*\(\s*\//,
|
|
37
|
-
];
|
|
38
|
-
// Look for validation in the same function scope
|
|
39
|
-
let current = node;
|
|
40
|
-
let depth = 0;
|
|
41
|
-
const maxDepth = 20;
|
|
42
|
-
while (current && depth < maxDepth) {
|
|
43
|
-
// Check siblings before this node
|
|
44
|
-
const parent = current.parent;
|
|
45
|
-
if (!parent)
|
|
46
|
-
break;
|
|
47
|
-
if (parent.type === 'BlockStatement') {
|
|
48
|
-
const body = parent.body;
|
|
49
|
-
const currentIndex = body.indexOf(current);
|
|
50
|
-
// Check previous statements in the same block
|
|
51
|
-
for (let i = currentIndex - 1; i >= 0 && i >= currentIndex - 5; i--) {
|
|
52
|
-
const stmt = body[i];
|
|
53
|
-
const stmtText = sourceCode.getText(stmt);
|
|
54
|
-
if (validationPatterns.some(pattern => pattern.test(stmtText))) {
|
|
55
|
-
return true; // Found validation
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
current = parent;
|
|
60
|
-
depth++;
|
|
61
|
-
}
|
|
62
|
-
// No validation found
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
exports.noInsecureRedirects = (0, eslint_devkit_2.createRule)({
|
|
66
|
-
name: 'no-insecure-redirects',
|
|
67
|
-
meta: {
|
|
68
|
-
type: 'problem',
|
|
69
|
-
docs: {
|
|
70
|
-
description: 'Detects open redirect vulnerabilities',
|
|
71
|
-
},
|
|
72
|
-
hasSuggestions: true,
|
|
73
|
-
messages: {
|
|
74
|
-
insecureRedirect: (0, eslint_devkit_1.formatLLMMessage)({
|
|
75
|
-
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
76
|
-
issueName: 'Open redirect',
|
|
77
|
-
cwe: 'CWE-601',
|
|
78
|
-
description: 'Unvalidated redirect detected - user-controlled URL',
|
|
79
|
-
severity: 'HIGH',
|
|
80
|
-
fix: 'Whitelist allowed domains or validate redirect target',
|
|
81
|
-
documentationLink: 'https://owasp.org/www-community/vulnerabilities/Unvalidated_Redirects_and_Forwards',
|
|
82
|
-
}),
|
|
83
|
-
whitelistDomains: (0, eslint_devkit_1.formatLLMMessage)({
|
|
84
|
-
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
85
|
-
issueName: 'Whitelist Domains',
|
|
86
|
-
description: 'Whitelist allowed redirect domains',
|
|
87
|
-
severity: 'LOW',
|
|
88
|
-
fix: 'if (allowedDomains.includes(url.hostname)) redirect(url)',
|
|
89
|
-
documentationLink: 'https://owasp.org/www-community/vulnerabilities/Unvalidated_Redirects_and_Forwards',
|
|
90
|
-
}),
|
|
91
|
-
validateRedirect: (0, eslint_devkit_1.formatLLMMessage)({
|
|
92
|
-
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
93
|
-
issueName: 'Validate Redirect',
|
|
94
|
-
description: 'Validate redirect URL before use',
|
|
95
|
-
severity: 'LOW',
|
|
96
|
-
fix: 'validateRedirectUrl(userInput) before redirect',
|
|
97
|
-
documentationLink: 'https://owasp.org/www-community/vulnerabilities/Unvalidated_Redirects_and_Forwards',
|
|
98
|
-
}),
|
|
99
|
-
useRelativeUrl: (0, eslint_devkit_1.formatLLMMessage)({
|
|
100
|
-
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
101
|
-
issueName: 'Use Relative URL',
|
|
102
|
-
description: 'Use relative URLs for internal redirects',
|
|
103
|
-
severity: 'LOW',
|
|
104
|
-
fix: 'redirect("/internal/path") instead of absolute URLs',
|
|
105
|
-
documentationLink: 'https://owasp.org/www-community/vulnerabilities/Unvalidated_Redirects_and_Forwards',
|
|
106
|
-
}),
|
|
107
|
-
},
|
|
108
|
-
schema: [
|
|
109
|
-
{
|
|
110
|
-
type: 'object',
|
|
111
|
-
properties: {
|
|
112
|
-
ignoreInTests: {
|
|
113
|
-
type: 'boolean',
|
|
114
|
-
default: true,
|
|
115
|
-
},
|
|
116
|
-
allowedDomains: {
|
|
117
|
-
type: 'array',
|
|
118
|
-
items: { type: 'string' },
|
|
119
|
-
default: [],
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
additionalProperties: false,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
},
|
|
126
|
-
defaultOptions: [
|
|
127
|
-
{
|
|
128
|
-
ignoreInTests: true,
|
|
129
|
-
allowedDomains: [],
|
|
130
|
-
},
|
|
131
|
-
],
|
|
132
|
-
create(context, [options = {}]) {
|
|
133
|
-
const { ignoreInTests = true } = options || {};
|
|
134
|
-
const filename = context.getFilename();
|
|
135
|
-
const isTestFile = ignoreInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
|
|
136
|
-
if (isTestFile) {
|
|
137
|
-
return {};
|
|
138
|
-
}
|
|
139
|
-
const sourceCode = context.sourceCode || context.sourceCode;
|
|
140
|
-
/**
|
|
141
|
-
* Check redirect calls and assignments
|
|
142
|
-
*/
|
|
143
|
-
function checkCallExpression(node) {
|
|
144
|
-
// Check for res.redirect, window.location, etc.
|
|
145
|
-
if (node.callee.type === 'MemberExpression' &&
|
|
146
|
-
node.callee.property.type === 'Identifier') {
|
|
147
|
-
const methodName = node.callee.property.name;
|
|
148
|
-
if (['redirect', 'replace', 'assign'].includes(methodName)) {
|
|
149
|
-
// Check if redirect URL is validated
|
|
150
|
-
if (!isRedirectValidated(node, sourceCode)) {
|
|
151
|
-
context.report({
|
|
152
|
-
node,
|
|
153
|
-
messageId: 'insecureRedirect',
|
|
154
|
-
suggest: [
|
|
155
|
-
{
|
|
156
|
-
messageId: 'whitelistDomains',
|
|
157
|
-
fix: () => null,
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
messageId: 'validateRedirect',
|
|
161
|
-
fix: () => null,
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
messageId: 'useRelativeUrl',
|
|
165
|
-
fix: () => null,
|
|
166
|
-
},
|
|
167
|
-
],
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Check assignment expressions like window.location.href = ...
|
|
175
|
-
*/
|
|
176
|
-
function checkAssignmentExpression(node) {
|
|
177
|
-
// Check for window.location.href assignments
|
|
178
|
-
if (node.left.type === 'MemberExpression' &&
|
|
179
|
-
node.left.object.type === 'MemberExpression' &&
|
|
180
|
-
node.left.object.object.type === 'Identifier' &&
|
|
181
|
-
node.left.object.object.name === 'window' &&
|
|
182
|
-
node.left.object.property.type === 'Identifier' &&
|
|
183
|
-
node.left.object.property.name === 'location' &&
|
|
184
|
-
node.left.property.type === 'Identifier' &&
|
|
185
|
-
['href', 'replace', 'assign'].includes(node.left.property.name)) {
|
|
186
|
-
// Check if assignment value comes from user input
|
|
187
|
-
const rightText = sourceCode.getText(node.right);
|
|
188
|
-
const userInputPattern = /\b(req\.(query|body|params)|window\.location|document\.location)\b/;
|
|
189
|
-
if (userInputPattern.test(rightText)) {
|
|
190
|
-
context.report({
|
|
191
|
-
node,
|
|
192
|
-
messageId: 'insecureRedirect',
|
|
193
|
-
suggest: [
|
|
194
|
-
{
|
|
195
|
-
messageId: 'whitelistDomains',
|
|
196
|
-
fix: () => null,
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
messageId: 'validateRedirect',
|
|
200
|
-
fix: () => null,
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
messageId: 'useRelativeUrl',
|
|
204
|
-
fix: () => null,
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return {
|
|
212
|
-
CallExpression: checkCallExpression,
|
|
213
|
-
AssignmentExpression: checkAssignmentExpression,
|
|
214
|
-
};
|
|
215
|
-
},
|
|
216
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @fileoverview Require secure WebSocket connections (wss://)
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.noInsecureWebsocket = void 0;
|
|
7
|
-
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
8
|
-
exports.noInsecureWebsocket = (0, eslint_devkit_1.createRule)({
|
|
9
|
-
name: 'no-insecure-websocket',
|
|
10
|
-
meta: {
|
|
11
|
-
type: 'problem',
|
|
12
|
-
docs: {
|
|
13
|
-
description: 'Require secure WebSocket connections (wss://)',
|
|
14
|
-
},
|
|
15
|
-
messages: {
|
|
16
|
-
violationDetected: (0, eslint_devkit_1.formatLLMMessage)({
|
|
17
|
-
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
18
|
-
issueName: 'Insecure WebSocket',
|
|
19
|
-
cwe: 'CWE-319',
|
|
20
|
-
description: 'Insecure WebSocket connection (ws://) - data transmitted in clear text',
|
|
21
|
-
severity: 'HIGH',
|
|
22
|
-
fix: 'Use wss:// instead of ws:// for secure WebSocket connections',
|
|
23
|
-
documentationLink: 'https://cwe.mitre.org/data/definitions/319.html',
|
|
24
|
-
})
|
|
25
|
-
},
|
|
26
|
-
schema: [],
|
|
27
|
-
},
|
|
28
|
-
defaultOptions: [],
|
|
29
|
-
create(context) {
|
|
30
|
-
function report(node) {
|
|
31
|
-
context.report({ node, messageId: 'violationDetected' });
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
NewExpression(node) {
|
|
35
|
-
// Check for new WebSocket('ws://...')
|
|
36
|
-
if (node.callee.type === 'Identifier' && node.callee.name === 'WebSocket') {
|
|
37
|
-
const urlArg = node.arguments[0];
|
|
38
|
-
// Check literal string
|
|
39
|
-
if (urlArg && urlArg.type === 'Literal' &&
|
|
40
|
-
typeof urlArg.value === 'string' &&
|
|
41
|
-
urlArg.value.startsWith('ws://')) {
|
|
42
|
-
report(node);
|
|
43
|
-
}
|
|
44
|
-
// Check template literal
|
|
45
|
-
if (urlArg && urlArg.type === 'TemplateLiteral') {
|
|
46
|
-
const text = context.sourceCode.getText(urlArg);
|
|
47
|
-
if (text.includes('ws://')) {
|
|
48
|
-
report(node);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
Literal(node) {
|
|
54
|
-
// Check for ws:// URLs in string literals
|
|
55
|
-
if (typeof node.value === 'string' && node.value.startsWith('ws://')) {
|
|
56
|
-
report(node);
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
},
|
|
61
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export interface Options {
|
|
2
|
-
/** Allow missing CORS checks in test files. Default: false */
|
|
3
|
-
allowInTests?: boolean;
|
|
4
|
-
/** Trusted CORS libraries. Default: ['cors', '@koa/cors', 'express-cors'] */
|
|
5
|
-
trustedLibraries?: string[];
|
|
6
|
-
/** Additional safe patterns to ignore. Default: [] */
|
|
7
|
-
ignorePatterns?: string[];
|
|
8
|
-
}
|
|
9
|
-
export declare const noMissingCorsCheck: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|