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,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for detect-suspicious-dependencies
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { detectSuspiciousDependencies } from './index';
|
|
7
|
+
|
|
8
|
+
const ruleTester = new RuleTester({
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
ruleTester.run('detect-suspicious-dependencies', detectSuspiciousDependencies, {
|
|
16
|
+
valid: [
|
|
17
|
+
// Valid popular package names
|
|
18
|
+
{ code: "import React from 'react'" },
|
|
19
|
+
{ code: "import _ from 'lodash'" },
|
|
20
|
+
{ code: "import express from 'express'" },
|
|
21
|
+
// Local imports
|
|
22
|
+
{ code: "import foo from './foo'" },
|
|
23
|
+
// Scoped packages
|
|
24
|
+
{ code: "import pkg from '@scope/package'" },
|
|
25
|
+
],
|
|
26
|
+
|
|
27
|
+
invalid: [
|
|
28
|
+
// Typosquatting-like names (within 2 Levenshtein distance of popular packages)
|
|
29
|
+
{ code: "import r from 'reakt'", errors: [{ messageId: 'violationDetected' }] },
|
|
30
|
+
{ code: "import l from 'lodas'", errors: [{ messageId: 'violationDetected' }] },
|
|
31
|
+
],
|
|
32
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Detect potential typosquatting in dependencies
|
|
3
|
+
* @see https://owasp.org/www-project-mobile-top-10/
|
|
4
|
+
* @see https://cwe.mitre.org/data/definitions/506.html
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createRule, formatLLMMessage, MessageIcons } from '@interlace/eslint-devkit';
|
|
8
|
+
import type { TSESTree } from '@interlace/eslint-devkit';
|
|
9
|
+
|
|
10
|
+
type MessageIds = 'violationDetected';
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-empty-interface -- Rule has no configurable options
|
|
13
|
+
export interface Options {}
|
|
14
|
+
|
|
15
|
+
type RuleOptions = [Options?];
|
|
16
|
+
|
|
17
|
+
export const detectSuspiciousDependencies = createRule<RuleOptions, MessageIds>({
|
|
18
|
+
name: 'detect-suspicious-dependencies',
|
|
19
|
+
meta: {
|
|
20
|
+
type: 'problem',
|
|
21
|
+
docs: {
|
|
22
|
+
description: 'Detect typosquatting in package names',
|
|
23
|
+
},
|
|
24
|
+
messages: {
|
|
25
|
+
violationDetected: formatLLMMessage({
|
|
26
|
+
icon: MessageIcons.SECURITY,
|
|
27
|
+
issueName: 'Suspicious Dependency',
|
|
28
|
+
cwe: 'CWE-506',
|
|
29
|
+
description: 'Suspicious package name detected - possible typosquatting',
|
|
30
|
+
severity: 'HIGH',
|
|
31
|
+
fix: 'Verify package authenticity on npm registry',
|
|
32
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/506.html',
|
|
33
|
+
})
|
|
34
|
+
},
|
|
35
|
+
schema: [],
|
|
36
|
+
},
|
|
37
|
+
defaultOptions: [],
|
|
38
|
+
create(context) {
|
|
39
|
+
const popularPackages = ['react', 'lodash', 'express', 'axios', 'webpack'];
|
|
40
|
+
|
|
41
|
+
function levenshtein(a: string, b: string): number {
|
|
42
|
+
const matrix = [];
|
|
43
|
+
for (let i = 0; i <= b.length; i++) {
|
|
44
|
+
matrix[i] = [i];
|
|
45
|
+
}
|
|
46
|
+
for (let j = 0; j <= a.length; j++) {
|
|
47
|
+
matrix[0][j] = j;
|
|
48
|
+
}
|
|
49
|
+
for (let i = 1; i <= b.length; i++) {
|
|
50
|
+
for (let j = 1; j <= a.length; j++) {
|
|
51
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
52
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
53
|
+
} else {
|
|
54
|
+
matrix[i][j] = Math.min(
|
|
55
|
+
matrix[i - 1][j - 1] + 1,
|
|
56
|
+
matrix[i][j - 1] + 1,
|
|
57
|
+
matrix[i - 1][j] + 1
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return matrix[b.length][a.length];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
ImportDeclaration(node: TSESTree.ImportDeclaration) {
|
|
67
|
+
const source = node.source.value;
|
|
68
|
+
if (typeof source === 'string' && !source.startsWith('.') && !source.startsWith('@')) {
|
|
69
|
+
for (const popular of popularPackages) {
|
|
70
|
+
const distance = levenshtein(source, popular);
|
|
71
|
+
if (distance > 0 && distance <= 2) {
|
|
72
|
+
context.report({
|
|
73
|
+
node,
|
|
74
|
+
messageId: 'violationDetected',
|
|
75
|
+
data: { name: source, similar: popular },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for detect-weak-password-validation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { detectWeakPasswordValidation } from './index';
|
|
7
|
+
|
|
8
|
+
const ruleTester = new RuleTester({
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
ruleTester.run('detect-weak-password-validation', detectWeakPasswordValidation, {
|
|
16
|
+
valid: [
|
|
17
|
+
// Strong password requirements
|
|
18
|
+
{ code: "if (password.length >= 12) { valid() }" },
|
|
19
|
+
{ code: "if (pwd.length >= 8) { valid() }" },
|
|
20
|
+
// Non-password length checks
|
|
21
|
+
{ code: "if (name.length >= 2) { valid() }" },
|
|
22
|
+
{ code: "const x = 1" },
|
|
23
|
+
],
|
|
24
|
+
|
|
25
|
+
invalid: [
|
|
26
|
+
// Weak password requirements
|
|
27
|
+
{ code: "if (password.length >= 4) { accept() }", errors: [{ messageId: 'violationDetected' }] },
|
|
28
|
+
{ code: "if (pwd.length >= 6) { proceed() }", errors: [{ messageId: 'violationDetected' }] },
|
|
29
|
+
{ code: "if (pass.length > 3) { ok() }", errors: [{ messageId: 'violationDetected' }] },
|
|
30
|
+
],
|
|
31
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Identify weak password requirements
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createRule, formatLLMMessage, MessageIcons } from '@interlace/eslint-devkit';
|
|
6
|
+
import type { TSESTree } from '@interlace/eslint-devkit';
|
|
7
|
+
|
|
8
|
+
type MessageIds = 'violationDetected';
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-empty-interface -- Rule has no configurable options
|
|
11
|
+
export interface Options {}
|
|
12
|
+
|
|
13
|
+
type RuleOptions = [Options?];
|
|
14
|
+
|
|
15
|
+
export const detectWeakPasswordValidation = createRule<RuleOptions, MessageIds>({
|
|
16
|
+
name: 'detect-weak-password-validation',
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'problem',
|
|
19
|
+
docs: {
|
|
20
|
+
description: 'Identify weak password requirements',
|
|
21
|
+
},
|
|
22
|
+
messages: {
|
|
23
|
+
violationDetected: formatLLMMessage({
|
|
24
|
+
icon: MessageIcons.SECURITY,
|
|
25
|
+
issueName: 'Weak Password Validation',
|
|
26
|
+
cwe: 'CWE-521',
|
|
27
|
+
description: 'Password length requirement is too weak (less than 8 characters)',
|
|
28
|
+
severity: 'CRITICAL',
|
|
29
|
+
fix: 'Require at least 12 characters with complexity requirements',
|
|
30
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/521.html',
|
|
31
|
+
})
|
|
32
|
+
},
|
|
33
|
+
schema: [],
|
|
34
|
+
},
|
|
35
|
+
defaultOptions: [],
|
|
36
|
+
create(context) {
|
|
37
|
+
function report(node: TSESTree.Node) {
|
|
38
|
+
context.report({ node, messageId: 'violationDetected' });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
BinaryExpression(node: TSESTree.BinaryExpression) {
|
|
43
|
+
// Detect weak length requirements like password.length >= 4
|
|
44
|
+
if (['>=', '>', '==', '==='].includes(node.operator)) {
|
|
45
|
+
// Check if left side is .length
|
|
46
|
+
if (node.left.type === 'MemberExpression' &&
|
|
47
|
+
node.left.property.type === 'Identifier' &&
|
|
48
|
+
node.left.property.name === 'length') {
|
|
49
|
+
|
|
50
|
+
// Check if comparing to a weak number
|
|
51
|
+
if (node.right.type === 'Literal' &&
|
|
52
|
+
typeof node.right.value === 'number' &&
|
|
53
|
+
node.right.value < 8) {
|
|
54
|
+
|
|
55
|
+
// Check if variable name suggests password
|
|
56
|
+
if (node.left.object.type === 'Identifier') {
|
|
57
|
+
const varName = node.left.object.name.toLowerCase();
|
|
58
|
+
if (varName.includes('password') || varName.includes('pwd') || varName.includes('pass')) {
|
|
59
|
+
report(node);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prevent configuration allowing insecure loads
|
|
3
|
+
* @see https://owasp.org/www-project-mobile-top-10/
|
|
4
|
+
* @see https://cwe.mitre.org/data/definitions/749.html
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createRule, formatLLMMessage, MessageIcons } from '@interlace/eslint-devkit';
|
|
8
|
+
import type { TSESTree } from '@interlace/eslint-devkit';
|
|
9
|
+
|
|
10
|
+
type MessageIds = 'violationDetected';
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-empty-interface -- Rule has no configurable options
|
|
13
|
+
export interface Options {}
|
|
14
|
+
|
|
15
|
+
type RuleOptions = [Options?];
|
|
16
|
+
|
|
17
|
+
export const noAllowArbitraryLoads = createRule<RuleOptions, MessageIds>({
|
|
18
|
+
name: 'no-allow-arbitrary-loads',
|
|
19
|
+
meta: {
|
|
20
|
+
type: 'problem',
|
|
21
|
+
docs: {
|
|
22
|
+
description: 'Prevent configuration allowing insecure loads',
|
|
23
|
+
category: 'Security',
|
|
24
|
+
recommended: true,
|
|
25
|
+
owaspMobile: ['M5'],
|
|
26
|
+
cweIds: ["CWE-749"],
|
|
27
|
+
},
|
|
28
|
+
messages: {
|
|
29
|
+
violationDetected: formatLLMMessage({
|
|
30
|
+
icon: MessageIcons.SECURITY,
|
|
31
|
+
issueName: 'violation Detected',
|
|
32
|
+
cwe: 'CWE-295',
|
|
33
|
+
description: 'Prevent configuration allowing insecure loads detected - allowArbitraryLoads: true',
|
|
34
|
+
severity: 'HIGH',
|
|
35
|
+
fix: 'Review and apply secure practices',
|
|
36
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/295.html',
|
|
37
|
+
})
|
|
38
|
+
},
|
|
39
|
+
schema: [],
|
|
40
|
+
},
|
|
41
|
+
defaultOptions: [],
|
|
42
|
+
create(context) {
|
|
43
|
+
return {
|
|
44
|
+
Property(node: TSESTree.Property) {
|
|
45
|
+
if (node.key.type === 'Identifier' &&
|
|
46
|
+
node.key.name === 'allowArbitraryLoads' &&
|
|
47
|
+
node.value.type === 'Literal' &&
|
|
48
|
+
node.value.value === true) {
|
|
49
|
+
context.report({ node, messageId: 'violationDetected' });
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for no-allow-arbitrary-loads
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { noAllowArbitraryLoads } from './index';
|
|
7
|
+
|
|
8
|
+
const ruleTester = new RuleTester({
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
ruleTester.run('no-allow-arbitrary-loads', noAllowArbitraryLoads, {
|
|
16
|
+
valid: [
|
|
17
|
+
// Secure configuration
|
|
18
|
+
{ code: "const config = { allowArbitraryLoads: false }" },
|
|
19
|
+
{ code: "const settings = { secureMode: true }" },
|
|
20
|
+
{ code: "const x = 1" },
|
|
21
|
+
],
|
|
22
|
+
|
|
23
|
+
invalid: [
|
|
24
|
+
// Insecure configuration
|
|
25
|
+
{ code: "const config = { allowArbitraryLoads: true }", errors: [{ messageId: 'violationDetected' }] },
|
|
26
|
+
{ code: "module.exports = { NSAppTransportSecurity: { allowArbitraryLoads: true } }", errors: [{ messageId: 'violationDetected' }] },
|
|
27
|
+
],
|
|
28
|
+
});
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prevent file access from user input
|
|
3
|
+
*
|
|
4
|
+
* False Positive Reduction:
|
|
5
|
+
* This rule detects safe patterns including:
|
|
6
|
+
* - path.basename() sanitization
|
|
7
|
+
* - path.join() with validated base directories
|
|
8
|
+
* - startsWith() validation guards
|
|
9
|
+
* - Early-return throw patterns
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { AST_NODE_TYPES, createRule, formatLLMMessage, MessageIcons } from '@interlace/eslint-devkit';
|
|
13
|
+
import type { TSESTree } from '@interlace/eslint-devkit';
|
|
14
|
+
|
|
15
|
+
type MessageIds = 'violationDetected';
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-empty-interface -- Rule has no configurable options
|
|
18
|
+
export interface Options {}
|
|
19
|
+
|
|
20
|
+
type RuleOptions = [Options?];
|
|
21
|
+
|
|
22
|
+
export const noArbitraryFileAccess = createRule<RuleOptions, MessageIds>({
|
|
23
|
+
name: 'no-arbitrary-file-access',
|
|
24
|
+
meta: {
|
|
25
|
+
type: 'problem',
|
|
26
|
+
docs: {
|
|
27
|
+
description: 'Prevent file access from user input',
|
|
28
|
+
},
|
|
29
|
+
messages: {
|
|
30
|
+
violationDetected: formatLLMMessage({
|
|
31
|
+
icon: MessageIcons.SECURITY,
|
|
32
|
+
issueName: 'Arbitrary File Access',
|
|
33
|
+
cwe: 'CWE-22',
|
|
34
|
+
description: 'File path from user input - path traversal vulnerability',
|
|
35
|
+
severity: 'HIGH',
|
|
36
|
+
fix: 'Validate and sanitize file paths, use allowlists',
|
|
37
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/22.html',
|
|
38
|
+
})
|
|
39
|
+
},
|
|
40
|
+
schema: [],
|
|
41
|
+
},
|
|
42
|
+
defaultOptions: [],
|
|
43
|
+
create(context) {
|
|
44
|
+
const sourceCode = context.sourceCode;
|
|
45
|
+
|
|
46
|
+
function report(node: TSESTree.Node) {
|
|
47
|
+
context.report({ node, messageId: 'violationDetected' });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const fsReadMethods = ['readFile', 'readFileSync', 'readdir', 'readdirSync', 'stat', 'statSync'];
|
|
51
|
+
const fsWriteMethods = ['writeFile', 'writeFileSync', 'appendFile', 'appendFileSync'];
|
|
52
|
+
const userInputSources = ['req', 'request', 'params', 'query', 'body'];
|
|
53
|
+
|
|
54
|
+
// Track variables that have been sanitized with path.basename()
|
|
55
|
+
const sanitizedVariables = new Set<string>();
|
|
56
|
+
// Track variables that have been validated with startsWith() guards
|
|
57
|
+
const validatedVariables = new Set<string>();
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check if a variable is assigned from path.basename() or path.join() with basename
|
|
61
|
+
*/
|
|
62
|
+
function checkVariableDeclaration(node: TSESTree.VariableDeclarator) {
|
|
63
|
+
if (node.id.type !== 'Identifier' || !node.init) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const varName = node.id.name;
|
|
68
|
+
const init = node.init;
|
|
69
|
+
|
|
70
|
+
// Check for path.basename() assignment
|
|
71
|
+
if (init.type === 'CallExpression' &&
|
|
72
|
+
init.callee.type === 'MemberExpression' &&
|
|
73
|
+
init.callee.object.type === 'Identifier' &&
|
|
74
|
+
init.callee.object.name === 'path' &&
|
|
75
|
+
init.callee.property.type === 'Identifier' &&
|
|
76
|
+
init.callee.property.name === 'basename') {
|
|
77
|
+
sanitizedVariables.add(varName);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check for path.join() with a sanitized variable or literal base
|
|
81
|
+
if (init.type === 'CallExpression' &&
|
|
82
|
+
init.callee.type === 'MemberExpression' &&
|
|
83
|
+
init.callee.object.type === 'Identifier' &&
|
|
84
|
+
init.callee.object.name === 'path' &&
|
|
85
|
+
init.callee.property.type === 'Identifier' &&
|
|
86
|
+
init.callee.property.name === 'join') {
|
|
87
|
+
|
|
88
|
+
// Check if any argument is a sanitized variable
|
|
89
|
+
const hasSanitizedArg = init.arguments.some((arg: TSESTree.CallExpressionArgument) =>
|
|
90
|
+
arg.type === 'Identifier' && sanitizedVariables.has(arg.name)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Check if first arg is a safe base (literal or known safe variable)
|
|
94
|
+
const firstArg = init.arguments[0];
|
|
95
|
+
const hasSafeBase = firstArg && (
|
|
96
|
+
firstArg.type === 'Literal' ||
|
|
97
|
+
(firstArg.type === 'Identifier' && /^(SAFE|BASE|ROOT|UPLOAD|PUBLIC)/i.test(firstArg.name))
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (hasSanitizedArg && hasSafeBase) {
|
|
101
|
+
sanitizedVariables.add(varName);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if there's a startsWith() guard validation for this variable
|
|
108
|
+
* Looks for patterns like:
|
|
109
|
+
* if (!path.startsWith(baseDir)) { throw ... }
|
|
110
|
+
* if (!path.startsWith(baseDir)) { return ... }
|
|
111
|
+
*/
|
|
112
|
+
function hasStartsWithGuard(node: TSESTree.Node, varName: string): boolean {
|
|
113
|
+
// Already validated
|
|
114
|
+
if (validatedVariables.has(varName)) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Walk up to find the containing block or function
|
|
119
|
+
let current: TSESTree.Node | undefined = node.parent;
|
|
120
|
+
|
|
121
|
+
while (current) {
|
|
122
|
+
// If we've reached a function body or block, search its statements
|
|
123
|
+
if (current.type === AST_NODE_TYPES.BlockStatement) {
|
|
124
|
+
const statements = current.body;
|
|
125
|
+
|
|
126
|
+
// Look for IF statements in this block that validate our variable
|
|
127
|
+
for (const stmt of statements) {
|
|
128
|
+
if (stmt.type === AST_NODE_TYPES.IfStatement) {
|
|
129
|
+
const testText = sourceCode.getText(stmt.test).toLowerCase();
|
|
130
|
+
|
|
131
|
+
// Check for startsWith() validation pattern with our variable
|
|
132
|
+
if (testText.includes('startswith') && testText.includes(varName.toLowerCase())) {
|
|
133
|
+
// Check if this is a guard clause (negated condition with throw/return)
|
|
134
|
+
const consequent = stmt.consequent;
|
|
135
|
+
|
|
136
|
+
// Handle block statement: if (...) { throw/return; }
|
|
137
|
+
if (consequent.type === AST_NODE_TYPES.BlockStatement && consequent.body.length > 0) {
|
|
138
|
+
const firstStmt = consequent.body[0];
|
|
139
|
+
if (firstStmt.type === AST_NODE_TYPES.ThrowStatement || firstStmt.type === AST_NODE_TYPES.ReturnStatement) {
|
|
140
|
+
validatedVariables.add(varName);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Handle direct statement: if (...) throw/return;
|
|
146
|
+
if (consequent.type === AST_NODE_TYPES.ThrowStatement || consequent.type === AST_NODE_TYPES.ReturnStatement) {
|
|
147
|
+
validatedVariables.add(varName);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Also check if current IS an if statement (when node is inside the consequent)
|
|
156
|
+
if (current.type === AST_NODE_TYPES.IfStatement) {
|
|
157
|
+
const testText = sourceCode.getText(current.test).toLowerCase();
|
|
158
|
+
if (testText.includes('startswith') && testText.includes(varName.toLowerCase())) {
|
|
159
|
+
validatedVariables.add(varName);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
current = current.parent;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if a variable comes from a sanitized/validated source
|
|
172
|
+
*/
|
|
173
|
+
function isVariableSafe(varName: string, node: TSESTree.Node): boolean {
|
|
174
|
+
// Already tracked as sanitized
|
|
175
|
+
if (sanitizedVariables.has(varName)) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Has startsWith guard validation
|
|
180
|
+
if (hasStartsWithGuard(node, varName)) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Check naming conventions that suggest safety
|
|
185
|
+
if (/^(safe|sanitized|validated|clean)/i.test(varName)) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
// Track variable declarations for sanitization patterns
|
|
194
|
+
VariableDeclarator(node: TSESTree.VariableDeclarator) {
|
|
195
|
+
checkVariableDeclaration(node);
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
CallExpression(node: TSESTree.CallExpression) {
|
|
199
|
+
// Detect fs.* with user input
|
|
200
|
+
if (node.callee.type === 'MemberExpression' &&
|
|
201
|
+
node.callee.object.type === 'Identifier' &&
|
|
202
|
+
node.callee.object.name === 'fs' &&
|
|
203
|
+
node.callee.property.type === 'Identifier' &&
|
|
204
|
+
[...fsReadMethods, ...fsWriteMethods].includes(node.callee.property.name)) {
|
|
205
|
+
|
|
206
|
+
const pathArg = node.arguments[0];
|
|
207
|
+
|
|
208
|
+
// Skip if path is a literal (safe)
|
|
209
|
+
if (pathArg && pathArg.type === 'Literal') {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check if path is a variable
|
|
214
|
+
if (pathArg && pathArg.type === 'Identifier') {
|
|
215
|
+
const varName = pathArg.name;
|
|
216
|
+
|
|
217
|
+
// Skip if variable is sanitized or validated
|
|
218
|
+
if (isVariableSafe(varName, node)) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
report(node);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Flag if path is from a member expression (user input sources)
|
|
227
|
+
if (pathArg?.type === 'MemberExpression' &&
|
|
228
|
+
pathArg.object.type === 'Identifier') {
|
|
229
|
+
const objName = pathArg.object.name.toLowerCase();
|
|
230
|
+
if (userInputSources.includes(objName)) {
|
|
231
|
+
report(node);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for no-arbitrary-file-access
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { noArbitraryFileAccess } from './index';
|
|
7
|
+
|
|
8
|
+
const ruleTester = new RuleTester({
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
ruleTester.run('no-arbitrary-file-access', noArbitraryFileAccess, {
|
|
16
|
+
valid: [
|
|
17
|
+
// Static file paths
|
|
18
|
+
{ code: "fs.readFileSync('./config.json')" },
|
|
19
|
+
{ code: "fs.writeFile('/app/data/log.txt', data, cb)" },
|
|
20
|
+
{ code: "fs.readdir('/safe/path')" },
|
|
21
|
+
{ code: "fs.stat('/known/file.txt')" },
|
|
22
|
+
// Non-fs code
|
|
23
|
+
{ code: "const x = 1" },
|
|
24
|
+
{ code: "other.readFile(path)" },
|
|
25
|
+
|
|
26
|
+
// ============================================
|
|
27
|
+
// FALSE POSITIVE PREVENTION TESTS
|
|
28
|
+
// ============================================
|
|
29
|
+
|
|
30
|
+
// FP-1: path.basename() sanitization
|
|
31
|
+
{
|
|
32
|
+
code: `
|
|
33
|
+
const safeName = path.basename(userFilename);
|
|
34
|
+
fs.readFileSync(safeName);
|
|
35
|
+
`,
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// FP-2: path.basename() + path.join() with safe base
|
|
39
|
+
{
|
|
40
|
+
code: `
|
|
41
|
+
const safeName = path.basename(userFilename);
|
|
42
|
+
const safePath = path.join(SAFE_DIR, safeName);
|
|
43
|
+
fs.readFileSync(safePath);
|
|
44
|
+
`,
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// FP-3: startsWith() validation guard with throw
|
|
48
|
+
{
|
|
49
|
+
code: `
|
|
50
|
+
function readFile(userPath) {
|
|
51
|
+
const filePath = path.join('/uploads', userPath);
|
|
52
|
+
if (!filePath.startsWith('/uploads')) {
|
|
53
|
+
throw new Error('Invalid path');
|
|
54
|
+
}
|
|
55
|
+
return fs.readFileSync(filePath);
|
|
56
|
+
}
|
|
57
|
+
`,
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// FP-4: startsWith() validation guard with return
|
|
61
|
+
{
|
|
62
|
+
code: `
|
|
63
|
+
function readFile(userPath) {
|
|
64
|
+
const filePath = path.join(baseDir, userPath);
|
|
65
|
+
if (!filePath.startsWith(baseDir)) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return fs.readFileSync(filePath);
|
|
69
|
+
}
|
|
70
|
+
`,
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// FP-5: Variables with safe naming conventions
|
|
74
|
+
{ code: "fs.readFileSync(safePath)" },
|
|
75
|
+
{ code: "fs.readFileSync(sanitizedPath)" },
|
|
76
|
+
{ code: "fs.readFileSync(validatedFilename)" },
|
|
77
|
+
{ code: "fs.readFileSync(cleanPath)" },
|
|
78
|
+
|
|
79
|
+
// FP-6: Combined pattern (real-world safe pattern from safe-patterns.js)
|
|
80
|
+
{
|
|
81
|
+
code: `
|
|
82
|
+
const SAFE_DIR = path.resolve(__dirname, 'uploads');
|
|
83
|
+
function safeReadFile(userFilename) {
|
|
84
|
+
const safeName = path.basename(userFilename);
|
|
85
|
+
const safePath = path.join(SAFE_DIR, safeName);
|
|
86
|
+
if (!safePath.startsWith(SAFE_DIR)) {
|
|
87
|
+
throw new Error('Invalid path');
|
|
88
|
+
}
|
|
89
|
+
return fs.readFileSync(safePath);
|
|
90
|
+
}
|
|
91
|
+
`,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
|
|
95
|
+
invalid: [
|
|
96
|
+
// Variable file paths - all fs read methods
|
|
97
|
+
{ code: "fs.readFileSync(filePath)", errors: [{ messageId: 'violationDetected' }] },
|
|
98
|
+
{ code: "fs.readFile(userFile, cb)", errors: [{ messageId: 'violationDetected' }] },
|
|
99
|
+
{ code: "fs.readdir(userDir)", errors: [{ messageId: 'violationDetected' }] },
|
|
100
|
+
{ code: "fs.readdirSync(scanPath)", errors: [{ messageId: 'violationDetected' }] },
|
|
101
|
+
{ code: "fs.stat(targetPath)", errors: [{ messageId: 'violationDetected' }] },
|
|
102
|
+
{ code: "fs.statSync(checkPath)", errors: [{ messageId: 'violationDetected' }] },
|
|
103
|
+
// Variable file paths - all fs write methods
|
|
104
|
+
{ code: "fs.writeFile(outputPath, data, cb)", errors: [{ messageId: 'violationDetected' }] },
|
|
105
|
+
{ code: "fs.writeFileSync(destPath, content)", errors: [{ messageId: 'violationDetected' }] },
|
|
106
|
+
{ code: "fs.appendFile(logPath, text, cb)", errors: [{ messageId: 'violationDetected' }] },
|
|
107
|
+
{ code: "fs.appendFileSync(filePath, data)", errors: [{ messageId: 'violationDetected' }] },
|
|
108
|
+
// User input from req object
|
|
109
|
+
{ code: "fs.readFile(req.file, cb)", errors: [{ messageId: 'violationDetected' }] },
|
|
110
|
+
// User input from request object
|
|
111
|
+
{ code: "fs.readFile(request.path, cb)", errors: [{ messageId: 'violationDetected' }] },
|
|
112
|
+
// User input from params object
|
|
113
|
+
{ code: "fs.readFileSync(params.filename)", errors: [{ messageId: 'violationDetected' }] },
|
|
114
|
+
// User input from query object
|
|
115
|
+
{ code: "fs.readFile(query.file, cb)", errors: [{ messageId: 'violationDetected' }] },
|
|
116
|
+
// User input from body object
|
|
117
|
+
{ code: "fs.writeFile(body.path, data, cb)", errors: [{ messageId: 'violationDetected' }] },
|
|
118
|
+
],
|
|
119
|
+
});
|