eslint-plugin-secure-coding 3.0.1 → 3.0.3

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.
Files changed (135) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +41 -206
  3. package/package.json +6 -5
  4. package/src/index.d.ts +2 -2
  5. package/src/index.js +29 -263
  6. package/src/rules/detect-non-literal-regexp/index.d.ts +3 -1
  7. package/src/rules/detect-object-injection/index.d.ts +3 -1
  8. package/src/rules/detect-object-injection/index.js +63 -0
  9. package/src/rules/detect-weak-password-validation/index.d.ts +3 -1
  10. package/src/rules/no-directive-injection/index.d.ts +3 -1
  11. package/src/rules/no-electron-security-issues/index.d.ts +3 -1
  12. package/src/rules/no-format-string-injection/index.d.ts +3 -1
  13. package/src/rules/no-graphql-injection/index.d.ts +10 -1
  14. package/src/rules/no-graphql-injection/index.js +294 -38
  15. package/src/rules/no-hardcoded-credentials/index.d.ts +3 -1
  16. package/src/rules/no-hardcoded-session-tokens/index.d.ts +3 -1
  17. package/src/rules/no-improper-sanitization/index.d.ts +3 -1
  18. package/src/rules/no-improper-type-validation/index.d.ts +3 -1
  19. package/src/rules/no-insecure-comparison/index.d.ts +3 -1
  20. package/src/rules/no-insecure-comparison/index.js +9 -0
  21. package/src/rules/no-ldap-injection/index.d.ts +3 -1
  22. package/src/rules/no-missing-authentication/index.d.ts +3 -1
  23. package/src/rules/no-missing-authentication/index.js +0 -1
  24. package/src/rules/no-pii-in-logs/index.d.ts +3 -1
  25. package/src/rules/no-privilege-escalation/index.d.ts +3 -1
  26. package/src/rules/no-redos-vulnerable-regex/index.d.ts +3 -1
  27. package/src/rules/no-sensitive-data-exposure/index.d.ts +3 -1
  28. package/src/rules/no-sensitive-data-exposure/index.js +33 -18
  29. package/src/rules/no-unchecked-loop-condition/index.d.ts +3 -1
  30. package/src/rules/no-unlimited-resource-allocation/index.d.ts +3 -1
  31. package/src/rules/no-unsafe-deserialization/index.d.ts +3 -1
  32. package/src/rules/no-unsafe-regex-construction/index.d.ts +3 -1
  33. package/src/rules/no-weak-password-recovery/index.d.ts +3 -1
  34. package/src/rules/no-xpath-injection/index.d.ts +3 -1
  35. package/src/rules/no-xpath-injection/index.js +26 -2
  36. package/src/rules/no-xxe-injection/index.d.ts +3 -1
  37. package/src/rules/require-backend-authorization/index.d.ts +3 -1
  38. package/src/rules/require-secure-defaults/index.d.ts +3 -1
  39. package/src/types/index.d.ts +5 -52
  40. package/src/rules/detect-child-process/index.d.ts +0 -28
  41. package/src/rules/detect-child-process/index.js +0 -534
  42. package/src/rules/detect-eval-with-expression/index.d.ts +0 -26
  43. package/src/rules/detect-eval-with-expression/index.js +0 -397
  44. package/src/rules/detect-mixed-content/index.d.ts +0 -10
  45. package/src/rules/detect-mixed-content/index.js +0 -45
  46. package/src/rules/detect-non-literal-fs-filename/index.d.ts +0 -24
  47. package/src/rules/detect-non-literal-fs-filename/index.js +0 -459
  48. package/src/rules/detect-suspicious-dependencies/index.d.ts +0 -10
  49. package/src/rules/detect-suspicious-dependencies/index.js +0 -76
  50. package/src/rules/no-allow-arbitrary-loads/index.d.ts +0 -10
  51. package/src/rules/no-allow-arbitrary-loads/index.js +0 -48
  52. package/src/rules/no-arbitrary-file-access/index.d.ts +0 -10
  53. package/src/rules/no-arbitrary-file-access/index.js +0 -200
  54. package/src/rules/no-buffer-overread/index.d.ts +0 -37
  55. package/src/rules/no-buffer-overread/index.js +0 -611
  56. package/src/rules/no-clickjacking/index.d.ts +0 -34
  57. package/src/rules/no-clickjacking/index.js +0 -401
  58. package/src/rules/no-client-side-auth-logic/index.d.ts +0 -10
  59. package/src/rules/no-client-side-auth-logic/index.js +0 -74
  60. package/src/rules/no-credentials-in-query-params/index.d.ts +0 -10
  61. package/src/rules/no-credentials-in-query-params/index.js +0 -62
  62. package/src/rules/no-data-in-temp-storage/index.d.ts +0 -10
  63. package/src/rules/no-data-in-temp-storage/index.js +0 -69
  64. package/src/rules/no-debug-code-in-production/index.d.ts +0 -10
  65. package/src/rules/no-debug-code-in-production/index.js +0 -54
  66. package/src/rules/no-disabled-certificate-validation/index.d.ts +0 -10
  67. package/src/rules/no-disabled-certificate-validation/index.js +0 -66
  68. package/src/rules/no-dynamic-dependency-loading/index.d.ts +0 -10
  69. package/src/rules/no-dynamic-dependency-loading/index.js +0 -54
  70. package/src/rules/no-exposed-debug-endpoints/index.d.ts +0 -10
  71. package/src/rules/no-exposed-debug-endpoints/index.js +0 -67
  72. package/src/rules/no-exposed-sensitive-data/index.d.ts +0 -28
  73. package/src/rules/no-exposed-sensitive-data/index.js +0 -345
  74. package/src/rules/no-http-urls/index.d.ts +0 -15
  75. package/src/rules/no-http-urls/index.js +0 -119
  76. package/src/rules/no-insecure-redirects/index.d.ts +0 -24
  77. package/src/rules/no-insecure-redirects/index.js +0 -221
  78. package/src/rules/no-insecure-websocket/index.d.ts +0 -10
  79. package/src/rules/no-insecure-websocket/index.js +0 -66
  80. package/src/rules/no-missing-cors-check/index.d.ts +0 -26
  81. package/src/rules/no-missing-cors-check/index.js +0 -404
  82. package/src/rules/no-missing-csrf-protection/index.d.ts +0 -28
  83. package/src/rules/no-missing-csrf-protection/index.js +0 -185
  84. package/src/rules/no-missing-security-headers/index.d.ts +0 -24
  85. package/src/rules/no-missing-security-headers/index.js +0 -223
  86. package/src/rules/no-password-in-url/index.d.ts +0 -10
  87. package/src/rules/no-password-in-url/index.js +0 -55
  88. package/src/rules/no-permissive-cors/index.d.ts +0 -10
  89. package/src/rules/no-permissive-cors/index.js +0 -74
  90. package/src/rules/no-sensitive-data-in-analytics/index.d.ts +0 -10
  91. package/src/rules/no-sensitive-data-in-analytics/index.js +0 -66
  92. package/src/rules/no-sensitive-data-in-cache/index.d.ts +0 -10
  93. package/src/rules/no-sensitive-data-in-cache/index.js +0 -53
  94. package/src/rules/no-toctou-vulnerability/index.d.ts +0 -24
  95. package/src/rules/no-toctou-vulnerability/index.js +0 -213
  96. package/src/rules/no-tracking-without-consent/index.d.ts +0 -10
  97. package/src/rules/no-tracking-without-consent/index.js +0 -72
  98. package/src/rules/no-unencrypted-transmission/index.d.ts +0 -28
  99. package/src/rules/no-unencrypted-transmission/index.js +0 -241
  100. package/src/rules/no-unescaped-url-parameter/index.d.ts +0 -26
  101. package/src/rules/no-unescaped-url-parameter/index.js +0 -360
  102. package/src/rules/no-unsafe-dynamic-require/index.d.ts +0 -17
  103. package/src/rules/no-unsafe-dynamic-require/index.js +0 -111
  104. package/src/rules/no-unvalidated-deeplinks/index.d.ts +0 -10
  105. package/src/rules/no-unvalidated-deeplinks/index.js +0 -67
  106. package/src/rules/no-unvalidated-user-input/index.d.ts +0 -26
  107. package/src/rules/no-unvalidated-user-input/index.js +0 -425
  108. package/src/rules/no-verbose-error-messages/index.d.ts +0 -10
  109. package/src/rules/no-verbose-error-messages/index.js +0 -73
  110. package/src/rules/no-zip-slip/index.d.ts +0 -33
  111. package/src/rules/no-zip-slip/index.js +0 -450
  112. package/src/rules/require-code-minification/index.d.ts +0 -10
  113. package/src/rules/require-code-minification/index.js +0 -48
  114. package/src/rules/require-csp-headers/index.d.ts +0 -10
  115. package/src/rules/require-csp-headers/index.js +0 -69
  116. package/src/rules/require-data-minimization/index.d.ts +0 -10
  117. package/src/rules/require-data-minimization/index.js +0 -55
  118. package/src/rules/require-dependency-integrity/index.d.ts +0 -10
  119. package/src/rules/require-dependency-integrity/index.js +0 -69
  120. package/src/rules/require-https-only/index.d.ts +0 -10
  121. package/src/rules/require-https-only/index.js +0 -67
  122. package/src/rules/require-mime-type-validation/index.d.ts +0 -10
  123. package/src/rules/require-mime-type-validation/index.js +0 -71
  124. package/src/rules/require-network-timeout/index.d.ts +0 -10
  125. package/src/rules/require-network-timeout/index.js +0 -57
  126. package/src/rules/require-package-lock/index.d.ts +0 -10
  127. package/src/rules/require-package-lock/index.js +0 -64
  128. package/src/rules/require-secure-credential-storage/index.d.ts +0 -10
  129. package/src/rules/require-secure-credential-storage/index.js +0 -53
  130. package/src/rules/require-secure-deletion/index.d.ts +0 -10
  131. package/src/rules/require-secure-deletion/index.js +0 -45
  132. package/src/rules/require-storage-encryption/index.d.ts +0 -10
  133. package/src/rules/require-storage-encryption/index.js +0 -53
  134. package/src/rules/require-url-validation/index.d.ts +0 -10
  135. package/src/rules/require-url-validation/index.js +0 -77
