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
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noTimingAttack = void 0;
|
|
4
|
-
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
5
|
-
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
6
|
-
const eslint_devkit_3 = require("@interlace/eslint-devkit");
|
|
7
|
-
exports.noTimingAttack = (0, eslint_devkit_1.createRule)({
|
|
8
|
-
name: 'no-timing-attack',
|
|
9
|
-
meta: {
|
|
10
|
-
type: 'problem',
|
|
11
|
-
deprecated: true,
|
|
12
|
-
replacedBy: ['@see eslint-plugin-crypto/no-timing-unsafe-compare'],
|
|
13
|
-
docs: {
|
|
14
|
-
description: 'Detects timing attack vulnerabilities in authentication code',
|
|
15
|
-
},
|
|
16
|
-
fixable: 'code',
|
|
17
|
-
messages: {
|
|
18
|
-
timingAttack: (0, eslint_devkit_2.formatLLMMessage)({
|
|
19
|
-
icon: eslint_devkit_2.MessageIcons.SECURITY,
|
|
20
|
-
issueName: 'Timing Attack Vulnerability',
|
|
21
|
-
cwe: 'CWE-208',
|
|
22
|
-
description: 'Timing attack possible - execution time reveals secret information',
|
|
23
|
-
severity: '{{severity}}',
|
|
24
|
-
fix: '{{safeAlternative}}',
|
|
25
|
-
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
26
|
-
}),
|
|
27
|
-
insecureStringComparison: (0, eslint_devkit_2.formatLLMMessage)({
|
|
28
|
-
icon: eslint_devkit_2.MessageIcons.SECURITY,
|
|
29
|
-
issueName: 'Insecure String Comparison',
|
|
30
|
-
cwe: 'CWE-208',
|
|
31
|
-
description: 'String comparison may leak timing information',
|
|
32
|
-
severity: 'HIGH',
|
|
33
|
-
fix: 'Use crypto.timingSafeEqual() for comparing secrets',
|
|
34
|
-
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
35
|
-
}),
|
|
36
|
-
earlyReturnLeakage: (0, eslint_devkit_2.formatLLMMessage)({
|
|
37
|
-
icon: eslint_devkit_2.MessageIcons.SECURITY,
|
|
38
|
-
issueName: 'Early Return Timing Leak',
|
|
39
|
-
cwe: 'CWE-208',
|
|
40
|
-
description: 'Early return may leak information through timing',
|
|
41
|
-
severity: 'MEDIUM',
|
|
42
|
-
fix: 'Process all inputs consistently to avoid timing differences',
|
|
43
|
-
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
44
|
-
}),
|
|
45
|
-
useTimingSafeEqual: (0, eslint_devkit_2.formatLLMMessage)({
|
|
46
|
-
icon: eslint_devkit_2.MessageIcons.INFO,
|
|
47
|
-
issueName: 'Use Timing Safe Equal',
|
|
48
|
-
description: 'Use crypto.timingSafeEqual for constant-time comparison',
|
|
49
|
-
severity: 'LOW',
|
|
50
|
-
fix: 'crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))',
|
|
51
|
-
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
52
|
-
}),
|
|
53
|
-
useConstantTimeComparison: (0, eslint_devkit_2.formatLLMMessage)({
|
|
54
|
-
icon: eslint_devkit_2.MessageIcons.INFO,
|
|
55
|
-
issueName: 'Constant Time Comparison',
|
|
56
|
-
description: 'Implement constant-time comparison algorithm',
|
|
57
|
-
severity: 'LOW',
|
|
58
|
-
fix: 'Compare all bytes regardless of content',
|
|
59
|
-
documentationLink: 'https://en.wikipedia.org/wiki/Timing_attack',
|
|
60
|
-
}),
|
|
61
|
-
avoidEarlyReturns: (0, eslint_devkit_2.formatLLMMessage)({
|
|
62
|
-
icon: eslint_devkit_2.MessageIcons.INFO,
|
|
63
|
-
issueName: 'Avoid Early Returns',
|
|
64
|
-
description: 'Avoid early returns in security-sensitive code',
|
|
65
|
-
severity: 'LOW',
|
|
66
|
-
fix: 'Process all inputs before making decisions',
|
|
67
|
-
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
68
|
-
}),
|
|
69
|
-
strategyTimingSafe: (0, eslint_devkit_2.formatLLMMessage)({
|
|
70
|
-
icon: eslint_devkit_2.MessageIcons.STRATEGY,
|
|
71
|
-
issueName: 'Timing Safe Strategy',
|
|
72
|
-
description: 'Use built-in timing-safe comparison functions',
|
|
73
|
-
severity: 'LOW',
|
|
74
|
-
fix: 'Use crypto.timingSafeEqual or equivalent library functions',
|
|
75
|
-
documentationLink: 'https://nodejs.org/api/crypto.html#cryptotimingsafeequal',
|
|
76
|
-
}),
|
|
77
|
-
strategyConstantTime: (0, eslint_devkit_2.formatLLMMessage)({
|
|
78
|
-
icon: eslint_devkit_2.MessageIcons.STRATEGY,
|
|
79
|
-
issueName: 'Constant Time Strategy',
|
|
80
|
-
description: 'Implement custom constant-time comparison',
|
|
81
|
-
severity: 'LOW',
|
|
82
|
-
fix: 'Compare all characters/bytes with consistent timing',
|
|
83
|
-
documentationLink: 'https://en.wikipedia.org/wiki/Timing_attack',
|
|
84
|
-
}),
|
|
85
|
-
strategyConsistentTiming: (0, eslint_devkit_2.formatLLMMessage)({
|
|
86
|
-
icon: eslint_devkit_2.MessageIcons.STRATEGY,
|
|
87
|
-
issueName: 'Consistent Timing Strategy',
|
|
88
|
-
description: 'Ensure consistent execution time for all inputs',
|
|
89
|
-
severity: 'LOW',
|
|
90
|
-
fix: 'Pad inputs or use fixed-time operations',
|
|
91
|
-
documentationLink: 'https://cwe.mitre.org/data/definitions/208.html',
|
|
92
|
-
})
|
|
93
|
-
},
|
|
94
|
-
schema: [
|
|
95
|
-
{
|
|
96
|
-
type: 'object',
|
|
97
|
-
properties: {
|
|
98
|
-
authFunctions: {
|
|
99
|
-
type: 'array',
|
|
100
|
-
items: { type: 'string' },
|
|
101
|
-
default: ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'],
|
|
102
|
-
},
|
|
103
|
-
sensitiveVariables: {
|
|
104
|
-
type: 'array',
|
|
105
|
-
items: { type: 'string' },
|
|
106
|
-
default: ['password', 'token', 'secret', 'key', 'credentials', 'auth'],
|
|
107
|
-
},
|
|
108
|
-
allowEarlyReturns: {
|
|
109
|
-
type: 'boolean',
|
|
110
|
-
default: false,
|
|
111
|
-
description: 'Allow early returns outside security-sensitive contexts'
|
|
112
|
-
},
|
|
113
|
-
trustedSanitizers: {
|
|
114
|
-
type: 'array',
|
|
115
|
-
items: { type: 'string' },
|
|
116
|
-
default: [],
|
|
117
|
-
description: 'Additional function names to consider as timing-safe',
|
|
118
|
-
},
|
|
119
|
-
trustedAnnotations: {
|
|
120
|
-
type: 'array',
|
|
121
|
-
items: { type: 'string' },
|
|
122
|
-
default: [],
|
|
123
|
-
description: 'Additional JSDoc annotations to consider as timing-safe markers',
|
|
124
|
-
},
|
|
125
|
-
strictMode: {
|
|
126
|
-
type: 'boolean',
|
|
127
|
-
default: false,
|
|
128
|
-
description: 'Disable all false positive detection (strict mode)',
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
additionalProperties: false,
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
},
|
|
135
|
-
defaultOptions: [
|
|
136
|
-
{
|
|
137
|
-
authFunctions: ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'],
|
|
138
|
-
sensitiveVariables: ['password', 'token', 'secret', 'key', 'credentials', 'auth'],
|
|
139
|
-
allowEarlyReturns: false,
|
|
140
|
-
trustedSanitizers: ['sanitize'],
|
|
141
|
-
trustedAnnotations: ['@timing-safe'],
|
|
142
|
-
strictMode: false,
|
|
143
|
-
},
|
|
144
|
-
],
|
|
145
|
-
create(context) {
|
|
146
|
-
const options = context.options[0] || {};
|
|
147
|
-
const { authFunctions = ['authenticate', 'login', 'verifyPassword', 'checkToken', 'validateCredentials'], sensitiveVariables = ['password', 'token', 'secret', 'key', 'credentials', 'auth'], allowEarlyReturns = false, trustedSanitizers = [], trustedAnnotations = [], strictMode = false, } = options;
|
|
148
|
-
const sourceCode = context.sourceCode || context.sourceCode;
|
|
149
|
-
const filename = context.filename || context.getFilename();
|
|
150
|
-
// Create safety checker for false positive detection
|
|
151
|
-
const safetyChecker = (0, eslint_devkit_3.createSafetyChecker)({
|
|
152
|
-
trustedSanitizers,
|
|
153
|
-
trustedAnnotations,
|
|
154
|
-
trustedOrmPatterns: [],
|
|
155
|
-
strictMode,
|
|
156
|
-
});
|
|
157
|
-
// Pre-compute Set for O(1) lookups (performance optimization)
|
|
158
|
-
const sensitiveVarSet = new Set(sensitiveVariables.map(s => s.toLowerCase()));
|
|
159
|
-
const authFunctionSet = new Set(authFunctions.map(f => f.toLowerCase()));
|
|
160
|
-
// Track variables that contain sensitive data
|
|
161
|
-
const sensitiveVars = new Set();
|
|
162
|
-
/**
|
|
163
|
-
* Check if a variable name indicates sensitive/auth data
|
|
164
|
-
* Uses Set-based lookup for O(1) performance
|
|
165
|
-
*/
|
|
166
|
-
const isSensitiveVariable = (varName) => {
|
|
167
|
-
const lowerName = varName.toLowerCase();
|
|
168
|
-
// Check if any sensitive keyword is a substring
|
|
169
|
-
for (const sensitive of sensitiveVarSet) {
|
|
170
|
-
if (lowerName.includes(sensitive))
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
return false;
|
|
174
|
-
};
|
|
175
|
-
// Pre-compute auth patterns set for O(1) lookups
|
|
176
|
-
const authPatternSet = new Set(['auth', 'login', 'verify', 'token', 'password', 'credential', 'authenticate']);
|
|
177
|
-
/**
|
|
178
|
-
* Check if we're in an authentication/security context
|
|
179
|
-
* Uses Set-based lookups for O(1) performance
|
|
180
|
-
*/
|
|
181
|
-
const isInAuthContext = (node) => {
|
|
182
|
-
// Check if we're inside an authentication function
|
|
183
|
-
let current = node;
|
|
184
|
-
/* c8 ignore start -- defensive auth context detection */
|
|
185
|
-
while (current) {
|
|
186
|
-
if (current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration || current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionExpression || current.type === eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
187
|
-
const funcName = current.id?.name;
|
|
188
|
-
if (funcName) {
|
|
189
|
-
const lowerName = funcName.toLowerCase();
|
|
190
|
-
// Check exact matches first (O(1) with Set)
|
|
191
|
-
if (authFunctionSet.has(lowerName)) {
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
// Check pattern matches for common auth function names
|
|
195
|
-
for (const pattern of authPatternSet) {
|
|
196
|
-
if (lowerName.includes(pattern))
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
if (current.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression) {
|
|
202
|
-
const callee = current.callee;
|
|
203
|
-
if (callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
|
|
204
|
-
const lowerName = callee.name.toLowerCase();
|
|
205
|
-
if (authFunctionSet.has(lowerName)) {
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
// Check pattern matches
|
|
209
|
-
for (const pattern of authPatternSet) {
|
|
210
|
-
if (lowerName.includes(pattern))
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
current = current.parent;
|
|
216
|
-
}
|
|
217
|
-
// NOTE: Removed sensitiveVars.size check - it was causing false positives
|
|
218
|
-
// by flagging every function when ANY sensitive variable exists in the file.
|
|
219
|
-
// Instead, we now check sensitive data involvement at the specific point of use.
|
|
220
|
-
return false;
|
|
221
|
-
/* c8 ignore stop */
|
|
222
|
-
};
|
|
223
|
-
/**
|
|
224
|
-
* Check if a comparison is timing-safe
|
|
225
|
-
*/
|
|
226
|
-
const isTimingSafeComparison = (node) => {
|
|
227
|
-
// Check for crypto.timingSafeEqual calls
|
|
228
|
-
let current = node;
|
|
229
|
-
while (current) {
|
|
230
|
-
if (current.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression) {
|
|
231
|
-
const callee = current.callee;
|
|
232
|
-
if (callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
233
|
-
callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
234
|
-
callee.object.name === 'crypto' &&
|
|
235
|
-
callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
236
|
-
callee.property.name === 'timingSafeEqual') {
|
|
237
|
-
return true;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
current = current.parent;
|
|
241
|
-
}
|
|
242
|
-
return false;
|
|
243
|
-
};
|
|
244
|
-
/**
|
|
245
|
-
* Check if we're inside a function that uses crypto.timingSafeEqual.
|
|
246
|
-
* Length checks before timingSafeEqual are necessary and safe.
|
|
247
|
-
*
|
|
248
|
-
* Pattern:
|
|
249
|
-
* function safeCompare(a, b) {
|
|
250
|
-
* if (a.length !== b.length) return false; // <-- This is SAFE
|
|
251
|
-
* return crypto.timingSafeEqual(a, b);
|
|
252
|
-
* }
|
|
253
|
-
*/
|
|
254
|
-
const isInTimingSafeEqualContext = (node) => {
|
|
255
|
-
// Find enclosing function
|
|
256
|
-
let funcNode = node;
|
|
257
|
-
while (funcNode) {
|
|
258
|
-
if (funcNode.type === eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
259
|
-
funcNode.type === eslint_devkit_1.AST_NODE_TYPES.FunctionExpression ||
|
|
260
|
-
funcNode.type === eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
261
|
-
break;
|
|
262
|
-
}
|
|
263
|
-
funcNode = funcNode.parent;
|
|
264
|
-
}
|
|
265
|
-
if (!funcNode) {
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
// Check if the function body contains crypto.timingSafeEqual
|
|
269
|
-
const funcText = sourceCode.getText(funcNode);
|
|
270
|
-
if (funcText.includes('timingSafeEqual')) {
|
|
271
|
-
return true;
|
|
272
|
-
}
|
|
273
|
-
// Also check for common timing-safe comparison library patterns
|
|
274
|
-
const timingSafePatterns = [
|
|
275
|
-
'scmp', // secure-compare
|
|
276
|
-
'safe-compare',
|
|
277
|
-
'constant-time',
|
|
278
|
-
'constantTimeCompare',
|
|
279
|
-
];
|
|
280
|
-
if (timingSafePatterns.some(pattern => funcText.includes(pattern))) {
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
return false;
|
|
284
|
-
};
|
|
285
|
-
/**
|
|
286
|
-
* Check if a comparison involves only non-sensitive data like length checks
|
|
287
|
-
*/
|
|
288
|
-
const isLengthOrNumericComparison = (node) => {
|
|
289
|
-
const leftText = sourceCode.getText(node.left);
|
|
290
|
-
const rightText = sourceCode.getText(node.right);
|
|
291
|
-
// Check for .length comparisons
|
|
292
|
-
if (leftText.includes('.length') || rightText.includes('.length')) {
|
|
293
|
-
return true;
|
|
294
|
-
}
|
|
295
|
-
// Check for numeric literal comparisons
|
|
296
|
-
if (node.left.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof node.left.value === 'number') {
|
|
297
|
-
return true;
|
|
298
|
-
}
|
|
299
|
-
if (node.right.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof node.right.value === 'number') {
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
return false;
|
|
303
|
-
};
|
|
304
|
-
/**
|
|
305
|
-
* Check if early return is in a security-sensitive context
|
|
306
|
-
*/
|
|
307
|
-
const isEarlyReturnInAuthContext = (node) => {
|
|
308
|
-
// If early returns are explicitly allowed, don't flag them
|
|
309
|
-
if (allowEarlyReturns) {
|
|
310
|
-
/* c8 ignore next */
|
|
311
|
-
return false;
|
|
312
|
-
}
|
|
313
|
-
return isInAuthContext(node);
|
|
314
|
-
};
|
|
315
|
-
return {
|
|
316
|
-
// Track sensitive variable declarations
|
|
317
|
-
VariableDeclarator(node) {
|
|
318
|
-
if (node.id.type === 'Identifier' && isSensitiveVariable(node.id.name)) {
|
|
319
|
-
sensitiveVars.add(node.id.name);
|
|
320
|
-
}
|
|
321
|
-
// Also check if the variable is assigned sensitive data
|
|
322
|
-
if (node.init && node.id.type === 'Identifier') {
|
|
323
|
-
const initText = sourceCode.getText(node.init).toLowerCase();
|
|
324
|
-
if (sensitiveVariables.some(sensitive => initText.includes(sensitive))) {
|
|
325
|
-
sensitiveVars.add(node.id.name);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
// Check binary expressions for insecure comparisons
|
|
330
|
-
BinaryExpression(node) {
|
|
331
|
-
if (node.operator !== '===' && node.operator !== '==' &&
|
|
332
|
-
node.operator !== '!==' && node.operator !== '!=') {
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
// Skip if already using timing-safe comparison
|
|
336
|
-
if (isTimingSafeComparison(node)) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
// FALSE POSITIVE REDUCTION: Skip length/numeric comparisons in timing-safe contexts
|
|
340
|
-
// Pattern: if (a.length !== b.length) return false; before crypto.timingSafeEqual
|
|
341
|
-
if (isInTimingSafeEqualContext(node) && isLengthOrNumericComparison(node)) {
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
345
|
-
if (safetyChecker.isSafe(node, context)) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
// Check if either side involves sensitive data
|
|
349
|
-
const leftText = sourceCode.getText(node.left).toLowerCase();
|
|
350
|
-
const rightText = sourceCode.getText(node.right).toLowerCase();
|
|
351
|
-
const involvesSensitiveData = [leftText, rightText].some(text => sensitiveVariables.some(sensitive => text.toLowerCase().includes(sensitive.toLowerCase())));
|
|
352
|
-
if (!involvesSensitiveData && !isInAuthContext(node)) {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
context.report({
|
|
356
|
-
node,
|
|
357
|
-
messageId: 'insecureStringComparison',
|
|
358
|
-
data: {
|
|
359
|
-
filePath: filename,
|
|
360
|
-
line: String(node.loc?.start.line ?? 0),
|
|
361
|
-
},
|
|
362
|
-
});
|
|
363
|
-
},
|
|
364
|
-
// Check for early returns that could leak timing information
|
|
365
|
-
ReturnStatement(node) {
|
|
366
|
-
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
367
|
-
if (safetyChecker.isSafe(node, context)) {
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
// FALSE POSITIVE REDUCTION: Skip if inside a function using timingSafeEqual
|
|
371
|
-
// Length check early returns are necessary and safe before timingSafeEqual
|
|
372
|
-
if (isInTimingSafeEqualContext(node)) {
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
if (!isEarlyReturnInAuthContext(node)) {
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
// Look for conditional returns (if/else returns)
|
|
379
|
-
let current = node;
|
|
380
|
-
let isConditionalReturn = false;
|
|
381
|
-
while (current && !isConditionalReturn) {
|
|
382
|
-
if (current.type === 'IfStatement') {
|
|
383
|
-
isConditionalReturn = true;
|
|
384
|
-
break;
|
|
385
|
-
}
|
|
386
|
-
current = current.parent;
|
|
387
|
-
}
|
|
388
|
-
if (!isConditionalReturn) {
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
// Check if this return involves sensitive data
|
|
392
|
-
const returnText = sourceCode.getText(node).toLowerCase();
|
|
393
|
-
const involvesSensitiveData = sensitiveVariables.some(sensitive => returnText.includes(sensitive));
|
|
394
|
-
if (!involvesSensitiveData && !isInAuthContext(node)) {
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
context.report({
|
|
398
|
-
node,
|
|
399
|
-
messageId: 'earlyReturnLeakage',
|
|
400
|
-
data: {
|
|
401
|
-
filePath: filename,
|
|
402
|
-
line: String(node.loc?.start.line ?? 0),
|
|
403
|
-
},
|
|
404
|
-
});
|
|
405
|
-
},
|
|
406
|
-
// Check function calls for timing-sensitive operations
|
|
407
|
-
CallExpression(node) {
|
|
408
|
-
const callee = node.callee;
|
|
409
|
-
// Check for insecure comparison functions
|
|
410
|
-
if (callee.type === 'MemberExpression' &&
|
|
411
|
-
callee.property.type === 'Identifier' &&
|
|
412
|
-
['equals', 'compare', 'matches'].includes(callee.property.name)) {
|
|
413
|
-
// Skip known timing-safe libraries
|
|
414
|
-
const objectName = callee.object.type === 'Identifier' ? callee.object.name : null;
|
|
415
|
-
if (objectName) {
|
|
416
|
-
// Known timing-safe comparison libraries
|
|
417
|
-
const timingSafeLibraries = ['bcrypt', 'crypto'];
|
|
418
|
-
if (timingSafeLibraries.includes(objectName) && callee.property.name === 'compare') {
|
|
419
|
-
return; // bcrypt.compare and crypto.compare are timing-safe
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
// Check if arguments involve sensitive data
|
|
423
|
-
const argsText = node.arguments
|
|
424
|
-
.map((arg) => sourceCode.getText(arg).toLowerCase())
|
|
425
|
-
.join(' ');
|
|
426
|
-
const involvesSensitiveData = sensitiveVariables.some(sensitive => argsText.includes(sensitive));
|
|
427
|
-
if (involvesSensitiveData || isInAuthContext(node)) {
|
|
428
|
-
// FALSE POSITIVE REDUCTION: Skip if annotated as safe
|
|
429
|
-
if (safetyChecker.isSafe(node, context)) {
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
context.report({
|
|
433
|
-
node,
|
|
434
|
-
messageId: 'timingAttack',
|
|
435
|
-
data: {
|
|
436
|
-
filePath: filename,
|
|
437
|
-
line: String(node.loc?.start.line ?? 0),
|
|
438
|
-
severity: 'HIGH',
|
|
439
|
-
safeAlternative: 'Use constant-time comparison functions',
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
};
|
|
446
|
-
},
|
|
447
|
-
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export interface Options {
|
|
2
|
-
/** Ignore in test files. Default: true */
|
|
3
|
-
ignoreInTests?: boolean;
|
|
4
|
-
/** File system methods to check. Default: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'] */
|
|
5
|
-
fsMethods?: string[];
|
|
6
|
-
}
|
|
7
|
-
export declare const noToctouVulnerability: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noToctouVulnerability = void 0;
|
|
4
|
-
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
5
|
-
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
6
|
-
exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
|
|
7
|
-
name: 'no-toctou-vulnerability',
|
|
8
|
-
meta: {
|
|
9
|
-
type: 'problem',
|
|
10
|
-
docs: {
|
|
11
|
-
description: 'Detects Time-of-Check-Time-of-Use vulnerabilities',
|
|
12
|
-
},
|
|
13
|
-
hasSuggestions: true,
|
|
14
|
-
messages: {
|
|
15
|
-
toctouVulnerability: (0, eslint_devkit_1.formatLLMMessage)({
|
|
16
|
-
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
17
|
-
issueName: 'TOCTOU vulnerability',
|
|
18
|
-
cwe: 'CWE-367',
|
|
19
|
-
description: 'Time-of-check Time-of-use race condition detected',
|
|
20
|
-
severity: 'HIGH',
|
|
21
|
-
fix: 'Use atomic operations or fs.promises for file operations',
|
|
22
|
-
documentationLink: 'https://cwe.mitre.org/data/definitions/367.html',
|
|
23
|
-
}),
|
|
24
|
-
useAtomicOperations: (0, eslint_devkit_1.formatLLMMessage)({
|
|
25
|
-
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
26
|
-
issueName: 'Use Atomic Operations',
|
|
27
|
-
description: 'Use atomic file operations',
|
|
28
|
-
severity: 'LOW',
|
|
29
|
-
fix: 'fs.promises.access() then fs.promises.readFile()',
|
|
30
|
-
documentationLink: 'https://nodejs.org/api/fs.html#fspromisesaccesspath-mode',
|
|
31
|
-
}),
|
|
32
|
-
useFsPromises: (0, eslint_devkit_1.formatLLMMessage)({
|
|
33
|
-
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
34
|
-
issueName: 'Use fs.promises',
|
|
35
|
-
description: 'Use fs.promises API',
|
|
36
|
-
severity: 'LOW',
|
|
37
|
-
fix: 'await fs.promises.readFile() instead of sync operations',
|
|
38
|
-
documentationLink: 'https://nodejs.org/api/fs.html#promises-api',
|
|
39
|
-
}),
|
|
40
|
-
addProperLocking: (0, eslint_devkit_1.formatLLMMessage)({
|
|
41
|
-
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
42
|
-
issueName: 'Add File Locking',
|
|
43
|
-
description: 'Add proper locking mechanism',
|
|
44
|
-
severity: 'LOW',
|
|
45
|
-
fix: 'Use proper-lockfile or similar for concurrent access',
|
|
46
|
-
documentationLink: 'https://github.com/moxystudio/node-proper-lockfile',
|
|
47
|
-
}),
|
|
48
|
-
},
|
|
49
|
-
schema: [
|
|
50
|
-
{
|
|
51
|
-
type: 'object',
|
|
52
|
-
properties: {
|
|
53
|
-
ignoreInTests: {
|
|
54
|
-
type: 'boolean',
|
|
55
|
-
default: true,
|
|
56
|
-
},
|
|
57
|
-
fsMethods: {
|
|
58
|
-
type: 'array',
|
|
59
|
-
items: { type: 'string' },
|
|
60
|
-
default: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'],
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
additionalProperties: false,
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
},
|
|
67
|
-
defaultOptions: [
|
|
68
|
-
{
|
|
69
|
-
ignoreInTests: true,
|
|
70
|
-
fsMethods: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'],
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
create(context, [options = {}]) {
|
|
74
|
-
const { ignoreInTests = true } = options || {};
|
|
75
|
-
const filename = context.getFilename();
|
|
76
|
-
const isTestFile = ignoreInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
|
|
77
|
-
if (isTestFile) {
|
|
78
|
-
return {};
|
|
79
|
-
}
|
|
80
|
-
const sourceCode = context.sourceCode || context.sourceCode;
|
|
81
|
-
/**
|
|
82
|
-
* Check for TOCTOU patterns
|
|
83
|
-
*/
|
|
84
|
-
function checkCallExpression(node) {
|
|
85
|
-
// 1. Identify the file operation (Use)
|
|
86
|
-
let useMethodName = '';
|
|
87
|
-
if (node.callee.type === 'MemberExpression' && node.callee.property.type === 'Identifier') {
|
|
88
|
-
const objectName = node.callee.object.type === 'Identifier' ? node.callee.object.name : '';
|
|
89
|
-
if (objectName === 'fs' || objectName === 'fsPromises') {
|
|
90
|
-
useMethodName = node.callee.property.name;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
else if (node.callee.type === 'Identifier') {
|
|
94
|
-
useMethodName = node.callee.name;
|
|
95
|
-
}
|
|
96
|
-
const riskyUseMethods = ['readFileSync', 'writeFileSync', 'readFile', 'writeFile', 'openSync', 'open', 'unlinkSync', 'unlink'];
|
|
97
|
-
if (!riskyUseMethods.includes(useMethodName)) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const useArg = node.arguments[0];
|
|
101
|
-
if (!useArg)
|
|
102
|
-
return;
|
|
103
|
-
// 2. Walk up to find the condition (Check)
|
|
104
|
-
let current = node.parent;
|
|
105
|
-
while (current) {
|
|
106
|
-
if (current.type === 'IfStatement') {
|
|
107
|
-
// Extract the condition node
|
|
108
|
-
let condition = current.test;
|
|
109
|
-
// Handle negated condition: if (!exists(path)) { create(path) } -> also TOCTOU but different logic?
|
|
110
|
-
// Actually TOCTOU is usually Check(exists) -> Use(read).
|
|
111
|
-
// If (!exists) -> create is Check -> Use.
|
|
112
|
-
// But strict TOCTOU is checking state then acting.
|
|
113
|
-
// If checking for negation
|
|
114
|
-
if (condition.type === 'UnaryExpression' && condition.operator === '!') {
|
|
115
|
-
condition = condition.argument;
|
|
116
|
-
}
|
|
117
|
-
if (condition.type === 'CallExpression') {
|
|
118
|
-
// Check if it's a file check method
|
|
119
|
-
let checkMethodName = '';
|
|
120
|
-
if (condition.callee.type === 'MemberExpression' && condition.callee.property.type === 'Identifier') {
|
|
121
|
-
checkMethodName = condition.callee.property.name;
|
|
122
|
-
}
|
|
123
|
-
else if (condition.callee.type === 'Identifier') {
|
|
124
|
-
checkMethodName = condition.callee.name;
|
|
125
|
-
}
|
|
126
|
-
const checkMethods = ['existsSync', 'statSync', 'accessSync', 'exists', 'stat', 'access'];
|
|
127
|
-
if (checkMethods.includes(checkMethodName)) {
|
|
128
|
-
// Compare arguments
|
|
129
|
-
const checkArg = condition.arguments[0];
|
|
130
|
-
if (checkArg) {
|
|
131
|
-
// Method 1: Identifier match (same variable)
|
|
132
|
-
if (checkArg.type === 'Identifier' && useArg.type === 'Identifier' && checkArg.name === useArg.name) {
|
|
133
|
-
reportToctou(node);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
// Method 2: Text match (fallback)
|
|
137
|
-
const checkArgText = sourceCode.getText(checkArg).replace(/\s/g, '');
|
|
138
|
-
const useArgText = sourceCode.getText(useArg).replace(/\s/g, '');
|
|
139
|
-
if (checkArgText === useArgText) {
|
|
140
|
-
reportToctou(node);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// Handle stats.isFile() / stats.isDirectory() pattern
|
|
146
|
-
if (condition.callee.type === 'MemberExpression' &&
|
|
147
|
-
condition.callee.property.type === 'Identifier' &&
|
|
148
|
-
['isFile', 'isDirectory'].includes(condition.callee.property.name) &&
|
|
149
|
-
condition.callee.object.type === 'Identifier') {
|
|
150
|
-
const statsVarName = condition.callee.object.name;
|
|
151
|
-
let currentScope = sourceCode.getScope(condition);
|
|
152
|
-
let variable = null;
|
|
153
|
-
while (currentScope) {
|
|
154
|
-
variable = currentScope.variables.find(v => v.name === statsVarName);
|
|
155
|
-
if (variable)
|
|
156
|
-
break;
|
|
157
|
-
currentScope = currentScope.upper;
|
|
158
|
-
}
|
|
159
|
-
if (variable && variable.defs.length > 0) {
|
|
160
|
-
const def = variable.defs[0];
|
|
161
|
-
if (def.type === 'Variable' && def.node.init && def.node.init.type === 'CallExpression') {
|
|
162
|
-
const init = def.node.init;
|
|
163
|
-
if (init.callee.type === 'MemberExpression' &&
|
|
164
|
-
init.callee.property.type === 'Identifier' &&
|
|
165
|
-
['statSync', 'lstatSync', 'stat', 'lstat'].includes(init.callee.property.name)) {
|
|
166
|
-
const statArg = init.arguments[0];
|
|
167
|
-
if (statArg) {
|
|
168
|
-
const checkArgText = sourceCode.getText(statArg).replace(/\s/g, '');
|
|
169
|
-
const useArgText = sourceCode.getText(useArg).replace(/\s/g, '');
|
|
170
|
-
if (checkArgText === useArgText) {
|
|
171
|
-
reportToctou(node);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
current = current.parent;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
function reportToctou(node) {
|
|
185
|
-
context.report({
|
|
186
|
-
node,
|
|
187
|
-
messageId: 'toctouVulnerability',
|
|
188
|
-
suggest: [
|
|
189
|
-
{
|
|
190
|
-
messageId: 'useAtomicOperations',
|
|
191
|
-
fix: () => null,
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
messageId: 'useFsPromises',
|
|
195
|
-
fix: () => null,
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
messageId: 'addProperLocking',
|
|
199
|
-
fix: () => null,
|
|
200
|
-
},
|
|
201
|
-
],
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
return {
|
|
205
|
-
CallExpression: checkCallExpression,
|
|
206
|
-
};
|
|
207
|
-
},
|
|
208
|
-
});
|