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,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive tests for no-improper-type-validation rule
|
|
3
|
+
* Security: CWE-1287 (Improper Validation of Specified Type of Input)
|
|
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 { noImproperTypeValidation } 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('no-improper-type-validation', () => {
|
|
26
|
+
describe('Valid Code', () => {
|
|
27
|
+
ruleTester.run('valid - proper type validation', noImproperTypeValidation, {
|
|
28
|
+
valid: [
|
|
29
|
+
// Proper type checking with strict equality
|
|
30
|
+
{
|
|
31
|
+
code: 'if (value !== null && typeof value === "object") { /* process */ }',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
code: 'if (Array.isArray(data)) { /* process array */ }',
|
|
35
|
+
},
|
|
36
|
+
// Non-user-input typeof checks are valid
|
|
37
|
+
{
|
|
38
|
+
code: 'if (typeof value === "string" && value.length > 0) { /* process */ }',
|
|
39
|
+
},
|
|
40
|
+
// Safe type guards
|
|
41
|
+
{
|
|
42
|
+
code: 'if (Number.isNaN(Number(value))) { /* handle NaN */ }',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
code: 'if (Object.prototype.toString.call(value) === "[object Array]") { /* process */ }',
|
|
46
|
+
},
|
|
47
|
+
// Strict equality for types
|
|
48
|
+
{
|
|
49
|
+
code: 'if (value !== null && value !== undefined) { /* process */ }',
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
invalid: [],
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('Invalid Code - Unsafe typeof Checks', () => {
|
|
57
|
+
ruleTester.run('invalid - unsafe typeof usage', noImproperTypeValidation, {
|
|
58
|
+
valid: [],
|
|
59
|
+
invalid: [
|
|
60
|
+
// typeof === "object" on user input variable
|
|
61
|
+
{
|
|
62
|
+
code: 'if (typeof userInput === "object") { processData(userInput); }',
|
|
63
|
+
errors: [
|
|
64
|
+
{
|
|
65
|
+
messageId: 'unsafeTypeofCheck',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
// typeof on data (user input variable)
|
|
70
|
+
{
|
|
71
|
+
code: 'const isObject = typeof data === "object";',
|
|
72
|
+
errors: [
|
|
73
|
+
{
|
|
74
|
+
messageId: 'unsafeTypeofCheck',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('Invalid Code - Unsafe instanceof Usage', () => {
|
|
83
|
+
ruleTester.run('invalid - unsafe instanceof usage', noImproperTypeValidation, {
|
|
84
|
+
valid: [],
|
|
85
|
+
invalid: [
|
|
86
|
+
// instanceof on user input with allowInstanceofSameRealm: false
|
|
87
|
+
{
|
|
88
|
+
code: 'if (userInput instanceof Array) { processArray(userInput); }',
|
|
89
|
+
options: [{ allowInstanceofSameRealm: false }],
|
|
90
|
+
errors: [
|
|
91
|
+
{
|
|
92
|
+
messageId: 'unsafeInstanceofUsage',
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
code: 'if (data instanceof Object) { handleObject(data); }',
|
|
98
|
+
options: [{ allowInstanceofSameRealm: false }],
|
|
99
|
+
errors: [
|
|
100
|
+
{
|
|
101
|
+
messageId: 'unsafeInstanceofUsage',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('Invalid Code - Loose Equality Type Checks', () => {
|
|
110
|
+
ruleTester.run('invalid - loose equality for types', noImproperTypeValidation, {
|
|
111
|
+
valid: [],
|
|
112
|
+
invalid: [
|
|
113
|
+
// Loose equality with null on user input variable - triggers both messages
|
|
114
|
+
// IfStatement reports missingNullCheck, BinaryExpression reports looseEqualityTypeCheck
|
|
115
|
+
{
|
|
116
|
+
code: 'if (input != null) { processInput(input); }',
|
|
117
|
+
errors: [
|
|
118
|
+
{
|
|
119
|
+
messageId: 'missingNullCheck',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
messageId: 'looseEqualityTypeCheck',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
// Loose equality with null (always flagged due to null comparison)
|
|
127
|
+
{
|
|
128
|
+
code: 'if (userData == null) { return; }',
|
|
129
|
+
errors: [
|
|
130
|
+
{
|
|
131
|
+
messageId: 'looseEqualityTypeCheck',
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('Invalid Code - Missing Null Checks', () => {
|
|
140
|
+
ruleTester.run('invalid - missing null checks', noImproperTypeValidation, {
|
|
141
|
+
valid: [],
|
|
142
|
+
invalid: [
|
|
143
|
+
// userInput is a user input variable - reports missingNullCheck first (IfStatement),
|
|
144
|
+
// then looseEqualityTypeCheck (BinaryExpression)
|
|
145
|
+
{
|
|
146
|
+
code: 'if (userInput != null) { processData(userInput); }',
|
|
147
|
+
errors: [
|
|
148
|
+
{
|
|
149
|
+
messageId: 'missingNullCheck',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
messageId: 'looseEqualityTypeCheck',
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
// req.body involves user input variable - only reports looseEqualityTypeCheck
|
|
157
|
+
// because req.body as MemberExpression doesn't trigger missingNullCheck
|
|
158
|
+
{
|
|
159
|
+
code: 'if (req.body == null) { return; }',
|
|
160
|
+
errors: [
|
|
161
|
+
{
|
|
162
|
+
messageId: 'looseEqualityTypeCheck',
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('Invalid Code - Unreliable Constructor Checks', () => {
|
|
171
|
+
ruleTester.run('invalid - unreliable constructor checks', noImproperTypeValidation, {
|
|
172
|
+
valid: [],
|
|
173
|
+
invalid: [
|
|
174
|
+
{
|
|
175
|
+
code: 'const type = userInput.constructor.name;',
|
|
176
|
+
errors: [
|
|
177
|
+
{
|
|
178
|
+
messageId: 'unreliableConstructorCheck',
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
code: 'if (data.constructor.name === "Array") { handleArray(data); }',
|
|
184
|
+
errors: [
|
|
185
|
+
{
|
|
186
|
+
messageId: 'unreliableConstructorCheck',
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('Invalid Code - Improper Type Validation', () => {
|
|
195
|
+
ruleTester.run('invalid - improper type validation patterns', noImproperTypeValidation, {
|
|
196
|
+
valid: [],
|
|
197
|
+
invalid: [
|
|
198
|
+
// typeof userInput === "object" - unsafe typeof check on user input
|
|
199
|
+
{
|
|
200
|
+
code: 'const type = typeof userInput === "object";',
|
|
201
|
+
errors: [
|
|
202
|
+
{
|
|
203
|
+
messageId: 'unsafeTypeofCheck',
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe('Valid Code - False Positives Reduced', () => {
|
|
212
|
+
ruleTester.run('valid - false positives reduced', noImproperTypeValidation, {
|
|
213
|
+
valid: [
|
|
214
|
+
// Safe annotations
|
|
215
|
+
{
|
|
216
|
+
code: `
|
|
217
|
+
/** @validated */
|
|
218
|
+
if (typeof userInput === "object") {
|
|
219
|
+
processData(userInput);
|
|
220
|
+
}
|
|
221
|
+
`,
|
|
222
|
+
},
|
|
223
|
+
// TypeScript type guards (would be handled by TS compiler)
|
|
224
|
+
{
|
|
225
|
+
code: `
|
|
226
|
+
function isString(value: any): value is string {
|
|
227
|
+
return typeof value === "string";
|
|
228
|
+
}
|
|
229
|
+
`,
|
|
230
|
+
},
|
|
231
|
+
// Safe type checking functions
|
|
232
|
+
{
|
|
233
|
+
code: `
|
|
234
|
+
if (validateType(userInput, "object")) {
|
|
235
|
+
processData(userInput);
|
|
236
|
+
}
|
|
237
|
+
`,
|
|
238
|
+
},
|
|
239
|
+
// Proper null checks
|
|
240
|
+
{
|
|
241
|
+
code: `
|
|
242
|
+
if (userInput !== null && userInput !== undefined) {
|
|
243
|
+
processData(userInput);
|
|
244
|
+
}
|
|
245
|
+
`,
|
|
246
|
+
},
|
|
247
|
+
// Safe instanceof within same realm
|
|
248
|
+
{
|
|
249
|
+
code: `
|
|
250
|
+
const arr = [1, 2, 3];
|
|
251
|
+
if (arr instanceof Array) {
|
|
252
|
+
processArray(arr);
|
|
253
|
+
}
|
|
254
|
+
`,
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
invalid: [],
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('Configuration Options', () => {
|
|
262
|
+
ruleTester.run('config - custom user input variables', noImproperTypeValidation, {
|
|
263
|
+
valid: [
|
|
264
|
+
// otherInput is NOT in userInputVariables, so it's not flagged
|
|
265
|
+
{
|
|
266
|
+
code: 'if (typeof otherInput === "object") { /* process */ }',
|
|
267
|
+
options: [{ userInputVariables: ['customInput'] }],
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
invalid: [
|
|
271
|
+
// customInput IS in userInputVariables, so it's flagged
|
|
272
|
+
{
|
|
273
|
+
code: 'if (typeof customInput === "object") { /* process */ }',
|
|
274
|
+
options: [{ userInputVariables: ['customInput'] }],
|
|
275
|
+
errors: [
|
|
276
|
+
{
|
|
277
|
+
messageId: 'unsafeTypeofCheck',
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
ruleTester.run('config - disable instanceof checks', noImproperTypeValidation, {
|
|
285
|
+
valid: [],
|
|
286
|
+
invalid: [
|
|
287
|
+
{
|
|
288
|
+
code: 'if (data instanceof Array) { /* process */ }',
|
|
289
|
+
options: [{ allowInstanceofSameRealm: false }],
|
|
290
|
+
errors: [
|
|
291
|
+
{
|
|
292
|
+
messageId: 'unsafeInstanceofUsage',
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('Complex Type Validation Scenarios', () => {
|
|
301
|
+
ruleTester.run('complex - real-world type validation patterns', noImproperTypeValidation, {
|
|
302
|
+
valid: [],
|
|
303
|
+
invalid: [
|
|
304
|
+
// typeof userInput === "object" triggers unsafeTypeofCheck
|
|
305
|
+
{
|
|
306
|
+
code: `
|
|
307
|
+
function processUserData(userInput) {
|
|
308
|
+
// DANGEROUS: typeof check misses null
|
|
309
|
+
if (typeof userInput === "object") {
|
|
310
|
+
// null would pass this check!
|
|
311
|
+
Object.keys(userInput).forEach(key => {
|
|
312
|
+
processField(key, userInput[key]);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
`,
|
|
317
|
+
errors: [
|
|
318
|
+
{
|
|
319
|
+
messageId: 'unsafeTypeofCheck',
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
// credentials != null triggers looseEqualityTypeCheck (null comparison)
|
|
324
|
+
{
|
|
325
|
+
code: `
|
|
326
|
+
// Authentication bypass through type confusion
|
|
327
|
+
function authenticate(credentials) {
|
|
328
|
+
// DANGEROUS: loose equality allows type confusion
|
|
329
|
+
if (credentials != null) {
|
|
330
|
+
if (credentials.username == "admin") { // == allows string/number confusion
|
|
331
|
+
return { role: "admin" };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return { role: "user" };
|
|
335
|
+
}
|
|
336
|
+
`,
|
|
337
|
+
errors: [
|
|
338
|
+
{
|
|
339
|
+
messageId: 'looseEqualityTypeCheck',
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
// input != null triggers missingNullCheck (IfStatement) first,
|
|
344
|
+
// then looseEqualityTypeCheck (BinaryExpression)
|
|
345
|
+
{
|
|
346
|
+
code: `
|
|
347
|
+
// Incomplete type validation
|
|
348
|
+
function validateAndProcess(input) {
|
|
349
|
+
// DANGEROUS: only checks != null, misses undefined
|
|
350
|
+
if (input != null) {
|
|
351
|
+
if (typeof input === "string") {
|
|
352
|
+
processString(input);
|
|
353
|
+
} else if (Array.isArray(input)) {
|
|
354
|
+
processArray(input);
|
|
355
|
+
}
|
|
356
|
+
// undefined would pass != null check but cause issues
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
`,
|
|
360
|
+
errors: [
|
|
361
|
+
{
|
|
362
|
+
messageId: 'missingNullCheck',
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
messageId: 'looseEqualityTypeCheck',
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
});
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint Rule: no-insecure-comparison
|
|
3
|
+
* Detects insecure comparison operators (==, !=) that can lead to type coercion vulnerabilities
|
|
4
|
+
* CWE-697: Incorrect Comparison
|
|
5
|
+
*
|
|
6
|
+
* @see https://cwe.mitre.org/data/definitions/697.html
|
|
7
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
|
|
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 = 'insecureComparison' | 'useStrictEquality' | 'timingUnsafeComparison';
|
|
14
|
+
|
|
15
|
+
export interface Options {
|
|
16
|
+
/** Allow insecure comparison in test files. Default: false */
|
|
17
|
+
allowInTests?: boolean;
|
|
18
|
+
|
|
19
|
+
/** Additional patterns to ignore. Default: [] */
|
|
20
|
+
ignorePatterns?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type RuleOptions = [Options?];
|
|
24
|
+
|
|
25
|
+
export const noInsecureComparison = createRule<RuleOptions, MessageIds>({
|
|
26
|
+
name: 'no-insecure-comparison',
|
|
27
|
+
meta: {
|
|
28
|
+
type: 'problem',
|
|
29
|
+
deprecated: true,
|
|
30
|
+
replacedBy: ['@see eslint-plugin-crypto/no-timing-unsafe-compare'],
|
|
31
|
+
docs: {
|
|
32
|
+
description: 'Detects insecure comparison operators (==, !=) that can lead to type coercion vulnerabilities',
|
|
33
|
+
},
|
|
34
|
+
fixable: 'code',
|
|
35
|
+
hasSuggestions: true,
|
|
36
|
+
messages: {
|
|
37
|
+
insecureComparison: formatLLMMessage({
|
|
38
|
+
icon: MessageIcons.SECURITY,
|
|
39
|
+
issueName: 'Insecure Comparison',
|
|
40
|
+
cwe: 'CWE-697',
|
|
41
|
+
description: 'Insecure comparison operator ({{operator}}) detected - can lead to type coercion vulnerabilities',
|
|
42
|
+
severity: 'HIGH',
|
|
43
|
+
fix: 'Use strict equality ({{strictOperator}}) instead: {{example}}',
|
|
44
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/697.html',
|
|
45
|
+
}),
|
|
46
|
+
useStrictEquality: formatLLMMessage({
|
|
47
|
+
icon: MessageIcons.INFO,
|
|
48
|
+
issueName: 'Use Strict Equality',
|
|
49
|
+
description: 'Use strict equality operator',
|
|
50
|
+
severity: 'LOW',
|
|
51
|
+
fix: 'Replace == with === and != with !==',
|
|
52
|
+
documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality',
|
|
53
|
+
}),
|
|
54
|
+
timingUnsafeComparison: formatLLMMessage({
|
|
55
|
+
icon: MessageIcons.SECURITY,
|
|
56
|
+
issueName: 'Timing Attack Risk',
|
|
57
|
+
cwe: 'CWE-208',
|
|
58
|
+
description: 'Secret comparison with {{operator}} can leak timing information',
|
|
59
|
+
severity: 'HIGH',
|
|
60
|
+
fix: 'Use crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))',
|
|
61
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b',
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
schema: [
|
|
65
|
+
{
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
allowInTests: {
|
|
69
|
+
type: 'boolean',
|
|
70
|
+
default: false,
|
|
71
|
+
description: 'Allow insecure comparison in test files',
|
|
72
|
+
},
|
|
73
|
+
ignorePatterns: {
|
|
74
|
+
type: 'array',
|
|
75
|
+
items: { type: 'string' },
|
|
76
|
+
default: [],
|
|
77
|
+
description: 'Additional patterns to ignore',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
defaultOptions: [
|
|
85
|
+
{
|
|
86
|
+
allowInTests: false,
|
|
87
|
+
ignorePatterns: [],
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
create(
|
|
91
|
+
context: TSESLint.RuleContext<MessageIds, RuleOptions>,
|
|
92
|
+
[options = {}]
|
|
93
|
+
) {
|
|
94
|
+
const {
|
|
95
|
+
allowInTests = false,
|
|
96
|
+
ignorePatterns = [],
|
|
97
|
+
} = options as Options;
|
|
98
|
+
|
|
99
|
+
const filename = context.getFilename();
|
|
100
|
+
const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
|
|
101
|
+
const sourceCode = context.sourceCode || context.sourceCode;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Check if a string matches any ignore pattern
|
|
105
|
+
*/
|
|
106
|
+
function matchesIgnorePattern(text: string, patterns: string[]): boolean {
|
|
107
|
+
return patterns.some(pattern => {
|
|
108
|
+
try {
|
|
109
|
+
const regex = new RegExp(pattern, 'i');
|
|
110
|
+
return regex.test(text);
|
|
111
|
+
} catch {
|
|
112
|
+
// Invalid regex - treat as literal string match
|
|
113
|
+
return text.toLowerCase().includes(pattern.toLowerCase());
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check BinaryExpression for insecure comparison operators
|
|
120
|
+
*/
|
|
121
|
+
function checkBinaryExpression(node: TSESTree.BinaryExpression) {
|
|
122
|
+
if (isTestFile) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const secretKeywords = ['secret', 'token', 'password', 'apikey', 'api_key', 'signature', 'auth', 'key', 'hash', 'digest', 'mac'];
|
|
127
|
+
|
|
128
|
+
const isSecurityContext = ((): boolean => {
|
|
129
|
+
let current: TSESTree.Node | undefined = node;
|
|
130
|
+
while (current) {
|
|
131
|
+
if ((current.type === 'FunctionDeclaration' ||
|
|
132
|
+
current.type === 'FunctionExpression' ||
|
|
133
|
+
current.type === 'ArrowFunctionExpression') &&
|
|
134
|
+
'id' in current && current.id?.name) {
|
|
135
|
+
if (/security|auth|crypto|hash|token|secret|insecure|verify|validate/i.test(current.id.name)) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (current.type === 'MethodDefinition' && current.key.type === 'Identifier') {
|
|
140
|
+
if (/security|auth|crypto|hash|token|secret|insecure|verify|validate/i.test(current.key.name)) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
current = current.parent;
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
})();
|
|
148
|
+
|
|
149
|
+
const isPotentialSecret = (expr: TSESTree.Expression): boolean => {
|
|
150
|
+
const text = sourceCode.getText(expr).toLowerCase();
|
|
151
|
+
if (secretKeywords.some(keyword => text.includes(keyword))) return true;
|
|
152
|
+
|
|
153
|
+
// In security contexts, treat generic terms as potential secrets
|
|
154
|
+
if (isSecurityContext) {
|
|
155
|
+
const contextKeywords = ['provided', 'expected', 'actual', 'input', 'value', 'data'];
|
|
156
|
+
return contextKeywords.some(keyword => text.includes(keyword));
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Timing-safe comparison for secrets even with strict equality
|
|
162
|
+
if ((node.operator === '===' || node.operator === '!==') &&
|
|
163
|
+
(isPotentialSecret(node.left) || isPotentialSecret(node.right))) {
|
|
164
|
+
const leftText = sourceCode.getText(node.left);
|
|
165
|
+
const rightText = sourceCode.getText(node.right);
|
|
166
|
+
|
|
167
|
+
// ... rest of logic uses example ...
|
|
168
|
+
const example = `crypto.timingSafeEqual(Buffer.from(${leftText}), Buffer.from(${rightText}))`;
|
|
169
|
+
|
|
170
|
+
context.report({
|
|
171
|
+
node,
|
|
172
|
+
messageId: 'timingUnsafeComparison',
|
|
173
|
+
data: {
|
|
174
|
+
operator: node.operator,
|
|
175
|
+
strictOperator: node.operator,
|
|
176
|
+
example: example,
|
|
177
|
+
},
|
|
178
|
+
suggest: [
|
|
179
|
+
{
|
|
180
|
+
messageId: 'useStrictEquality', // This messageId usage might be wrong for timing safe output, but kept for now or reused?
|
|
181
|
+
// Wait, previous code used useStrictEquality as suggest?
|
|
182
|
+
// Ah, the previous code had a fix/suggest structure.
|
|
183
|
+
fix: (fixer: TSESLint.RuleFixer) => fixer.replaceText(node, example),
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check for insecure comparison operators
|
|
191
|
+
if (node.operator === '==' || node.operator === '!=') {
|
|
192
|
+
const text = sourceCode.getText(node);
|
|
193
|
+
|
|
194
|
+
// Check if it matches any ignore pattern
|
|
195
|
+
if (matchesIgnorePattern(text, ignorePatterns)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const strictOperator = node.operator === '==' ? '===' : '!==';
|
|
200
|
+
const leftText = sourceCode.getText(node.left);
|
|
201
|
+
const rightText = sourceCode.getText(node.right);
|
|
202
|
+
const example = `${leftText} ${strictOperator} ${rightText}`;
|
|
203
|
+
|
|
204
|
+
context.report({
|
|
205
|
+
node: node,
|
|
206
|
+
messageId: 'insecureComparison',
|
|
207
|
+
data: {
|
|
208
|
+
operator: node.operator,
|
|
209
|
+
strictOperator,
|
|
210
|
+
example,
|
|
211
|
+
},
|
|
212
|
+
fix: (fixer: TSESLint.RuleFixer) => {
|
|
213
|
+
return fixer.replaceText(node, example);
|
|
214
|
+
},
|
|
215
|
+
suggest: [
|
|
216
|
+
{
|
|
217
|
+
messageId: 'useStrictEquality',
|
|
218
|
+
fix: (fixer: TSESLint.RuleFixer) => {
|
|
219
|
+
return fixer.replaceText(node, example);
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
BinaryExpression: checkBinaryExpression,
|
|
229
|
+
};
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
|