@@ -1,54 +0,0 @@
1
- "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.noDebugCodeInProduction = void 0;
9
- /**
10
- * @fileoverview Detect debug code in production
11
- * @see https://owasp.org/www-project-mobile-top-10/
12
- * @see https://cwe.mitre.org/data/definitions/489.html
13
- */
14
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
15
- exports.noDebugCodeInProduction = (0, eslint_devkit_1.createRule)({
16
- name: 'no-debug-code-in-production',
17
- meta: {
18
- type: 'problem',
19
- docs: {
20
- description: 'Detect debug code in production',
21
- },
22
- messages: {
23
- violationDetected: (0, eslint_devkit_1.formatLLMMessage)({
24
- icon: eslint_devkit_1.MessageIcons.SECURITY,
25
- issueName: 'violation Detected',
26
- cwe: 'CWE-489',
27
- description: 'Detect debug code in production detected - DEBUG, __DEV__, console',
28
- severity: 'HIGH',
29
- fix: 'Review and apply secure practices',
30
- documentationLink: 'https://cwe.mitre.org/data/definitions/489.html',
31
- })
32
- },
33
- schema: [],
34
- },
35
- defaultOptions: [],
36
- create(context) {
37
- return {
38
- Identifier(node) {
39
- if (['DEBUG', '__DEV__'].includes(node.name)) {
40
- context.report({ node, messageId: 'violationDetected' });
41
- }
42
- },
43
- CallExpression(node) {
44
- if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
45
- node.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
46
- node.callee.object.name === 'console' &&
47
- node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
48
- node.callee.property.name === 'log') {
49
- context.report({ node, messageId: 'violationDetected' });
50
- }
51
- },
52
- };
53
- },
54
- });
@@ -1,10 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- export interface Options {
7
- }
8
- type RuleOptions = [Options?];
9
- export declare const noDisabledCertificateValidation: import("@typescript-eslint/utils/ts-eslint").RuleModule<"violationDetected", RuleOptions, unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
10
- export {};
@@ -1,66 +0,0 @@
1
- "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.noDisabledCertificateValidation = void 0;
9
- /**
10
- * @fileoverview Prevent disabled SSL/TLS certificate validation
11
- */
12
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
13
- exports.noDisabledCertificateValidation = (0, eslint_devkit_1.createRule)({
14
- name: 'no-disabled-certificate-validation',
15
- meta: {
16
- type: 'problem',
17
- docs: {
18
- description: 'Prevent disabled SSL/TLS certificate validation',
19
- },
20
- messages: {
21
- violationDetected: (0, eslint_devkit_1.formatLLMMessage)({
22
- icon: eslint_devkit_1.MessageIcons.SECURITY,
23
- issueName: 'Disabled Certificate Validation',
24
- cwe: 'CWE-295',
25
- description: 'SSL/TLS certificate validation is disabled - man-in-the-middle attack possible',
26
- severity: 'CRITICAL',
27
- fix: 'Remove rejectUnauthorized: false or verify: false, fix certificate issues properly',
28
- documentationLink: 'https://cwe.mitre.org/data/definitions/295.html',
29
- })
30
- },
31
- schema: [],
32
- },
33
- defaultOptions: [],
34
- create(context) {
35
- function report(node) {
36
- context.report({ node, messageId: 'violationDetected' });
37
- }
38
- const dangerousProperties = ['rejectUnauthorized', 'strictSSL', 'verify'];
39
- return {
40
- Property(node) {
41
- // Check for dangerous SSL options set to false
42
- if (node.key.type === 'Identifier' &&
43
- dangerousProperties.includes(node.key.name) &&
44
- node.value.type === 'Literal' &&
45
- node.value.value === false) {
46
- report(node);
47
- }
48
- },
49
- AssignmentExpression(node) {
50
- // Check for NODE_TLS_REJECT_UNAUTHORIZED = '0'
51
- if (node.left.type === 'MemberExpression' &&
52
- node.left.object.type === 'MemberExpression' &&
53
- node.left.object.object.type === 'Identifier' &&
54
- node.left.object.object.name === 'process' &&
55
- node.left.object.property.type === 'Identifier' &&
56
- node.left.object.property.name === 'env' &&
57
- node.left.property.type === 'Identifier' &&
58
- node.left.property.name === 'NODE_TLS_REJECT_UNAUTHORIZED') {
59
- if (node.right.type === 'Literal' && node.right.value === '0') {
60
- report(node);
61
- }
62
- }
63
- },
64
- };
65
- },
66
- });
@@ -1,10 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- export interface Options {
7
- }
8
- type RuleOptions = [Options?];
9
- export declare const noDynamicDependencyLoading: import("@typescript-eslint/utils/ts-eslint").RuleModule<"violationDetected", RuleOptions, unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
10
- export {};
@@ -1,54 +0,0 @@
1
- "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.noDynamicDependencyLoading = void 0;
9
- /**
10
- * @fileoverview Prevent dynamic dependency injection
11
- * @see https://owasp.org/www-project-mobile-top-10/
12
- * @see https://cwe.mitre.org/data/definitions/494.html
13
- */
14
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
15
- exports.noDynamicDependencyLoading = (0, eslint_devkit_1.createRule)({
16
- name: 'no-dynamic-dependency-loading',
17
- meta: {
18
- type: 'problem',
19
- docs: {
20
- description: 'Prevent runtime dependency injection with dynamic paths',
21
- },
22
- messages: {
23
- violationDetected: (0, eslint_devkit_1.formatLLMMessage)({
24
- icon: eslint_devkit_1.MessageIcons.SECURITY,
25
- issueName: 'violation Detected',
26
- cwe: 'CWE-1104',
27
- description: 'Dynamic import/require detected - use static imports for security',
28
- severity: 'HIGH',
29
- fix: 'Review and apply secure practices',
30
- documentationLink: 'https://cwe.mitre.org/data/definitions/1104.html',
31
- })
32
- },
33
- schema: [],
34
- },
35
- defaultOptions: [],
36
- create(context) {
37
- return {
38
- CallExpression(node) {
39
- // Dynamic require
40
- if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
41
- node.callee.name === 'require' &&
42
- node.arguments[0]?.type !== eslint_devkit_1.AST_NODE_TYPES.Literal) {
43
- context.report({ node, messageId: 'violationDetected' });
44
- }
45
- },
46
- ImportExpression(node) {
47
- // Dynamic import()
48
- if (node.source.type !== 'Literal') {
49
- context.report({ node, messageId: 'violationDetected' });
50
- }
51
- },
52
- };
53
- },
54
- });
@@ -1,10 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- export interface Options {
7
- }
8
- type RuleOptions = [Options?];
9
- export declare const noExposedDebugEndpoints: import("@typescript-eslint/utils/ts-eslint").RuleModule<"violationDetected", RuleOptions, unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
10
- export {};
@@ -1,67 +0,0 @@
1
- "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.noExposedDebugEndpoints = void 0;
9
- /**
10
- * @fileoverview Detect debug endpoints without auth
11
- */
12
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
13
- exports.noExposedDebugEndpoints = (0, eslint_devkit_1.createRule)({
14
- name: 'no-exposed-debug-endpoints',
15
- meta: {
16
- type: 'problem',
17
- docs: {
18
- description: 'Detect debug endpoints without auth',
19
- },
20
- messages: {
21
- violationDetected: (0, eslint_devkit_1.formatLLMMessage)({
22
- icon: eslint_devkit_1.MessageIcons.SECURITY,
23
- issueName: 'Exposed Debug Endpoint',
24
- cwe: 'CWE-489',
25
- description: 'Debug endpoint exposed without authentication',
26
- severity: 'HIGH',
27
- fix: 'Remove debug endpoints from production or add authentication',
28
- documentationLink: 'https://cwe.mitre.org/data/definitions/489.html',
29
- })
30
- },
31
- schema: [],
32
- },
33
- defaultOptions: [],
34
- create(context) {
35
- function report(node) {
36
- context.report({ node, messageId: 'violationDetected' });
37
- }
38
- const debugPaths = ['/debug', '/__debug__', '/admin', '/_admin', '/test', '/health'];
39
- return {
40
- CallExpression(node) {
41
- // Detect app.get('/debug', handler)
42
- if (node.callee.type === 'MemberExpression' &&
43
- node.callee.object.type === 'Identifier' &&
44
- ['app', 'router', 'express'].includes(node.callee.object.name) &&
45
- node.callee.property.type === 'Identifier' &&
46
- ['get', 'post', 'use'].includes(node.callee.property.name)) {
47
- const pathArg = node.arguments[0];
48
- if (pathArg && pathArg.type === 'Literal' && typeof pathArg.value === 'string') {
49
- const path = pathArg.value.toLowerCase();
50
- if (debugPaths.some(dp => path.includes(dp))) {
51
- report(node);
52
- }
53
- }
54
- }
55
- },
56
- Literal(node) {
57
- // Flag debug path strings in general
58
- if (typeof node.value === 'string') {
59
- const path = node.value.toLowerCase();
60
- if (debugPaths.some(dp => path === dp)) {
61
- report(node);
62
- }
63
- }
64
- },
65
- };
66
- },
67
- });
@@ -1,28 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- /**
7
- * ESLint Rule: no-exposed-sensitive-data
8
- * Detects exposure of PII/sensitive data (SSN, credit card numbers in logs, etc.)
9
- * CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
10
- *
11
- * @see https://cwe.mitre.org/data/definitions/200.html
12
- * @see https://owasp.org/www-community/vulnerabilities/Information_exposure
13
- */
14
- import type { TSESLint } from '@interlace/eslint-devkit';
15
- type MessageIds = 'exposedSensitiveData' | 'sanitizeData';
16
- export interface Options {
17
- /** Allow sensitive data in test files. Default: false */
18
- allowInTests?: boolean;
19
- /** Patterns to detect sensitive data. Default: ['ssn', 'social', 'credit', 'card', 'password', 'secret', 'token', 'apiKey'] */
20
- sensitivePatterns?: string[];
21
- /** Logging/output patterns to detect. Default: ['log', 'console', 'print', 'debug', 'error', 'warn', 'info', 'trace', 'response', 'res.', 'req.'] */
22
- loggingPatterns?: string[];
23
- /** Additional safe patterns to ignore. Default: [] */
24
- ignorePatterns?: string[];
25
- }
26
- type RuleOptions = [Options?];
27
- export declare const noExposedSensitiveData: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener>;
28
- export {};
@@ -1,345 +0,0 @@
1
- "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.noExposedSensitiveData = void 0;
9
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
- /**
12
- * Default sensitive data patterns
13
- */
14
- const DEFAULT_SENSITIVE_PATTERNS = [
15
- 'ssn',
16
- 'social',
17
- 'credit',
18
- 'card',
19
- 'password',
20
- 'secret',
21
- 'token',
22
- 'apiKey',
23
- 'apikey',
24
- 'api_key',
25
- 'privateKey',
26
- 'private_key',
27
- 'accessToken',
28
- 'access_token',
29
- 'refreshToken',
30
- 'refresh_token',
31
- 'authToken',
32
- 'auth_token',
33
- ];
34
- /**
35
- * Default logging/output patterns
36
- */
37
- const DEFAULT_LOGGING_PATTERNS = [
38
- 'log',
39
- 'console',
40
- 'print',
41
- 'debug',
42
- 'error',
43
- 'warn',
44
- 'info',
45
- 'trace',
46
- 'response',
47
- 'res.',
48
- 'req.',
49
- ];
50
- /**
51
- * Sensitive data regex patterns
52
- */
53
- const SENSITIVE_DATA_PATTERNS = {
54
- // SSN pattern (XXX-XX-XXXX)
55
- ssn: /\b\d{3}-\d{2}-\d{4}\b/,
56
- // Credit card pattern (basic - 13-19 digits, may have spaces/dashes)
57
- creditCard: /\b(?:\d{4}[-\s]?){3}\d{1,4}\b/,
58
- // Email with sensitive context
59
- sensitiveEmail: /\b(?:password|secret|token|key|credential)[\w.-]*@[\w.-]+\.\w+\b/i,
60
- };
61
- /**
62
- * Check if a string contains sensitive data patterns
63
- */
64
- function containsSensitiveData(value, sensitivePatterns) {
65
- // Check for SSN pattern
66
- if (SENSITIVE_DATA_PATTERNS.ssn.test(value)) {
67
- return { isSensitive: true, type: 'SSN pattern detected' };
68
- }
69
- // Check for credit card pattern
70
- if (SENSITIVE_DATA_PATTERNS.creditCard.test(value)) {
71
- return { isSensitive: true, type: 'Credit card pattern detected' };
72
- }
73
- // Check for sensitive keywords in variable names or values
74
- const lowerValue = value.toLowerCase();
75
- for (const pattern of sensitivePatterns) {
76
- if (lowerValue.includes(pattern.toLowerCase())) {
77
- // Check if it's in a sensitive context (not just a comment or documentation)
78
- if (/\b(?:log|console|print|debug|error|warn|info|trace|response|res\.|req\.|body|query|params)\b/i.test(value) ||
79
- /\b(?:password|secret|token|key|credential)\s*[:=]/i.test(value)) {
80
- return { isSensitive: true, type: `Sensitive data keyword: ${pattern}` };
81
- }
82
- }
83
- }
84
- return { isSensitive: false, type: '' };
85
- }
86
- /**
87
- * Check if a string matches any ignore pattern
88
- */
89
- function matchesIgnorePattern(text, patterns) {
90
- return patterns.some(pattern => {
91
- try {
92
- const regex = new RegExp(pattern, 'i');
93
- return regex.test(text);
94
- }
95
- catch {
96
- return false;
97
- }
98
- });
99
- }
100
- /**
101
- * Check if a node is in a logging or output context
102
- */
103
- function isInLoggingContext(node, sourceCode, loggingPatterns) {
104
- // Build regex pattern from logging patterns
105
- const patternRegex = new RegExp(`\\b(?:${loggingPatterns.map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})\\b`, 'i');
106
- let current = node;
107
- // Check parent chain for logging calls
108
- while (current && 'parent' in current && current.parent) {
109
- current = current.parent;
110
- if (current.type === 'CallExpression') {
111
- const callExpr = current;
112
- const callText = sourceCode.getText(current);
113
- // Check if it's a logging method call
114
- if (callExpr.callee.type === 'MemberExpression') {
115
- const memberExpr = callExpr.callee;
116
- if (memberExpr.object.type === 'Identifier' && memberExpr.property.type === 'Identifier') {
117
- const objName = memberExpr.object.name.toLowerCase();
118
- const propName = memberExpr.property.name.toLowerCase();
119
- // Check against logging patterns
120
- if (loggingPatterns.some(p => objName.includes(p.toLowerCase()) || propName.includes(p.toLowerCase()))) {
121
- return true;
122
- }
123
- }
124
- }
125
- // Also check for patterns in the call text
126
- if (patternRegex.test(callText)) {
127
- return true;
128
- }
129
- }
130
- // Check if it's in an object expression that's being logged
131
- if (current.type === 'ObjectExpression') {
132
- // Check if parent is a CallExpression with logging
133
- if (current.parent && current.parent.type === 'CallExpression') {
134
- const parentCall = current.parent;
135
- const callText = sourceCode.getText(parentCall);
136
- if (patternRegex.test(callText)) {
137
- return true;
138
- }
139
- }
140
- }
141
- }
142
- return false;
143
- }
144
- exports.noExposedSensitiveData = (0, eslint_devkit_2.createRule)({
145
- name: 'no-exposed-sensitive-data',
146
- meta: {
147
- type: 'problem',
148
- docs: {
149
- description: 'Detects exposure of PII/sensitive data (SSN, credit card numbers in logs, etc.)',
150
- },
151
- hasSuggestions: true,
152
- messages: {
153
- exposedSensitiveData: (0, eslint_devkit_1.formatLLMMessage)({
154
- icon: eslint_devkit_1.MessageIcons.SECURITY,
155
- issueName: 'Exposed Sensitive Data',
156
- cwe: 'CWE-200',
157
- description: 'Sensitive data exposure detected: {{issue}}',
158
- severity: 'CRITICAL',
159
- fix: '{{safeAlternative}}',
160
- documentationLink: 'https://cwe.mitre.org/data/definitions/200.html',
161
- }),
162
- sanitizeData: (0, eslint_devkit_1.formatLLMMessage)({
163
- icon: eslint_devkit_1.MessageIcons.INFO,
164
- issueName: 'Sanitize Data',
165
- description: 'Remove or mask sensitive information',
166
- severity: 'LOW',
167
- fix: 'Mask or remove sensitive data before logging/transmitting',
168
- documentationLink: 'https://cwe.mitre.org/data/definitions/200.html',
169
- }),
170
- },
171
- schema: [
172
- {
173
- type: 'object',
174
- properties: {
175
- allowInTests: {
176
- type: 'boolean',
177
- default: false,
178
- description: 'Allow sensitive data in test files',
179
- },
180
- sensitivePatterns: {
181
- type: 'array',
182
- items: { type: 'string' },
183
- default: [],
184
- description: 'Patterns to detect sensitive data',
185
- },
186
- loggingPatterns: {
187
- type: 'array',
188
- items: { type: 'string' },
189
- default: [],
190
- description: 'Logging/output patterns to detect',
191
- },
192
- ignorePatterns: {
193
- type: 'array',
194
- items: { type: 'string' },
195
- default: [],
196
- description: 'Additional safe patterns to ignore',
197
- },
198
- },
199
- additionalProperties: false,
200
- },
201
- ],
202
- },
203
- defaultOptions: [
204
- {
205
- allowInTests: false,
206
- sensitivePatterns: [],
207
- loggingPatterns: [],
208
- ignorePatterns: [],
209
- },
210
- ],
211
- create(context, [options = {}]) {
212
- const { allowInTests = false, sensitivePatterns, loggingPatterns, ignorePatterns = [], } = options;
213
- const patternsToCheck = sensitivePatterns && sensitivePatterns.length > 0
214
- ? sensitivePatterns
215
- : DEFAULT_SENSITIVE_PATTERNS;
216
- const loggingPatternsToCheck = loggingPatterns && loggingPatterns.length > 0
217
- ? loggingPatterns
218
- : DEFAULT_LOGGING_PATTERNS;
219
- const filename = context.getFilename();
220
- const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
221
- const sourceCode = context.sourceCode || context.sourceCode;
222
- function checkLiteral(node) {
223
- if (isTestFile) {
224
- return;
225
- }
226
- if (typeof node.value !== 'string') {
227
- return;
228
- }
229
- const value = node.value;
230
- const text = sourceCode.getText(node);
231
- // Check if it matches any ignore pattern
232
- if (matchesIgnorePattern(text, ignorePatterns)) {
233
- return;
234
- }
235
- // Only check if in logging/output context
236
- if (!isInLoggingContext(node, sourceCode, loggingPatternsToCheck)) {
237
- return;
238
- }
239
- const { isSensitive, type } = containsSensitiveData(value, patternsToCheck);
240
- if (isSensitive) {
241
- // For SSN and credit card patterns, don't provide auto-fix suggestions
242
- // as they require manual review
243
- const isPatternMatch = type.includes('pattern detected');
244
- context.report({
245
- node,
246
- messageId: 'exposedSensitiveData',
247
- data: {
248
- issue: `${type} in logging/output context`,
249
- safeAlternative: 'Remove or mask sensitive data before logging: logger.info({ userId: user.id }) instead of logger.info({ password: user.password })',
250
- },
251
- ...(isPatternMatch ? {} : {
252
- suggest: [
253
- {
254
- messageId: 'sanitizeData',
255
- fix(fixer) {
256
- // Suggest removing or masking the sensitive data
257
- return fixer.replaceText(node, '"***REDACTED***"');
258
- },
259
- },
260
- ],
261
- }),
262
- });
263
- }
264
- }
265
- function checkIdentifier(node) {
266
- if (isTestFile) {
267
- return;
268
- }
269
- const name = node.name;
270
- const text = sourceCode.getText(node);
271
- // Check if it matches any ignore pattern
272
- if (matchesIgnorePattern(text, ignorePatterns)) {
273
- return;
274
- }
275
- // Only check if in logging/output context
276
- if (!isInLoggingContext(node, sourceCode, loggingPatternsToCheck)) {
277
- return;
278
- }
279
- // Check if identifier name contains sensitive patterns
280
- const lowerName = name.toLowerCase();
281
- for (const pattern of patternsToCheck) {
282
- if (lowerName.includes(pattern.toLowerCase())) {
283
- // Check if it's being accessed in a sensitive way
284
- if (node.parent) {
285
- const parentText = sourceCode.getText(node.parent);
286
- // Build regex pattern from logging patterns
287
- const loggingPatternRegex = new RegExp(`\\b(?:${loggingPatternsToCheck.map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})\\b`, 'i');
288
- if (loggingPatternRegex.test(parentText)) {
289
- // Get the actual property name from context
290
- const actualPropertyName = name;
291
- context.report({
292
- node,
293
- messageId: 'exposedSensitiveData',
294
- data: {
295
- issue: `Sensitive variable "${name}" exposed in logging/output`,
296
- safeAlternative: `Remove or mask sensitive data: logger.info({ userId: user.id }) instead of logger.info({ ${actualPropertyName}: user.${actualPropertyName} })`,
297
- },
298
- // Don't provide auto-fix suggestions for identifiers (too risky)
299
- });
300
- break;
301
- }
302
- }
303
- }
304
- }
305
- }
306
- function checkMemberExpression(node) {
307
- if (isTestFile) {
308
- return;
309
- }
310
- const text = sourceCode.getText(node);
311
- // Check if it matches any ignore pattern
312
- if (matchesIgnorePattern(text, ignorePatterns)) {
313
- return;
314
- }
315
- // Only check if in logging/output context
316
- if (!isInLoggingContext(node, sourceCode, loggingPatternsToCheck)) {
317
- return;
318
- }
319
- // Check if property name contains sensitive patterns
320
- if (node.property.type === 'Identifier') {
321
- const propertyName = node.property.name.toLowerCase();
322
- for (const pattern of patternsToCheck) {
323
- if (propertyName.includes(pattern.toLowerCase())) {
324
- const objectName = node.object.type === 'Identifier' ? node.object.name : 'object';
325
- context.report({
326
- node,
327
- messageId: 'exposedSensitiveData',
328
- data: {
329
- issue: `Sensitive property "${node.property.name}" exposed in logging/output`,
330
- safeAlternative: `Remove or mask sensitive data: logger.info({ userId: user.id }) instead of logger.info({ ${node.property.name}: ${objectName}.${node.property.name} })`,
331
- },
332
- // Don't provide auto-fix suggestions for property access (too risky)
333
- });
334
- break;
335
- }
336
- }
337
- }
338
- }
339
- return {
340
- Literal: checkLiteral,
341
- Identifier: checkIdentifier,
342
- MemberExpression: checkMemberExpression,
343
- };
344
- },
345
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- type MessageIds = 'insecureHttp' | 'insecureHttpWithException';
7
- export interface Options {
8
- /** List of hostnames allowed to use HTTP (e.g., localhost, 127.0.0.1) */
9
- allowedHosts?: string[];
10
- /** List of ports allowed for HTTP (e.g., 3000, 8080 for development) */
11
- allowedPorts?: number[];
12
- }
13
- type RuleOptions = [Options?];
14
- export declare const noHttpUrls: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, RuleOptions, unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
15
- export {};