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,552 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint Rule: no-timing-attack
|
|
3
|
+
* Detects timing attack vulnerabilities (CWE-208)
|
|
4
|
+
*
|
|
5
|
+
* Timing attacks exploit the time it takes for operations to complete to leak
|
|
6
|
+
* sensitive information. This is particularly dangerous in authentication code
|
|
7
|
+
* where attackers can use timing differences to guess passwords, tokens, or
|
|
8
|
+
* other secrets.
|
|
9
|
+
*
|
|
10
|
+
* False Positive Reduction:
|
|
11
|
+
* This rule uses security utilities to reduce false positives by detecting:
|
|
12
|
+
* - Authentication-related functions and variables
|
|
13
|
+
* - Secret/sensitive data handling
|
|
14
|
+
* - JSDoc annotations (@timing-safe, @constant-time)
|
|
15
|
+
* - Safe comparison libraries (crypto.timingSafeEqual)
|
|
16
|
+
*/
|
|
17
|
+
import type { TSESLint, TSESTree } from '@interlace/eslint-devkit';
|
|
18
|
+
import { AST_NODE_TYPES, createRule } from '@interlace/eslint-devkit';
|
|
19
|
+
import { formatLLMMessage, MessageIcons } from '@interlace/eslint-devkit';
|
|
20
|
+
import {
|
|
21
|
+
createSafetyChecker,
|
|
22
|
+
type SecurityRuleOptions,
|
|
23
|
+
} from '@interlace/eslint-devkit';
|
|
24
|
+
|
|
25
|
+
type MessageIds =
|
|
26
|
+
| 'timingAttack'
|
|
27
|
+
| 'insecureStringComparison'
|
|
28
|
+
| 'earlyReturnLeakage'
|
|
29
|
+
| 'useTimingSafeEqual'
|
|
30
|
+
| 'useConstantTimeComparison'
|
|
31
|
+
| 'avoidEarlyReturns'
|
|
32
|
+
| 'strategyTimingSafe'
|
|
33
|
+
| 'strategyConstantTime'
|
|
34
|
+
| 'strategyConsistentTiming';
|
|
35
|
+
|
|
36
|
+
export interface Options extends SecurityRuleOptions {
|
|
37
|
+
/** Functions that are considered authentication-related */
|
|
38
|
+
authFunctions?: string[];
|
|
39
|
+
|
|
40
|
+
/** Variables that contain sensitive/auth data */
|
|
41
|
+
sensitiveVariables?: string[];
|
|
42
|
+
|
|
43
|
+
/** Allow early returns in non-sensitive contexts */
|
|
44
|
+
allowEarlyReturns?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type RuleOptions = [Options?];
|
|
48
|
+
|
|
49
|
+
export const noTimingAttack = createRule<RuleOptions, MessageIds>({
|
|
50
|
+
name: 'no-timing-attack',
|
|
51
|
+
meta: {
|
|
52
|
+
type: 'problem',
|
|
53
|
+
deprecated: true,
|
|
54
|
+
replacedBy: ['@see eslint-plugin-crypto/no-timing-unsafe-compare'],
|
|
55
|
+
docs: {
|
|
56
|
+
description: 'Detects timing attack vulnerabilities in authentication code',
|
|
57
|
+
},
|
|
58
|
+
fixable: 'code',
|
|
59
|
+
messages: {
|
|
60
|
+
timingAttack: formatLLMMessage({
|
|
61
|
+
icon: MessageIcons.SECURITY,
|
|
62
|
+
issueName: 'Timing Attack Vulnerability',
|
|
63
|
+
cwe: 'CWE-208',
|
|
64
|
+
description: 'Timing attack possible - execution time reveals secret information',
|
|
65
|
+
severity: '{{severity}}',
|
|
66
|
+
fix: '{{safeAlternative}}',
|
|
67
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
68
|
+
}),
|
|
69
|
+
insecureStringComparison: formatLLMMessage({
|
|
70
|
+
icon: MessageIcons.SECURITY,
|
|
71
|
+
issueName: 'Insecure String Comparison',
|
|
72
|
+
cwe: 'CWE-208',
|
|
73
|
+
description: 'String comparison may leak timing information',
|
|
74
|
+
severity: 'HIGH',
|
|
75
|
+
fix: 'Use crypto.timingSafeEqual() for comparing secrets',
|
|
76
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
77
|
+
}),
|
|
78
|
+
earlyReturnLeakage: formatLLMMessage({
|
|
79
|
+
icon: MessageIcons.SECURITY,
|
|
80
|
+
issueName: 'Early Return Timing Leak',
|
|
81
|
+
cwe: 'CWE-208',
|
|
82
|
+
description: 'Early return may leak information through timing',
|
|
83
|
+
severity: 'MEDIUM',
|
|
84
|
+
fix: 'Process all inputs consistently to avoid timing differences',
|
|
85
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
86
|
+
}),
|
|
87
|
+
useTimingSafeEqual: formatLLMMessage({
|
|
88
|
+
icon: MessageIcons.INFO,
|
|
89
|
+
issueName: 'Use Timing Safe Equal',
|
|
90
|
+
description: 'Use crypto.timingSafeEqual for constant-time comparison',
|
|
91
|
+
severity: 'LOW',
|
|
92
|
+
fix: 'crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))',
|
|
93
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
94
|
+
}),
|
|
95
|
+
useConstantTimeComparison: formatLLMMessage({
|
|
96
|
+
icon: MessageIcons.INFO,
|
|
97
|
+
issueName: 'Constant Time Comparison',
|
|
98
|
+
description: 'Implement constant-time comparison algorithm',
|
|
99
|
+
severity: 'LOW',
|
|
100
|
+
fix: 'Compare all bytes regardless of content',
|
|
101
|
+
documentationLink: 'https://en.wikipedia.org/wiki/Timing_attack',
|
|
102
|
+
}),
|
|
103
|
+
avoidEarlyReturns: formatLLMMessage({
|
|
104
|
+
icon: MessageIcons.INFO,
|
|
105
|
+
issueName: 'Avoid Early Returns',
|
|
106
|
+
description: 'Avoid early returns in security-sensitive code',
|
|
107
|
+
severity: 'LOW',
|
|
108
|
+
fix: 'Process all inputs before making decisions',
|
|
109
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
110
|
+
}),
|
|
111
|
+
strategyTimingSafe: formatLLMMessage({
|
|
112
|
+
icon: MessageIcons.STRATEGY,
|
|
113
|
+
issueName: 'Timing Safe Strategy',
|
|
114
|
+
description: 'Use built-in timing-safe comparison functions',
|
|
115
|
+
severity: 'LOW',
|
|
116
|
+
fix: 'Use crypto.timingSafeEqual or equivalent library functions',
|
|
117
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
118
|
+
}),
|
|
119
|
+
strategyConstantTime: formatLLMMessage({
|
|
120
|
+
icon: MessageIcons.STRATEGY,
|
|
121
|
+
issueName: 'Constant Time Strategy',
|
|
122
|
+
description: 'Implement custom constant-time comparison',
|
|
123
|
+
severity: 'LOW',
|
|
124
|
+
fix: 'Compare all characters/bytes with consistent timing',
|
|
125
|
+
documentationLink: 'https://en.wikipedia.org/wiki/Timing_attack',
|
|
126
|
+
}),
|
|
127
|
+
strategyConsistentTiming: formatLLMMessage({
|
|
128
|
+
icon: MessageIcons.STRATEGY,
|
|
129
|
+
issueName: 'Consistent Timing Strategy',
|
|
130
|
+
description: 'Ensure consistent execution time for all inputs',
|
|
131
|
+
severity: 'LOW',
|
|
132
|
+
fix: 'Pad inputs or use fixed-time operations',
|
|
133
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
134
|
+
})
|
|
135
|
+
},
|
|
136
|
+
schema: [
|
|
137
|
+
{
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
authFunctions: {
|
|
141
|
+
type: 'array',
|
|
142
|
+
items: { type: 'string' },
|
|
143
|
+
default: ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'],
|
|
144
|
+
},
|
|
145
|
+
sensitiveVariables: {
|
|
146
|
+
type: 'array',
|
|
147
|
+
items: { type: 'string' },
|
|
148
|
+
default: ['password', 'token', 'secret', 'key', 'credentials', 'auth'],
|
|
149
|
+
},
|
|
150
|
+
allowEarlyReturns: {
|
|
151
|
+
type: 'boolean',
|
|
152
|
+
default: false,
|
|
153
|
+
description: 'Allow early returns outside security-sensitive contexts'
|
|
154
|
+
},
|
|
155
|
+
trustedSanitizers: {
|
|
156
|
+
type: 'array',
|
|
157
|
+
items: { type: 'string' },
|
|
158
|
+
default: [],
|
|
159
|
+
description: 'Additional function names to consider as timing-safe',
|
|
160
|
+
},
|
|
161
|
+
trustedAnnotations: {
|
|
162
|
+
type: 'array',
|
|
163
|
+
items: { type: 'string' },
|
|
164
|
+
default: [],
|
|
165
|
+
description: 'Additional JSDoc annotations to consider as timing-safe markers',
|
|
166
|
+
},
|
|
167
|
+
strictMode: {
|
|
168
|
+
type: 'boolean',
|
|
169
|
+
default: false,
|
|
170
|
+
description: 'Disable all false positive detection (strict mode)',
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
additionalProperties: false,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
defaultOptions: [
|
|
178
|
+
{
|
|
179
|
+
authFunctions: ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'],
|
|
180
|
+
sensitiveVariables: ['password', 'token', 'secret', 'key', 'credentials', 'auth'],
|
|
181
|
+
allowEarlyReturns: false,
|
|
182
|
+
trustedSanitizers: ['sanitize'],
|
|
183
|
+
trustedAnnotations: ['@timing-safe'],
|
|
184
|
+
strictMode: false,
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
create(context: TSESLint.RuleContext<MessageIds, RuleOptions>) {
|
|
188
|
+
const options = context.options[0] || {};
|
|
189
|
+
const {
|
|
190
|
+
authFunctions = ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'],
|
|
191
|
+
sensitiveVariables = ['password', 'token', 'secret', 'key', 'credentials', 'auth'],
|
|
192
|
+
allowEarlyReturns = false,
|
|
193
|
+
trustedSanitizers = [],
|
|
194
|
+
trustedAnnotations = [],
|
|
195
|
+
strictMode = false,
|
|
196
|
+
}: Options = options;
|
|
197
|
+
|
|
198
|
+
const sourceCode = context.sourceCode || context.sourceCode;
|
|
199
|
+
const filename = context.filename || context.getFilename();
|
|
200
|
+
|
|
201
|
+
// Create safety checker for false positive detection
|
|
202
|
+
const safetyChecker = createSafetyChecker({
|
|
203
|
+
trustedSanitizers,
|
|
204
|
+
trustedAnnotations,
|
|
205
|
+
trustedOrmPatterns: [],
|
|
206
|
+
strictMode,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Pre-compute Set for O(1) lookups (performance optimization)
|
|
210
|
+
const sensitiveVarSet = new Set(sensitiveVariables.map(s => s.toLowerCase()));
|
|
211
|
+
const authFunctionSet = new Set(authFunctions.map(f => f.toLowerCase()));
|
|
212
|
+
|
|
213
|
+
// Track variables that contain sensitive data
|
|
214
|
+
const sensitiveVars = new Set<string>();
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Check if a variable name indicates sensitive/auth data
|
|
218
|
+
* Uses Set-based lookup for O(1) performance
|
|
219
|
+
*/
|
|
220
|
+
const isSensitiveVariable = (varName: string): boolean => {
|
|
221
|
+
const lowerName = varName.toLowerCase();
|
|
222
|
+
// Check if any sensitive keyword is a substring
|
|
223
|
+
for (const sensitive of sensitiveVarSet) {
|
|
224
|
+
if (lowerName.includes(sensitive)) return true;
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Pre-compute auth patterns set for O(1) lookups
|
|
230
|
+
const authPatternSet = new Set(['auth', 'login', 'verify', 'token', 'password', 'credential', 'authenticate']);
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Check if we're in an authentication/security context
|
|
234
|
+
* Uses Set-based lookups for O(1) performance
|
|
235
|
+
*/
|
|
236
|
+
const isInAuthContext = (node: TSESTree.Node): boolean => {
|
|
237
|
+
// Check if we're inside an authentication function
|
|
238
|
+
let current: TSESTree.Node | undefined = node;
|
|
239
|
+
/* c8 ignore start -- defensive auth context detection */
|
|
240
|
+
while (current) {
|
|
241
|
+
if (current.type === AST_NODE_TYPES.FunctionDeclaration || current.type === AST_NODE_TYPES.FunctionExpression || current.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
242
|
+
const funcName = (current as { id?: { name?: string } }).id?.name;
|
|
243
|
+
if (funcName) {
|
|
244
|
+
const lowerName = funcName.toLowerCase();
|
|
245
|
+
// Check exact matches first (O(1) with Set)
|
|
246
|
+
if (authFunctionSet.has(lowerName)) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
// Check pattern matches for common auth function names
|
|
250
|
+
for (const pattern of authPatternSet) {
|
|
251
|
+
if (lowerName.includes(pattern)) return true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (current.type === AST_NODE_TYPES.CallExpression) {
|
|
256
|
+
const callee = current.callee;
|
|
257
|
+
if (callee.type === AST_NODE_TYPES.Identifier) {
|
|
258
|
+
const lowerName = callee.name.toLowerCase();
|
|
259
|
+
if (authFunctionSet.has(lowerName)) {
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
// Check pattern matches
|
|
263
|
+
for (const pattern of authPatternSet) {
|
|
264
|
+
if (lowerName.includes(pattern)) return true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
current = current.parent as TSESTree.Node;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// NOTE: Removed sensitiveVars.size check - it was causing false positives
|
|
272
|
+
// by flagging every function when ANY sensitive variable exists in the file.
|
|
273
|
+
// Instead, we now check sensitive data involvement at the specific point of use.
|
|
274
|
+
return false;
|
|
275
|
+
/* c8 ignore stop */
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Check if a comparison is timing-safe
|
|
280
|
+
*/
|
|
281
|
+
const isTimingSafeComparison = (node: TSESTree.BinaryExpression): boolean => {
|
|
282
|
+
// Check for crypto.timingSafeEqual calls
|
|
283
|
+
let current: TSESTree.Node | undefined = node;
|
|
284
|
+
while (current) {
|
|
285
|
+
if (current.type === AST_NODE_TYPES.CallExpression) {
|
|
286
|
+
const callee = current.callee;
|
|
287
|
+
if (
|
|
288
|
+
callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
289
|
+
callee.object.type === AST_NODE_TYPES.Identifier &&
|
|
290
|
+
callee.object.name === 'crypto' &&
|
|
291
|
+
callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
292
|
+
callee.property.name === 'timingSafeEqual'
|
|
293
|
+
) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
current = current.parent as TSESTree.Node;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return false;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Check if we're inside a function that uses crypto.timingSafeEqual.
|
|
305
|
+
* Length checks before timingSafeEqual are necessary and safe.
|
|
306
|
+
*
|
|
307
|
+
* Pattern:
|
|
308
|
+
* function safeCompare(a, b) {
|
|
309
|
+
* if (a.length !== b.length) return false; // <-- This is SAFE
|
|
310
|
+
* return crypto.timingSafeEqual(a, b);
|
|
311
|
+
* }
|
|
312
|
+
*/
|
|
313
|
+
const isInTimingSafeEqualContext = (node: TSESTree.Node): boolean => {
|
|
314
|
+
// Find enclosing function
|
|
315
|
+
let funcNode: TSESTree.Node | undefined = node;
|
|
316
|
+
while (funcNode) {
|
|
317
|
+
if (
|
|
318
|
+
funcNode.type === AST_NODE_TYPES.FunctionDeclaration ||
|
|
319
|
+
funcNode.type === AST_NODE_TYPES.FunctionExpression ||
|
|
320
|
+
funcNode.type === AST_NODE_TYPES.ArrowFunctionExpression
|
|
321
|
+
) {
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
funcNode = funcNode.parent;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (!funcNode) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check if the function body contains crypto.timingSafeEqual
|
|
332
|
+
const funcText = sourceCode.getText(funcNode);
|
|
333
|
+
if (funcText.includes('timingSafeEqual')) {
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Also check for common timing-safe comparison library patterns
|
|
338
|
+
const timingSafePatterns = [
|
|
339
|
+
'scmp', // secure-compare
|
|
340
|
+
'safe-compare',
|
|
341
|
+
'constant-time',
|
|
342
|
+
'constantTimeCompare',
|
|
343
|
+
];
|
|
344
|
+
|
|
345
|
+
if (timingSafePatterns.some(pattern => funcText.includes(pattern))) {
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return false;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Check if a comparison involves only non-sensitive data like length checks
|
|
354
|
+
*/
|
|
355
|
+
const isLengthOrNumericComparison = (node: TSESTree.BinaryExpression): boolean => {
|
|
356
|
+
const leftText = sourceCode.getText(node.left);
|
|
357
|
+
const rightText = sourceCode.getText(node.right);
|
|
358
|
+
|
|
359
|
+
// Check for .length comparisons
|
|
360
|
+
if (leftText.includes('.length') || rightText.includes('.length')) {
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Check for numeric literal comparisons
|
|
365
|
+
if (node.left.type === AST_NODE_TYPES.Literal && typeof node.left.value === 'number') {
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
if (node.right.type === AST_NODE_TYPES.Literal && typeof node.right.value === 'number') {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return false;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Check if early return is in a security-sensitive context
|
|
378
|
+
*/
|
|
379
|
+
const isEarlyReturnInAuthContext = (node: TSESTree.ReturnStatement): boolean => {
|
|
380
|
+
// If early returns are explicitly allowed, don't flag them
|
|
381
|
+
if (allowEarlyReturns) {
|
|
382
|
+
/* c8 ignore next */
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return isInAuthContext(node);
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
// Track sensitive variable declarations
|
|
391
|
+
VariableDeclarator(node: TSESTree.VariableDeclarator) {
|
|
392
|
+
if (node.id.type === 'Identifier' && isSensitiveVariable(node.id.name)) {
|
|
393
|
+
sensitiveVars.add(node.id.name);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Also check if the variable is assigned sensitive data
|
|
397
|
+
if (node.init && node.id.type === 'Identifier') {
|
|
398
|
+
const initText = sourceCode.getText(node.init).toLowerCase();
|
|
399
|
+
if (sensitiveVariables.some(sensitive => initText.includes(sensitive))) {
|
|
400
|
+
sensitiveVars.add(node.id.name);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
|
|
405
|
+
// Check binary expressions for insecure comparisons
|
|
406
|
+
BinaryExpression(node: TSESTree.BinaryExpression) {
|
|
407
|
+
if (node.operator !== '===' && node.operator !== '==' &&
|
|
408
|
+
node.operator !== '!==' && node.operator !== '!=') {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Skip if already using timing-safe comparison
|
|
413
|
+
if (isTimingSafeComparison(node)) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// FALSE POSITIVE REDUCTION: Skip length/numeric comparisons in timing-safe contexts
|
|
418
|
+
// Pattern: if (a.length !== b.length) return false; before crypto.timingSafeEqual
|
|
419
|
+
if (isInTimingSafeEqualContext(node) && isLengthOrNumericComparison(node)) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
424
|
+
if (safetyChecker.isSafe(node, context)) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Check if either side involves sensitive data
|
|
429
|
+
const leftText = sourceCode.getText(node.left).toLowerCase();
|
|
430
|
+
const rightText = sourceCode.getText(node.right).toLowerCase();
|
|
431
|
+
|
|
432
|
+
const involvesSensitiveData = [leftText, rightText].some(text =>
|
|
433
|
+
sensitiveVariables.some(sensitive => text.toLowerCase().includes(sensitive.toLowerCase()))
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
if (!involvesSensitiveData && !isInAuthContext(node)) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
context.report({
|
|
441
|
+
node,
|
|
442
|
+
messageId: 'insecureStringComparison',
|
|
443
|
+
data: {
|
|
444
|
+
filePath: filename,
|
|
445
|
+
line: String(node.loc?.start.line ?? 0),
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
// Check for early returns that could leak timing information
|
|
451
|
+
ReturnStatement(node: TSESTree.ReturnStatement) {
|
|
452
|
+
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
453
|
+
if (safetyChecker.isSafe(node, context)) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// FALSE POSITIVE REDUCTION: Skip if inside a function using timingSafeEqual
|
|
458
|
+
// Length check early returns are necessary and safe before timingSafeEqual
|
|
459
|
+
if (isInTimingSafeEqualContext(node)) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (!isEarlyReturnInAuthContext(node)) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Look for conditional returns (if/else returns)
|
|
468
|
+
let current: TSESTree.Node | undefined = node;
|
|
469
|
+
let isConditionalReturn = false;
|
|
470
|
+
|
|
471
|
+
while (current && !isConditionalReturn) {
|
|
472
|
+
if (current.type === 'IfStatement') {
|
|
473
|
+
isConditionalReturn = true;
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
current = current.parent as TSESTree.Node;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (!isConditionalReturn) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Check if this return involves sensitive data
|
|
484
|
+
const returnText = sourceCode.getText(node).toLowerCase();
|
|
485
|
+
const involvesSensitiveData = sensitiveVariables.some(sensitive =>
|
|
486
|
+
returnText.includes(sensitive)
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
if (!involvesSensitiveData && !isInAuthContext(node)) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
context.report({
|
|
494
|
+
node,
|
|
495
|
+
messageId: 'earlyReturnLeakage',
|
|
496
|
+
data: {
|
|
497
|
+
filePath: filename,
|
|
498
|
+
line: String(node.loc?.start.line ?? 0),
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
// Check function calls for timing-sensitive operations
|
|
504
|
+
CallExpression(node: TSESTree.CallExpression) {
|
|
505
|
+
const callee = node.callee;
|
|
506
|
+
|
|
507
|
+
// Check for insecure comparison functions
|
|
508
|
+
if (
|
|
509
|
+
callee.type === 'MemberExpression' &&
|
|
510
|
+
callee.property.type === 'Identifier' &&
|
|
511
|
+
['equals', 'compare', 'matches'].includes(callee.property.name)
|
|
512
|
+
) {
|
|
513
|
+
// Skip known timing-safe libraries
|
|
514
|
+
const objectName = callee.object.type === 'Identifier' ? callee.object.name : null;
|
|
515
|
+
if (objectName) {
|
|
516
|
+
// Known timing-safe comparison libraries
|
|
517
|
+
const timingSafeLibraries = ['bcrypt', 'crypto'];
|
|
518
|
+
if (timingSafeLibraries.includes(objectName) && callee.property.name === 'compare') {
|
|
519
|
+
return; // bcrypt.compare and crypto.compare are timing-safe
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Check if arguments involve sensitive data
|
|
524
|
+
const argsText = node.arguments
|
|
525
|
+
.map((arg: TSESTree.CallExpressionArgument) => sourceCode.getText(arg).toLowerCase())
|
|
526
|
+
.join(' ');
|
|
527
|
+
const involvesSensitiveData = sensitiveVariables.some(sensitive =>
|
|
528
|
+
argsText.includes(sensitive)
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
if (involvesSensitiveData || isInAuthContext(node)) {
|
|
532
|
+
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
533
|
+
if (safetyChecker.isSafe(node, context)) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
context.report({
|
|
538
|
+
node,
|
|
539
|
+
messageId: 'timingAttack',
|
|
540
|
+
data: {
|
|
541
|
+
filePath: filename,
|
|
542
|
+
line: String(node.loc?.start.line ?? 0),
|
|
543
|
+
severity: 'HIGH',
|
|
544
|
+
safeAlternative: 'Use constant-time comparison functions',
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
},
|
|
552
|
+
});
|