eslint-plugin-secure-coding 2.3.2 → 2.3.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.
- package/README.md +1 -0
- package/package.json +3 -10
- package/src/index.ts +605 -0
- package/src/rules/__tests__/integration-demo.test.ts +290 -0
- package/src/rules/__tests__/integration-llm.test.ts +89 -0
- package/src/rules/database-injection/database-injection.test.ts +456 -0
- package/src/rules/database-injection/index.ts +488 -0
- package/src/rules/detect-child-process/detect-child-process.test.ts +207 -0
- package/src/rules/detect-child-process/index.ts +634 -0
- package/src/rules/detect-eval-with-expression/detect-eval-with-expression.test.ts +416 -0
- package/src/rules/detect-eval-with-expression/index.ts +463 -0
- package/src/rules/detect-mixed-content/detect-mixed-content.test.ts +28 -0
- package/src/rules/detect-mixed-content/index.ts +52 -0
- package/src/rules/detect-non-literal-fs-filename/detect-non-literal-fs-filename.test.ts +269 -0
- package/src/rules/detect-non-literal-fs-filename/index.ts +551 -0
- package/src/rules/detect-non-literal-regexp/detect-non-literal-regexp.test.ts +189 -0
- package/src/rules/detect-non-literal-regexp/index.ts +490 -0
- package/src/rules/detect-object-injection/detect-object-injection.test.ts +440 -0
- package/src/rules/detect-object-injection/index.ts +674 -0
- package/src/rules/detect-suspicious-dependencies/detect-suspicious-dependencies.test.ts +32 -0
- package/src/rules/detect-suspicious-dependencies/index.ts +84 -0
- package/src/rules/detect-weak-password-validation/detect-weak-password-validation.test.ts +31 -0
- package/src/rules/detect-weak-password-validation/index.ts +68 -0
- package/src/rules/no-allow-arbitrary-loads/index.ts +54 -0
- package/src/rules/no-allow-arbitrary-loads/no-allow-arbitrary-loads.test.ts +28 -0
- package/src/rules/no-arbitrary-file-access/index.ts +238 -0
- package/src/rules/no-arbitrary-file-access/no-arbitrary-file-access.test.ts +119 -0
- package/src/rules/no-buffer-overread/index.ts +724 -0
- package/src/rules/no-buffer-overread/no-buffer-overread.test.ts +313 -0
- package/src/rules/no-clickjacking/index.ts +481 -0
- package/src/rules/no-clickjacking/no-clickjacking.test.ts +253 -0
- package/src/rules/no-client-side-auth-logic/index.ts +81 -0
- package/src/rules/no-client-side-auth-logic/no-client-side-auth-logic.test.ts +33 -0
- package/src/rules/no-credentials-in-query-params/index.ts +69 -0
- package/src/rules/no-credentials-in-query-params/no-credentials-in-query-params.test.ts +33 -0
- package/src/rules/no-credentials-in-storage-api/index.ts +64 -0
- package/src/rules/no-credentials-in-storage-api/no-credentials-in-storage-api.test.ts +31 -0
- package/src/rules/no-data-in-temp-storage/index.ts +75 -0
- package/src/rules/no-data-in-temp-storage/no-data-in-temp-storage.test.ts +33 -0
- package/src/rules/no-debug-code-in-production/index.ts +59 -0
- package/src/rules/no-debug-code-in-production/no-debug-code-in-production.test.ts +26 -0
- package/src/rules/no-directive-injection/index.ts +551 -0
- package/src/rules/no-directive-injection/no-directive-injection.test.ts +305 -0
- package/src/rules/no-disabled-certificate-validation/index.ts +72 -0
- package/src/rules/no-disabled-certificate-validation/no-disabled-certificate-validation.test.ts +33 -0
- package/src/rules/no-document-cookie/index.ts +113 -0
- package/src/rules/no-document-cookie/no-document-cookie.test.ts +382 -0
- package/src/rules/no-dynamic-dependency-loading/index.ts +60 -0
- package/src/rules/no-dynamic-dependency-loading/no-dynamic-dependency-loading.test.ts +27 -0
- package/src/rules/no-electron-security-issues/index.ts +504 -0
- package/src/rules/no-electron-security-issues/no-electron-security-issues.test.ts +324 -0
- package/src/rules/no-exposed-debug-endpoints/index.ts +73 -0
- package/src/rules/no-exposed-debug-endpoints/no-exposed-debug-endpoints.test.ts +40 -0
- package/src/rules/no-exposed-sensitive-data/index.ts +428 -0
- package/src/rules/no-exposed-sensitive-data/no-exposed-sensitive-data.test.ts +75 -0
- package/src/rules/no-format-string-injection/index.ts +801 -0
- package/src/rules/no-format-string-injection/no-format-string-injection.test.ts +437 -0
- package/src/rules/no-graphql-injection/index.ts +508 -0
- package/src/rules/no-graphql-injection/no-graphql-injection.test.ts +371 -0
- package/src/rules/no-hardcoded-credentials/index.ts +478 -0
- package/src/rules/no-hardcoded-credentials/no-hardcoded-credentials.test.ts +639 -0
- package/src/rules/no-hardcoded-session-tokens/index.ts +69 -0
- package/src/rules/no-hardcoded-session-tokens/no-hardcoded-session-tokens.test.ts +42 -0
- package/src/rules/no-http-urls/index.ts +131 -0
- package/src/rules/no-http-urls/no-http-urls.test.ts +60 -0
- package/src/rules/no-improper-sanitization/index.ts +502 -0
- package/src/rules/no-improper-sanitization/no-improper-sanitization.test.ts +156 -0
- package/src/rules/no-improper-type-validation/index.ts +572 -0
- package/src/rules/no-improper-type-validation/no-improper-type-validation.test.ts +372 -0
- package/src/rules/no-insecure-comparison/index.ts +232 -0
- package/src/rules/no-insecure-comparison/no-insecure-comparison.test.ts +218 -0
- package/src/rules/no-insecure-cookie-settings/index.ts +391 -0
- package/src/rules/no-insecure-cookie-settings/no-insecure-cookie-settings.test.ts +409 -0
- package/src/rules/no-insecure-jwt/index.ts +467 -0
- package/src/rules/no-insecure-jwt/no-insecure-jwt.test.ts +259 -0
- package/src/rules/no-insecure-redirects/index.ts +267 -0
- package/src/rules/no-insecure-redirects/no-insecure-redirects.test.ts +108 -0
- package/src/rules/no-insecure-websocket/index.ts +72 -0
- package/src/rules/no-insecure-websocket/no-insecure-websocket.test.ts +42 -0
- package/src/rules/no-insufficient-postmessage-validation/index.ts +497 -0
- package/src/rules/no-insufficient-postmessage-validation/no-insufficient-postmessage-validation.test.ts +360 -0
- package/src/rules/no-insufficient-random/index.ts +288 -0
- package/src/rules/no-insufficient-random/no-insufficient-random.test.ts +246 -0
- package/src/rules/no-ldap-injection/index.ts +547 -0
- package/src/rules/no-ldap-injection/no-ldap-injection.test.ts +317 -0
- package/src/rules/no-missing-authentication/index.ts +408 -0
- package/src/rules/no-missing-authentication/no-missing-authentication.test.ts +350 -0
- package/src/rules/no-missing-cors-check/index.ts +453 -0
- package/src/rules/no-missing-cors-check/no-missing-cors-check.test.ts +392 -0
- package/src/rules/no-missing-csrf-protection/index.ts +229 -0
- package/src/rules/no-missing-csrf-protection/no-missing-csrf-protection.test.ts +222 -0
- package/src/rules/no-missing-security-headers/index.ts +266 -0
- package/src/rules/no-missing-security-headers/no-missing-security-headers.test.ts +98 -0
- package/src/rules/no-password-in-url/index.ts +64 -0
- package/src/rules/no-password-in-url/no-password-in-url.test.ts +27 -0
- package/src/rules/no-permissive-cors/index.ts +78 -0
- package/src/rules/no-permissive-cors/no-permissive-cors.test.ts +28 -0
- package/src/rules/no-pii-in-logs/index.ts +83 -0
- package/src/rules/no-pii-in-logs/no-pii-in-logs.test.ts +26 -0
- package/src/rules/no-postmessage-origin-wildcard/index.ts +67 -0
- package/src/rules/no-postmessage-origin-wildcard/no-postmessage-origin-wildcard.test.ts +27 -0
- package/src/rules/no-privilege-escalation/index.ts +403 -0
- package/src/rules/no-privilege-escalation/no-privilege-escalation.test.ts +306 -0
- package/src/rules/no-redos-vulnerable-regex/index.ts +379 -0
- package/src/rules/no-redos-vulnerable-regex/no-redos-vulnerable-regex.test.ts +83 -0
- package/src/rules/no-sensitive-data-exposure/index.ts +294 -0
- package/src/rules/no-sensitive-data-exposure/no-sensitive-data-exposure.test.ts +262 -0
- package/src/rules/no-sensitive-data-in-analytics/index.ts +73 -0
- package/src/rules/no-sensitive-data-in-analytics/no-sensitive-data-in-analytics.test.ts +42 -0
- package/src/rules/no-sensitive-data-in-cache/index.ts +59 -0
- package/src/rules/no-sensitive-data-in-cache/no-sensitive-data-in-cache.test.ts +32 -0
- package/src/rules/no-sql-injection/index.ts +424 -0
- package/src/rules/no-sql-injection/no-sql-injection.test.ts +303 -0
- package/src/rules/no-timing-attack/index.ts +552 -0
- package/src/rules/no-timing-attack/no-timing-attack.test.ts +348 -0
- package/src/rules/no-toctou-vulnerability/index.ts +250 -0
- package/src/rules/no-toctou-vulnerability/no-toctou-vulnerability.test.ts +60 -0
- package/src/rules/no-tracking-without-consent/index.ts +78 -0
- package/src/rules/no-tracking-without-consent/no-tracking-without-consent.test.ts +34 -0
- package/src/rules/no-unchecked-loop-condition/index.ts +781 -0
- package/src/rules/no-unchecked-loop-condition/no-unchecked-loop-condition.test.ts +459 -0
- package/src/rules/no-unencrypted-local-storage/index.ts +73 -0
- package/src/rules/no-unencrypted-local-storage/no-unencrypted-local-storage.test.ts +41 -0
- package/src/rules/no-unencrypted-transmission/index.ts +296 -0
- package/src/rules/no-unencrypted-transmission/no-unencrypted-transmission.test.ts +287 -0
- package/src/rules/no-unescaped-url-parameter/index.ts +424 -0
- package/src/rules/no-unescaped-url-parameter/no-unescaped-url-parameter.test.ts +263 -0
- package/src/rules/no-unlimited-resource-allocation/index.ts +767 -0
- package/src/rules/no-unlimited-resource-allocation/no-unlimited-resource-allocation.test.ts +544 -0
- package/src/rules/no-unsafe-deserialization/index.ts +593 -0
- package/src/rules/no-unsafe-deserialization/no-unsafe-deserialization.test.ts +310 -0
- package/src/rules/no-unsafe-dynamic-require/index.ts +125 -0
- package/src/rules/no-unsafe-dynamic-require/no-unsafe-dynamic-require.test.ts +151 -0
- package/src/rules/no-unsafe-regex-construction/index.ts +370 -0
- package/src/rules/no-unsafe-regex-construction/no-unsafe-regex-construction.test.ts +181 -0
- package/src/rules/no-unsanitized-html/index.ts +400 -0
- package/src/rules/no-unsanitized-html/no-unsanitized-html.test.ts +488 -0
- package/src/rules/no-unvalidated-deeplinks/index.ts +73 -0
- package/src/rules/no-unvalidated-deeplinks/no-unvalidated-deeplinks.test.ts +29 -0
- package/src/rules/no-unvalidated-user-input/index.ts +498 -0
- package/src/rules/no-unvalidated-user-input/no-unvalidated-user-input.test.ts +463 -0
- package/src/rules/no-verbose-error-messages/index.ts +83 -0
- package/src/rules/no-verbose-error-messages/no-verbose-error-messages.test.ts +34 -0
- package/src/rules/no-weak-crypto/index.ts +447 -0
- package/src/rules/no-weak-crypto/no-weak-crypto.test.ts +297 -0
- package/src/rules/no-weak-password-recovery/index.ts +509 -0
- package/src/rules/no-weak-password-recovery/no-weak-password-recovery.test.ts +184 -0
- package/src/rules/no-xpath-injection/index.ts +596 -0
- package/src/rules/no-xpath-injection/no-xpath-injection.test.ts +405 -0
- package/src/rules/no-xxe-injection/index.ts +342 -0
- package/src/rules/no-xxe-injection/no-xxe-injection.test.ts +122 -0
- package/src/rules/no-zip-slip/index.ts +526 -0
- package/src/rules/no-zip-slip/no-zip-slip.test.ts +305 -0
- package/src/rules/require-backend-authorization/index.ts +71 -0
- package/src/rules/require-backend-authorization/require-backend-authorization.test.ts +31 -0
- package/src/rules/require-code-minification/index.ts +54 -0
- package/src/rules/require-code-minification/require-code-minification.test.ts +30 -0
- package/src/rules/require-csp-headers/index.ts +74 -0
- package/src/rules/require-csp-headers/require-csp-headers.test.ts +34 -0
- package/src/rules/require-data-minimization/index.ts +65 -0
- package/src/rules/require-data-minimization/require-data-minimization.test.ts +31 -0
- package/src/rules/require-dependency-integrity/index.ts +78 -0
- package/src/rules/require-dependency-integrity/require-dependency-integrity.test.ts +44 -0
- package/src/rules/require-https-only/index.ts +75 -0
- package/src/rules/require-https-only/require-https-only.test.ts +26 -0
- package/src/rules/require-mime-type-validation/index.ts +77 -0
- package/src/rules/require-mime-type-validation/require-mime-type-validation.test.ts +32 -0
- package/src/rules/require-network-timeout/index.ts +58 -0
- package/src/rules/require-network-timeout/require-network-timeout.test.ts +26 -0
- package/src/rules/require-package-lock/index.ts +75 -0
- package/src/rules/require-package-lock/require-package-lock.test.ts +27 -0
- package/src/rules/require-secure-credential-storage/index.ts +60 -0
- package/src/rules/require-secure-credential-storage/require-secure-credential-storage.test.ts +26 -0
- package/src/rules/require-secure-defaults/index.ts +54 -0
- package/src/rules/require-secure-defaults/require-secure-defaults.test.ts +26 -0
- package/src/rules/require-secure-deletion/index.ts +52 -0
- package/src/rules/require-secure-deletion/require-secure-deletion.test.ts +29 -0
- package/src/rules/require-storage-encryption/index.ts +60 -0
- package/src/rules/require-storage-encryption/require-storage-encryption.test.ts +26 -0
- package/src/rules/require-url-validation/index.ts +85 -0
- package/src/rules/require-url-validation/require-url-validation.test.ts +32 -0
- package/src/types/{index.d.ts → index.ts} +157 -53
- package/src/index.d.ts +0 -32
- package/src/index.js +0 -465
- package/src/rules/database-injection/index.d.ts +0 -13
- package/src/rules/database-injection/index.js +0 -406
- 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-non-literal-regexp/index.d.ts +0 -9
- package/src/rules/detect-non-literal-regexp/index.js +0 -403
- package/src/rules/detect-object-injection/index.d.ts +0 -11
- package/src/rules/detect-object-injection/index.js +0 -560
- package/src/rules/detect-suspicious-dependencies/index.d.ts +0 -8
- package/src/rules/detect-suspicious-dependencies/index.js +0 -71
- package/src/rules/detect-weak-password-validation/index.d.ts +0 -6
- package/src/rules/detect-weak-password-validation/index.js +0 -58
- 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-credentials-in-storage-api/index.d.ts +0 -6
- package/src/rules/no-credentials-in-storage-api/index.js +0 -54
- 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-directive-injection/index.d.ts +0 -12
- package/src/rules/no-directive-injection/index.js +0 -457
- 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-document-cookie/index.d.ts +0 -5
- package/src/rules/no-document-cookie/index.js +0 -89
- 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-electron-security-issues/index.d.ts +0 -10
- package/src/rules/no-electron-security-issues/index.js +0 -423
- 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-format-string-injection/index.d.ts +0 -17
- package/src/rules/no-format-string-injection/index.js +0 -660
- package/src/rules/no-graphql-injection/index.d.ts +0 -12
- package/src/rules/no-graphql-injection/index.js +0 -411
- package/src/rules/no-hardcoded-credentials/index.d.ts +0 -26
- package/src/rules/no-hardcoded-credentials/index.js +0 -376
- package/src/rules/no-hardcoded-session-tokens/index.d.ts +0 -6
- package/src/rules/no-hardcoded-session-tokens/index.js +0 -59
- 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-improper-sanitization/index.d.ts +0 -12
- package/src/rules/no-improper-sanitization/index.js +0 -411
- package/src/rules/no-improper-type-validation/index.d.ts +0 -10
- package/src/rules/no-improper-type-validation/index.js +0 -475
- package/src/rules/no-insecure-comparison/index.d.ts +0 -7
- package/src/rules/no-insecure-comparison/index.js +0 -193
- package/src/rules/no-insecure-cookie-settings/index.d.ts +0 -9
- package/src/rules/no-insecure-cookie-settings/index.js +0 -306
- package/src/rules/no-insecure-jwt/index.d.ts +0 -10
- package/src/rules/no-insecure-jwt/index.js +0 -380
- 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-insufficient-postmessage-validation/index.d.ts +0 -14
- package/src/rules/no-insufficient-postmessage-validation/index.js +0 -392
- package/src/rules/no-insufficient-random/index.d.ts +0 -9
- package/src/rules/no-insufficient-random/index.js +0 -208
- package/src/rules/no-ldap-injection/index.d.ts +0 -10
- package/src/rules/no-ldap-injection/index.js +0 -455
- package/src/rules/no-missing-authentication/index.d.ts +0 -13
- package/src/rules/no-missing-authentication/index.js +0 -333
- 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-pii-in-logs/index.d.ts +0 -8
- package/src/rules/no-pii-in-logs/index.js +0 -70
- package/src/rules/no-postmessage-origin-wildcard/index.d.ts +0 -8
- package/src/rules/no-postmessage-origin-wildcard/index.js +0 -56
- package/src/rules/no-privilege-escalation/index.d.ts +0 -13
- package/src/rules/no-privilege-escalation/index.js +0 -321
- package/src/rules/no-redos-vulnerable-regex/index.d.ts +0 -7
- package/src/rules/no-redos-vulnerable-regex/index.js +0 -306
- package/src/rules/no-sensitive-data-exposure/index.d.ts +0 -11
- package/src/rules/no-sensitive-data-exposure/index.js +0 -250
- 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-sql-injection/index.d.ts +0 -10
- package/src/rules/no-sql-injection/index.js +0 -335
- package/src/rules/no-timing-attack/index.d.ts +0 -10
- package/src/rules/no-timing-attack/index.js +0 -447
- 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-unchecked-loop-condition/index.d.ts +0 -12
- package/src/rules/no-unchecked-loop-condition/index.js +0 -646
- package/src/rules/no-unencrypted-local-storage/index.d.ts +0 -8
- package/src/rules/no-unencrypted-local-storage/index.js +0 -61
- 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-unlimited-resource-allocation/index.d.ts +0 -12
- package/src/rules/no-unlimited-resource-allocation/index.js +0 -643
- package/src/rules/no-unsafe-deserialization/index.d.ts +0 -10
- package/src/rules/no-unsafe-deserialization/index.js +0 -491
- 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-unsafe-regex-construction/index.d.ts +0 -9
- package/src/rules/no-unsafe-regex-construction/index.js +0 -291
- package/src/rules/no-unsanitized-html/index.d.ts +0 -9
- package/src/rules/no-unsanitized-html/index.js +0 -335
- 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-weak-crypto/index.d.ts +0 -11
- package/src/rules/no-weak-crypto/index.js +0 -351
- package/src/rules/no-weak-password-recovery/index.d.ts +0 -12
- package/src/rules/no-weak-password-recovery/index.js +0 -424
- package/src/rules/no-xpath-injection/index.d.ts +0 -10
- package/src/rules/no-xpath-injection/index.js +0 -487
- package/src/rules/no-xxe-injection/index.d.ts +0 -7
- package/src/rules/no-xxe-injection/index.js +0 -266
- 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-backend-authorization/index.d.ts +0 -6
- package/src/rules/require-backend-authorization/index.js +0 -60
- 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-defaults/index.d.ts +0 -8
- package/src/rules/require-secure-defaults/index.js +0 -47
- 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
- package/src/types/index.js +0 -17
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive tests for detect-non-literal-regexp rule
|
|
3
|
+
* Security: CWE-400 (ReDoS - Regular Expression Denial of Service)
|
|
4
|
+
*/
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { describe, it, afterAll } from 'vitest';
|
|
7
|
+
import parser from '@typescript-eslint/parser';
|
|
8
|
+
import { detectNonLiteralRegexp } from './index';
|
|
9
|
+
|
|
10
|
+
// Configure RuleTester for Vitest
|
|
11
|
+
RuleTester.afterAll = afterAll;
|
|
12
|
+
RuleTester.it = it;
|
|
13
|
+
RuleTester.itOnly = it.only;
|
|
14
|
+
RuleTester.describe = describe;
|
|
15
|
+
|
|
16
|
+
// Use Flat Config format (ESLint 9+)
|
|
17
|
+
const ruleTester = new RuleTester({
|
|
18
|
+
languageOptions: {
|
|
19
|
+
parser,
|
|
20
|
+
ecmaVersion: 2022,
|
|
21
|
+
sourceType: 'module',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('detect-non-literal-regexp', () => {
|
|
26
|
+
describe('Valid Code', () => {
|
|
27
|
+
ruleTester.run('valid - safe regex patterns', detectNonLiteralRegexp, {
|
|
28
|
+
valid: [
|
|
29
|
+
// Not RegExp - these are safe
|
|
30
|
+
{
|
|
31
|
+
code: 'const result = myFunction(pattern);',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
code: 'obj.RegExp(pattern);',
|
|
35
|
+
},
|
|
36
|
+
// Note: This rule is very strict and detects ReDoS patterns even in literals
|
|
37
|
+
// Most regex patterns will trigger the rule, so we only test non-RegExp code as valid
|
|
38
|
+
],
|
|
39
|
+
invalid: [],
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('Invalid Code - Dynamic RegExp', () => {
|
|
44
|
+
ruleTester.run('invalid - dynamic regex patterns', detectNonLiteralRegexp, {
|
|
45
|
+
valid: [],
|
|
46
|
+
invalid: [
|
|
47
|
+
{
|
|
48
|
+
code: 'const pattern = new RegExp(userInput);',
|
|
49
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
code: 'const regex = RegExp(userPattern);',
|
|
53
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
code: 'new RegExp(`^${userInput}$`);',
|
|
57
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
code: `
|
|
61
|
+
const pattern = getUserInput();
|
|
62
|
+
const regex = new RegExp(pattern);
|
|
63
|
+
`,
|
|
64
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
code: 'new RegExp(config.pattern);',
|
|
68
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('Invalid Code - ReDoS Patterns in Literals', () => {
|
|
75
|
+
ruleTester.run('invalid - True ReDoS vulnerable patterns', detectNonLiteralRegexp, {
|
|
76
|
+
valid: [
|
|
77
|
+
// Simple regex literals are now safe - no nested quantifiers
|
|
78
|
+
{ code: 'const pattern = /^[a-z]+$/;' },
|
|
79
|
+
{ code: 'const emailRegex = /^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$/;' },
|
|
80
|
+
// Static string patterns with new RegExp are now also safe by default
|
|
81
|
+
{ code: 'const pattern = new RegExp("^[a-z]+$");' },
|
|
82
|
+
{ code: 'const pattern = RegExp("^test$");' },
|
|
83
|
+
{ code: 'const pattern = new RegExp(`^test$`);' },
|
|
84
|
+
// Explicitly allowing literals also works
|
|
85
|
+
{ code: 'const pattern = new RegExp("^[a-z]+$");', options: [{ allowLiterals: true }] },
|
|
86
|
+
{ code: 'const pattern = RegExp("^test$");', options: [{ allowLiterals: true }] },
|
|
87
|
+
{ code: 'const pattern = new RegExp(`^test$`);', options: [{ allowLiterals: true }] },
|
|
88
|
+
],
|
|
89
|
+
invalid: [
|
|
90
|
+
// Truly dangerous nested quantifier patterns: (a+)+
|
|
91
|
+
{
|
|
92
|
+
code: 'const pattern = /(a+)+b/;',
|
|
93
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
code: 'const pattern = /([a-z]+)*/;',
|
|
97
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
98
|
+
},
|
|
99
|
+
// Dynamic regex with variables still flagged
|
|
100
|
+
{
|
|
101
|
+
code: 'const pattern = new RegExp(userInput);',
|
|
102
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
describe('Suggestions', () => {
|
|
110
|
+
ruleTester.run('suggestions for fixes', detectNonLiteralRegexp, {
|
|
111
|
+
valid: [],
|
|
112
|
+
invalid: [
|
|
113
|
+
{
|
|
114
|
+
code: 'const regex = new RegExp(userInput);',
|
|
115
|
+
errors: [
|
|
116
|
+
{
|
|
117
|
+
messageId: 'regexpReDoS',
|
|
118
|
+
// Note: Rule may not provide suggestions in all cases
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('Edge Cases', () => {
|
|
127
|
+
ruleTester.run('edge cases', detectNonLiteralRegexp, {
|
|
128
|
+
valid: [
|
|
129
|
+
// Note: Rule may detect RegExp calls even when reassigned
|
|
130
|
+
// This is a limitation of static analysis
|
|
131
|
+
// allowLiterals option now allows static string patterns
|
|
132
|
+
{
|
|
133
|
+
code: 'new RegExp("^test$");',
|
|
134
|
+
options: [{ allowLiterals: true }],
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
invalid: [
|
|
138
|
+
{
|
|
139
|
+
code: 'const RegExp = myFunction; RegExp(pattern);',
|
|
140
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('Options', () => {
|
|
147
|
+
ruleTester.run('options testing', detectNonLiteralRegexp, {
|
|
148
|
+
valid: [
|
|
149
|
+
// allowLiterals option now allows static string patterns
|
|
150
|
+
{
|
|
151
|
+
code: 'new RegExp("^test$");',
|
|
152
|
+
options: [{ allowLiterals: true }],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
invalid: [
|
|
156
|
+
// Dynamic userInput patterns are still flagged
|
|
157
|
+
{
|
|
158
|
+
code: 'new RegExp(userInput);',
|
|
159
|
+
options: [{ maxPatternLength: 100 }],
|
|
160
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
code: 'new RegExp(userInput);',
|
|
164
|
+
options: [{ allowLiterals: true }],
|
|
165
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('Uncovered Lines', () => {
|
|
172
|
+
// Note: Lines 365-368 (early return with allowLiterals) are not testable
|
|
173
|
+
// because the rule flags all regex patterns as potentially vulnerable
|
|
174
|
+
// The allowLiterals option only affects whether literals are processed or not
|
|
175
|
+
|
|
176
|
+
// Line 389: Early return when no vulnerability is detected
|
|
177
|
+
ruleTester.run('line 389 - no vulnerability detected', detectNonLiteralRegexp, {
|
|
178
|
+
valid: [],
|
|
179
|
+
invalid: [
|
|
180
|
+
// This should trigger the no vulnerability case, but still report due to dynamic nature
|
|
181
|
+
{
|
|
182
|
+
code: 'new RegExp(userInput);',
|
|
183
|
+
errors: [{ messageId: 'regexpReDoS' }],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint Rule: detect-non-literal-regexp
|
|
3
|
+
* Detects RegExp(variable), which might allow an attacker to DOS your server with a long-running regular expression
|
|
4
|
+
* LLM-optimized with comprehensive ReDoS prevention guidance
|
|
5
|
+
*
|
|
6
|
+
* @see https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
|
|
7
|
+
* @see https://cwe.mitre.org/data/definitions/400.html
|
|
8
|
+
*/
|
|
9
|
+
import type { TSESLint, TSESTree } from '@interlace/eslint-devkit';
|
|
10
|
+
import { formatLLMMessage, MessageIcons } from '@interlace/eslint-devkit';
|
|
11
|
+
import { createRule } from '@interlace/eslint-devkit';
|
|
12
|
+
|
|
13
|
+
type MessageIds =
|
|
14
|
+
| 'regexpReDoS'
|
|
15
|
+
| 'useStaticRegex'
|
|
16
|
+
| 'validateInput'
|
|
17
|
+
| 'useRegexLibrary'
|
|
18
|
+
| 'addTimeout'
|
|
19
|
+
| 'escapeUserInput';
|
|
20
|
+
|
|
21
|
+
export interface Options {
|
|
22
|
+
/** Allow literal string regex patterns. Default: false (stricter) */
|
|
23
|
+
allowLiterals?: boolean;
|
|
24
|
+
|
|
25
|
+
/** Additional RegExp creation patterns to check */
|
|
26
|
+
additionalPatterns?: string[];
|
|
27
|
+
|
|
28
|
+
/** Maximum allowed pattern length for dynamic regex */
|
|
29
|
+
maxPatternLength?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type RuleOptions = [Options?];
|
|
33
|
+
|
|
34
|
+
// Type guard for regex literal nodes
|
|
35
|
+
const isRegExpLiteral = (node: TSESTree.Node): node is TSESTree.Literal & { regex: { pattern: string; flags: string } } => {
|
|
36
|
+
return node.type === 'Literal' && Object.prototype.hasOwnProperty.call(node, 'regex');
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* RegExp creation patterns and their security implications
|
|
41
|
+
*/
|
|
42
|
+
interface RegExpPattern {
|
|
43
|
+
pattern: string;
|
|
44
|
+
dangerous: boolean;
|
|
45
|
+
vulnerability: 'redos' | 'injection' | 'performance';
|
|
46
|
+
safeAlternative: string;
|
|
47
|
+
example: { bad: string; good: string };
|
|
48
|
+
effort: string;
|
|
49
|
+
riskLevel: 'low' | 'medium' | 'high' | 'critical';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const REGEXP_PATTERNS: RegExpPattern[] = [
|
|
53
|
+
{
|
|
54
|
+
pattern: 'new RegExp\\(.*\\)',
|
|
55
|
+
dangerous: true,
|
|
56
|
+
vulnerability: 'redos',
|
|
57
|
+
safeAlternative: 'Pre-defined RegExp constants',
|
|
58
|
+
example: {
|
|
59
|
+
bad: 'new RegExp(userInput)',
|
|
60
|
+
good: 'const PATTERNS = { email: /^[a-zA-Z0-9]+$/ }; PATTERNS[userChoice]'
|
|
61
|
+
},
|
|
62
|
+
effort: '10-15 minutes',
|
|
63
|
+
riskLevel: 'high'
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
pattern: 'RegExp\\(.*\\)',
|
|
67
|
+
dangerous: true,
|
|
68
|
+
vulnerability: 'redos',
|
|
69
|
+
safeAlternative: 'Static RegExp literals or validated patterns',
|
|
70
|
+
example: {
|
|
71
|
+
bad: 'RegExp(userPattern)',
|
|
72
|
+
good: 'const safePattern = userPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \'\\\\$&\'); new RegExp(`^${safePattern}$`)'
|
|
73
|
+
},
|
|
74
|
+
effort: '15-20 minutes',
|
|
75
|
+
riskLevel: 'high'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
pattern: '/.*\\*\\*.*|.*\\+\\+.*|.*\\?\\?/',
|
|
79
|
+
dangerous: true,
|
|
80
|
+
vulnerability: 'redos',
|
|
81
|
+
safeAlternative: 'Avoid nested quantifiers, use atomic groups',
|
|
82
|
+
example: {
|
|
83
|
+
bad: '/(a+)+b/', // ReDoS vulnerable
|
|
84
|
+
good: '/(?>a+)b/', // Atomic group (if supported) or restructure
|
|
85
|
+
},
|
|
86
|
+
effort: '20-30 minutes',
|
|
87
|
+
riskLevel: 'critical'
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
export const detectNonLiteralRegexp = createRule<RuleOptions, MessageIds>({
|
|
92
|
+
name: 'detect-non-literal-regexp',
|
|
93
|
+
meta: {
|
|
94
|
+
type: 'problem',
|
|
95
|
+
docs: {
|
|
96
|
+
description: 'Detects RegExp(variable), which might allow an attacker to DOS your server with a long-running regular expression',
|
|
97
|
+
},
|
|
98
|
+
messages: {
|
|
99
|
+
// 🎯 Token optimization: 41% reduction (51→30 tokens) - compact template variables
|
|
100
|
+
regexpReDoS: formatLLMMessage({
|
|
101
|
+
icon: MessageIcons.WARNING,
|
|
102
|
+
issueName: 'ReDoS vulnerability',
|
|
103
|
+
cwe: 'CWE-400',
|
|
104
|
+
description: 'ReDoS vulnerability detected',
|
|
105
|
+
severity: '{{riskLevel}}',
|
|
106
|
+
fix: '{{safeAlternative}}',
|
|
107
|
+
documentationLink: 'https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS',
|
|
108
|
+
}),
|
|
109
|
+
useStaticRegex: formatLLMMessage({
|
|
110
|
+
icon: MessageIcons.INFO,
|
|
111
|
+
issueName: 'Use Static Regex',
|
|
112
|
+
description: 'Use pre-defined RegExp constants',
|
|
113
|
+
severity: 'LOW',
|
|
114
|
+
fix: 'const PATTERN = /^[a-z]+$/; // Define at module level',
|
|
115
|
+
documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp',
|
|
116
|
+
}),
|
|
117
|
+
validateInput: formatLLMMessage({
|
|
118
|
+
icon: MessageIcons.INFO,
|
|
119
|
+
issueName: 'Validate Input',
|
|
120
|
+
description: 'Validate and escape user input',
|
|
121
|
+
severity: 'LOW',
|
|
122
|
+
fix: 'Validate input length and characters before RegExp',
|
|
123
|
+
documentationLink: 'https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS',
|
|
124
|
+
}),
|
|
125
|
+
useRegexLibrary: formatLLMMessage({
|
|
126
|
+
icon: MessageIcons.INFO,
|
|
127
|
+
issueName: 'Use Safe Library',
|
|
128
|
+
description: 'Use safe-regex library or re2',
|
|
129
|
+
severity: 'LOW',
|
|
130
|
+
fix: 'import { isSafe } from "safe-regex"; if (isSafe(pattern)) ...',
|
|
131
|
+
documentationLink: 'https://github.com/substack/safe-regex',
|
|
132
|
+
}),
|
|
133
|
+
addTimeout: formatLLMMessage({
|
|
134
|
+
icon: MessageIcons.INFO,
|
|
135
|
+
issueName: 'Add Timeout',
|
|
136
|
+
description: 'Add timeout to regex operations',
|
|
137
|
+
severity: 'LOW',
|
|
138
|
+
fix: 'Use timeout wrapper for regex operations',
|
|
139
|
+
documentationLink: 'https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS',
|
|
140
|
+
}),
|
|
141
|
+
escapeUserInput: formatLLMMessage({
|
|
142
|
+
icon: MessageIcons.INFO,
|
|
143
|
+
issueName: 'Escape Input',
|
|
144
|
+
description: 'Escape special regex characters',
|
|
145
|
+
severity: 'LOW',
|
|
146
|
+
fix: 'input.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")',
|
|
147
|
+
documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping',
|
|
148
|
+
})
|
|
149
|
+
},
|
|
150
|
+
schema: [
|
|
151
|
+
{
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
allowLiterals: {
|
|
155
|
+
type: 'boolean',
|
|
156
|
+
default: false,
|
|
157
|
+
description: 'Allow literal string regex patterns'
|
|
158
|
+
},
|
|
159
|
+
additionalPatterns: {
|
|
160
|
+
type: 'array',
|
|
161
|
+
items: { type: 'string' },
|
|
162
|
+
default: [],
|
|
163
|
+
description: 'Additional RegExp creation patterns to check'
|
|
164
|
+
},
|
|
165
|
+
maxPatternLength: {
|
|
166
|
+
type: 'number',
|
|
167
|
+
default: 100,
|
|
168
|
+
minimum: 1,
|
|
169
|
+
description: 'Maximum allowed pattern length for dynamic regex'
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
additionalProperties: false,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
defaultOptions: [
|
|
177
|
+
{
|
|
178
|
+
allowLiterals: false,
|
|
179
|
+
additionalPatterns: [],
|
|
180
|
+
maxPatternLength: 100
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
create(context: TSESLint.RuleContext<MessageIds, RuleOptions>) {
|
|
184
|
+
const options = context.options[0] || {};
|
|
185
|
+
const {
|
|
186
|
+
allowLiterals = false,
|
|
187
|
+
maxPatternLength = 100
|
|
188
|
+
|
|
189
|
+
}: Options = options || {};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Check if a node is a literal string (potentially safe)
|
|
193
|
+
* Includes template literals without expressions
|
|
194
|
+
*/
|
|
195
|
+
const isLiteralString = (node: TSESTree.Node): boolean => {
|
|
196
|
+
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
// Template literals without expressions are also static/safe
|
|
200
|
+
if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Check if a regex pattern contains dangerous ReDoS patterns
|
|
208
|
+
* Only flag truly dangerous patterns like nested quantifiers: (a+)+, (a*)*
|
|
209
|
+
*/
|
|
210
|
+
const hasReDoSPatterns = (pattern: string): boolean => {
|
|
211
|
+
// Detect truly dangerous nested quantifier patterns that cause exponential backtracking
|
|
212
|
+
// Pattern like (a+)+, (a*)+, (a+)*, (a*)*, ([a-z]+)+
|
|
213
|
+
const nestedQuantifierPatterns = [
|
|
214
|
+
/\([^)]*[+*]\)[+*]/, // (something+)+ or (something*)* patterns
|
|
215
|
+
/\([^)]*[+*]\)\{[0-9,]+\}/, // (something+){n,m} patterns
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
for (const dangerousPattern of nestedQuantifierPatterns) {
|
|
219
|
+
if (dangerousPattern.test(pattern)) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return false;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Extract regex pattern from RegExp construction
|
|
229
|
+
*/
|
|
230
|
+
const extractPattern = (node: TSESTree.CallExpression | TSESTree.NewExpression): {
|
|
231
|
+
pattern: string;
|
|
232
|
+
patternNode: TSESTree.Node | null;
|
|
233
|
+
constructor: string;
|
|
234
|
+
isDynamic: boolean;
|
|
235
|
+
length: number;
|
|
236
|
+
} => {
|
|
237
|
+
const sourceCode = context.sourceCode || context.sourceCode;
|
|
238
|
+
|
|
239
|
+
// Determine constructor type
|
|
240
|
+
let constructor = 'RegExp';
|
|
241
|
+
if (node.type === 'NewExpression' && node.callee.type === 'Identifier') {
|
|
242
|
+
constructor = `new ${node.callee.name}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// First argument is the pattern
|
|
246
|
+
const patternNode = node.arguments.length > 0 ? node.arguments[0] : null;
|
|
247
|
+
const pattern = patternNode ? sourceCode.getText(patternNode) : '';
|
|
248
|
+
const isDynamic = patternNode ? !isLiteralString(patternNode) : false;
|
|
249
|
+
const length = patternNode && isLiteralString(patternNode) ?
|
|
250
|
+
String((patternNode as TSESTree.Literal).value).length : pattern.length;
|
|
251
|
+
|
|
252
|
+
return { pattern, patternNode, constructor, isDynamic, length };
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Detect the specific vulnerability pattern
|
|
257
|
+
*/
|
|
258
|
+
const detectVulnerability = (pattern: string, isDynamic: boolean): RegExpPattern | null => {
|
|
259
|
+
// Check for dynamic construction first (highest risk)
|
|
260
|
+
if (isDynamic) {
|
|
261
|
+
for (const vuln of REGEXP_PATTERNS) {
|
|
262
|
+
if (new RegExp(vuln.pattern, 'i').test(pattern)) {
|
|
263
|
+
return vuln;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Generic dynamic RegExp construction
|
|
267
|
+
return {
|
|
268
|
+
pattern: 'dynamic',
|
|
269
|
+
dangerous: true,
|
|
270
|
+
vulnerability: 'redos',
|
|
271
|
+
safeAlternative: 'Pre-defined RegExp constants',
|
|
272
|
+
example: {
|
|
273
|
+
bad: pattern,
|
|
274
|
+
good: 'const PATTERNS = { email: /^[a-zA-Z0-9]+$/ }; PATTERNS[type]'
|
|
275
|
+
},
|
|
276
|
+
effort: '10-15 minutes',
|
|
277
|
+
riskLevel: 'high'
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Check for ReDoS patterns in literal regex
|
|
282
|
+
if (hasReDoSPatterns(pattern)) {
|
|
283
|
+
return {
|
|
284
|
+
pattern: 'redos-literal',
|
|
285
|
+
dangerous: true,
|
|
286
|
+
vulnerability: 'redos',
|
|
287
|
+
safeAlternative: 'Restructure regex to avoid nested quantifiers',
|
|
288
|
+
example: {
|
|
289
|
+
bad: pattern,
|
|
290
|
+
good: pattern.replace(/(a+)\+/g, '$1') // Simplified example
|
|
291
|
+
},
|
|
292
|
+
effort: '20-30 minutes',
|
|
293
|
+
riskLevel: 'high'
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return null;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Generate refactoring steps based on the vulnerability
|
|
302
|
+
*/
|
|
303
|
+
const generateRefactoringSteps = (vulnerability: RegExpPattern): string => {
|
|
304
|
+
if (vulnerability.pattern === 'dynamic') {
|
|
305
|
+
return [
|
|
306
|
+
' 1. Create a whitelist of allowed regex patterns',
|
|
307
|
+
' 2. Use object lookup: PATTERNS[userChoice]',
|
|
308
|
+
' 3. If dynamic needed: escape input with regex escaping function',
|
|
309
|
+
' 4. Add pattern length validation',
|
|
310
|
+
' 5. Consider using a safe regex library'
|
|
311
|
+
].join('\n');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (vulnerability.pattern === 'redos-literal') {
|
|
315
|
+
return [
|
|
316
|
+
' 1. Identify nested quantifiers (*+, ++, ?+)',
|
|
317
|
+
' 2. Restructure regex to avoid exponential backtracking',
|
|
318
|
+
' 3. Use atomic groups if supported: (?>...)',
|
|
319
|
+
' 4. Test regex performance with long inputs',
|
|
320
|
+
' 5. Consider alternatives like string methods'
|
|
321
|
+
].join('\n');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
switch (vulnerability.vulnerability) {
|
|
325
|
+
case 'redos':
|
|
326
|
+
return [
|
|
327
|
+
' 1. Avoid nested quantifiers and backreferences',
|
|
328
|
+
' 2. Use possessive quantifiers: *+, ++, ?+',
|
|
329
|
+
' 3. Restructure regex to be more specific',
|
|
330
|
+
' 4. Test with potentially malicious inputs',
|
|
331
|
+
' 5. Consider safe-regex library validation'
|
|
332
|
+
].join('\n');
|
|
333
|
+
|
|
334
|
+
case 'injection':
|
|
335
|
+
return [
|
|
336
|
+
' 1. Escape user input before RegExp construction',
|
|
337
|
+
' 2. Use RegExp.escape() if available',
|
|
338
|
+
' 3. Validate input against allowed character sets',
|
|
339
|
+
' 4. Add length limits to prevent oversized patterns',
|
|
340
|
+
' 5. Use static patterns when possible'
|
|
341
|
+
].join('\n');
|
|
342
|
+
|
|
343
|
+
default:
|
|
344
|
+
return [
|
|
345
|
+
' 1. Identify the specific regex use case',
|
|
346
|
+
' 2. Choose appropriate safe alternative',
|
|
347
|
+
' 3. Add input validation and escaping',
|
|
348
|
+
' 4. Test thoroughly with edge cases',
|
|
349
|
+
' 5. Monitor performance in production'
|
|
350
|
+
].join('\n');
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Determine overall risk level
|
|
356
|
+
*/
|
|
357
|
+
const determineRiskLevel = (vulnerability: RegExpPattern, pattern: string): string => {
|
|
358
|
+
if (vulnerability.riskLevel === 'critical' || hasReDoSPatterns(pattern)) {
|
|
359
|
+
return 'CRITICAL';
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (vulnerability.riskLevel === 'high') {
|
|
363
|
+
return 'HIGH';
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return 'MEDIUM';
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Check RegExp constructor calls for vulnerabilities
|
|
371
|
+
*/
|
|
372
|
+
const checkRegExpCall = (node: TSESTree.CallExpression | TSESTree.NewExpression) => {
|
|
373
|
+
// Check for RegExp constructor calls
|
|
374
|
+
const isRegExpCall = node.callee.type === 'Identifier' && node.callee.name === 'RegExp';
|
|
375
|
+
const isNewRegExp = node.type === 'NewExpression' && node.callee.type === 'Identifier' && node.callee.name === 'RegExp';
|
|
376
|
+
|
|
377
|
+
if (!isRegExpCall && !isNewRegExp) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const { pattern, patternNode, isDynamic, length } = extractPattern(node);
|
|
382
|
+
|
|
383
|
+
// Allow literals if configured and pattern is reasonable length
|
|
384
|
+
if (allowLiterals && patternNode && isLiteralString(patternNode) && length <= maxPatternLength) {
|
|
385
|
+
// Still check for ReDoS patterns even in literals
|
|
386
|
+
if (!hasReDoSPatterns(pattern)) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const vulnerability = detectVulnerability(pattern, isDynamic);
|
|
392
|
+
|
|
393
|
+
// If no specific vulnerability detected but it's dynamic, still warn
|
|
394
|
+
const effectiveVulnerability = vulnerability || (isDynamic ? {
|
|
395
|
+
pattern: 'dynamic',
|
|
396
|
+
dangerous: true,
|
|
397
|
+
vulnerability: 'redos' as const,
|
|
398
|
+
safeAlternative: 'Use static RegExp patterns',
|
|
399
|
+
example: {
|
|
400
|
+
bad: pattern,
|
|
401
|
+
good: '/^safe-pattern$/'
|
|
402
|
+
},
|
|
403
|
+
effort: '10-15 minutes',
|
|
404
|
+
riskLevel: 'medium' as const
|
|
405
|
+
} : null);
|
|
406
|
+
|
|
407
|
+
if (!effectiveVulnerability) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const riskLevel = determineRiskLevel(effectiveVulnerability, pattern);
|
|
412
|
+
const steps = generateRefactoringSteps(effectiveVulnerability);
|
|
413
|
+
|
|
414
|
+
context.report({
|
|
415
|
+
node,
|
|
416
|
+
messageId: 'regexpReDoS',
|
|
417
|
+
data: {
|
|
418
|
+
pattern: pattern.substring(0, 30) + (pattern.length > 30 ? '...' : ''),
|
|
419
|
+
riskLevel,
|
|
420
|
+
vulnerability: effectiveVulnerability.vulnerability,
|
|
421
|
+
safeAlternative: effectiveVulnerability.safeAlternative,
|
|
422
|
+
steps,
|
|
423
|
+
effort: effectiveVulnerability.effort
|
|
424
|
+
},
|
|
425
|
+
suggest: [
|
|
426
|
+
{
|
|
427
|
+
messageId: 'useStaticRegex',
|
|
428
|
+
fix: () => null
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
messageId: 'validateInput',
|
|
432
|
+
fix: () => null
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
messageId: 'useRegexLibrary',
|
|
436
|
+
fix: () => null
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
messageId: 'addTimeout',
|
|
440
|
+
fix: () => null
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
messageId: 'escapeUserInput',
|
|
444
|
+
fix: () => null
|
|
445
|
+
}
|
|
446
|
+
]
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Check literal regex patterns for ReDoS vulnerabilities
|
|
452
|
+
*/
|
|
453
|
+
const checkLiteralRegExp = (node: TSESTree.Node) => {
|
|
454
|
+
if (!isRegExpLiteral(node)) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const pattern = node.regex.pattern;
|
|
459
|
+
|
|
460
|
+
// Check for ReDoS patterns
|
|
461
|
+
if (hasReDoSPatterns(pattern)) {
|
|
462
|
+
const vulnerability = detectVulnerability(pattern, false);
|
|
463
|
+
|
|
464
|
+
if (vulnerability) {
|
|
465
|
+
const riskLevel = determineRiskLevel(vulnerability, pattern);
|
|
466
|
+
const steps = generateRefactoringSteps(vulnerability);
|
|
467
|
+
|
|
468
|
+
context.report({
|
|
469
|
+
node,
|
|
470
|
+
messageId: 'regexpReDoS',
|
|
471
|
+
data: {
|
|
472
|
+
pattern: pattern.substring(0, 30) + (pattern.length > 30 ? '...' : ''),
|
|
473
|
+
riskLevel,
|
|
474
|
+
vulnerability: vulnerability.vulnerability,
|
|
475
|
+
safeAlternative: vulnerability.safeAlternative,
|
|
476
|
+
steps,
|
|
477
|
+
effort: vulnerability.effort
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
return {
|
|
485
|
+
CallExpression: checkRegExpCall,
|
|
486
|
+
NewExpression: checkRegExpCall,
|
|
487
|
+
Literal: checkLiteralRegExp
|
|
488
|
+
};
|
|
489
|
+
},
|
|
490
|
+
});
|