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,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive tests for no-clickjacking rule
|
|
3
|
+
* Security: CWE-1021 (Improper Restriction of Rendered UI Layers or Frames)
|
|
4
|
+
*/
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { describe, it, afterAll } from 'vitest';
|
|
7
|
+
import parser from '@typescript-eslint/parser';
|
|
8
|
+
import { noClickjacking } from './index';
|
|
9
|
+
|
|
10
|
+
// Configure RuleTester for Vitest
|
|
11
|
+
RuleTester.afterAll = afterAll;
|
|
12
|
+
RuleTester.it = it;
|
|
13
|
+
RuleTester.itOnly = it.only;
|
|
14
|
+
RuleTester.describe = describe;
|
|
15
|
+
|
|
16
|
+
// Use Flat Config format (ESLint 9+) with JSX support
|
|
17
|
+
const ruleTester = new RuleTester({
|
|
18
|
+
languageOptions: {
|
|
19
|
+
parser,
|
|
20
|
+
ecmaVersion: 2022,
|
|
21
|
+
sourceType: 'module',
|
|
22
|
+
parserOptions: {
|
|
23
|
+
ecmaFeatures: {
|
|
24
|
+
jsx: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('no-clickjacking', () => {
|
|
31
|
+
describe('Valid Code', () => {
|
|
32
|
+
ruleTester.run('valid - protected against clickjacking', noClickjacking, {
|
|
33
|
+
valid: [
|
|
34
|
+
// Trusted iframe sources (starts with /)
|
|
35
|
+
{
|
|
36
|
+
code: '<iframe src="/local-content.html"></iframe>',
|
|
37
|
+
},
|
|
38
|
+
// Proper CSP (would be set server-side) - no UI elements
|
|
39
|
+
{
|
|
40
|
+
code: '// CSP: frame-ancestors \'self\'; const x = 1;',
|
|
41
|
+
},
|
|
42
|
+
// Code without UI elements doesn't require frame-busting
|
|
43
|
+
{
|
|
44
|
+
code: 'const data = { name: "test" };',
|
|
45
|
+
},
|
|
46
|
+
// Frame-busting detection (marks hasFrameBusting=true)
|
|
47
|
+
{
|
|
48
|
+
code: 'if (top != self) { console.log("framed"); }',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
invalid: [],
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('Invalid Code - Missing Frame Busting', () => {
|
|
56
|
+
// Note: missingFrameBusting now only triggers on entry point files (index/app/page.tsx/jsx)
|
|
57
|
+
// These tests use the default test filename which is not an entry point
|
|
58
|
+
ruleTester.run('valid - non-entry-point files skip frame-busting check', noClickjacking, {
|
|
59
|
+
valid: [
|
|
60
|
+
// Code with button (UI element) but no frame-busting - now valid since not entry point
|
|
61
|
+
{
|
|
62
|
+
code: 'const x = 1; function handleClick() {} button;',
|
|
63
|
+
},
|
|
64
|
+
// Code with onClick handler but no frame-busting - now valid since not entry point
|
|
65
|
+
{
|
|
66
|
+
code: 'element.onClick = handler;',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
invalid: [],
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('Invalid Code - Unsafe iframe Usage', () => {
|
|
74
|
+
ruleTester.run('invalid - unsafe iframe sources', noClickjacking, {
|
|
75
|
+
valid: [],
|
|
76
|
+
invalid: [
|
|
77
|
+
// External HTTP source is untrusted
|
|
78
|
+
// missingFrameBusting only triggers for UI elements (button|input|form|a|div)
|
|
79
|
+
{
|
|
80
|
+
code: '<iframe src="http://external-site.com"></iframe>',
|
|
81
|
+
errors: [
|
|
82
|
+
{
|
|
83
|
+
messageId: 'unsafeIframeUsage',
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
// HTTPS untrusted source
|
|
88
|
+
{
|
|
89
|
+
code: '<iframe src="https://untrusted.com/widget"></iframe>',
|
|
90
|
+
errors: [
|
|
91
|
+
{
|
|
92
|
+
messageId: 'unsafeIframeUsage',
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('Invalid Code - Frame Manipulation', () => {
|
|
101
|
+
ruleTester.run('invalid - dangerous frame manipulation', noClickjacking, {
|
|
102
|
+
valid: [],
|
|
103
|
+
invalid: [
|
|
104
|
+
// Direct assignment to top.location
|
|
105
|
+
// missingFrameBusting only triggers for UI elements
|
|
106
|
+
{
|
|
107
|
+
code: 'top.location = "http://evil.com";',
|
|
108
|
+
errors: [
|
|
109
|
+
{
|
|
110
|
+
messageId: 'frameManipulation',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
// Assignment to window.location
|
|
115
|
+
{
|
|
116
|
+
code: 'window.location = "http://evil.com";',
|
|
117
|
+
errors: [
|
|
118
|
+
{
|
|
119
|
+
messageId: 'frameManipulation',
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('Invalid Code - Transparent Overlays', () => {
|
|
128
|
+
ruleTester.run('invalid - transparent elements that could hide attacks', noClickjacking, {
|
|
129
|
+
valid: [],
|
|
130
|
+
invalid: [
|
|
131
|
+
// Literal must include 'style=' or 'css' to trigger
|
|
132
|
+
// missingFrameBusting only triggers for UI elements
|
|
133
|
+
{
|
|
134
|
+
code: 'const css = "style=opacity: 0; position: absolute; top: 0; left: 0;";',
|
|
135
|
+
errors: [
|
|
136
|
+
{
|
|
137
|
+
messageId: 'transparentFrameOverlay',
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
// cssText with visibility hidden
|
|
142
|
+
{
|
|
143
|
+
code: 'element.cssText = "css visibility: hidden; z-index: -1;";',
|
|
144
|
+
errors: [
|
|
145
|
+
{
|
|
146
|
+
messageId: 'transparentFrameOverlay',
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
// Template literal with 'style' keyword
|
|
151
|
+
{
|
|
152
|
+
code: 'const style = `style opacity: 0; position: absolute; top: 0; left: 0;`;',
|
|
153
|
+
errors: [
|
|
154
|
+
{
|
|
155
|
+
messageId: 'transparentFrameOverlay',
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('Valid Code - False Positives Reduced', () => {
|
|
164
|
+
ruleTester.run('valid - false positives reduced', noClickjacking, {
|
|
165
|
+
valid: [
|
|
166
|
+
// Trusted sources (starts with /)
|
|
167
|
+
{
|
|
168
|
+
code: '<iframe src="/local-content.html"></iframe>',
|
|
169
|
+
},
|
|
170
|
+
// Safe frame-busting comparison (doesn't assign)
|
|
171
|
+
{
|
|
172
|
+
code: 'if (top !== self) { console.log("framed"); }',
|
|
173
|
+
},
|
|
174
|
+
// String without 'style=' or 'css' doesn't trigger overlay check
|
|
175
|
+
{
|
|
176
|
+
code: 'const loadingStyle = "opacity: 0; transition: opacity 0.3s;";',
|
|
177
|
+
},
|
|
178
|
+
// Disabled frame-busting requirement
|
|
179
|
+
{
|
|
180
|
+
code: '<button onClick={handleClick}>Click me</button>',
|
|
181
|
+
options: [{ requireFrameBusting: false }],
|
|
182
|
+
},
|
|
183
|
+
// Code without UI elements
|
|
184
|
+
{
|
|
185
|
+
code: 'const data = 123;',
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
invalid: [],
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('Configuration Options', () => {
|
|
193
|
+
ruleTester.run('config - trusted sources', noClickjacking, {
|
|
194
|
+
valid: [
|
|
195
|
+
// Trusted source configured - no unsafeIframeUsage
|
|
196
|
+
// iframe alone doesn't trigger missingFrameBusting
|
|
197
|
+
{
|
|
198
|
+
code: '<iframe src="https://trusted.com"></iframe>',
|
|
199
|
+
options: [{ trustedSources: ['https://trusted.com'] }],
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
invalid: [
|
|
203
|
+
// Untrusted source - only unsafeIframeUsage (no UI elements for missingFrameBusting)
|
|
204
|
+
{
|
|
205
|
+
code: '<iframe src="https://untrusted.com"></iframe>',
|
|
206
|
+
options: [{ trustedSources: ['https://trusted.com'] }],
|
|
207
|
+
errors: [
|
|
208
|
+
{
|
|
209
|
+
messageId: 'unsafeIframeUsage',
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
ruleTester.run('config - disable frame-busting requirement', noClickjacking, {
|
|
217
|
+
valid: [
|
|
218
|
+
// With requireFrameBusting=false, UI elements don't trigger missingFrameBusting
|
|
219
|
+
{
|
|
220
|
+
code: '<form><input type="text" /><button>Submit</button></form>',
|
|
221
|
+
options: [{ requireFrameBusting: false }],
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
invalid: [],
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('Complex Clickjacking Scenarios', () => {
|
|
229
|
+
ruleTester.run('complex - real-world clickjacking attack patterns', noClickjacking, {
|
|
230
|
+
valid: [],
|
|
231
|
+
invalid: [
|
|
232
|
+
// Untrusted iframe source - only unsafeIframeUsage (iframe doesn't count as UI element)
|
|
233
|
+
{
|
|
234
|
+
code: '<iframe src="https://untrusted-social-widget.com/like" width="200" height="50" />',
|
|
235
|
+
errors: [
|
|
236
|
+
{
|
|
237
|
+
messageId: 'unsafeIframeUsage',
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
// Frame manipulation attempt - only frameManipulation
|
|
242
|
+
{
|
|
243
|
+
code: 'window.location = "https://evil.com";',
|
|
244
|
+
errors: [
|
|
245
|
+
{
|
|
246
|
+
messageId: 'frameManipulation',
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prevent authentication logic in client code
|
|
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 noClientSideAuthLogic = createRule<RuleOptions, MessageIds>({
|
|
16
|
+
name: 'no-client-side-auth-logic',
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'problem',
|
|
19
|
+
docs: {
|
|
20
|
+
description: 'Prevent authentication logic in client code',
|
|
21
|
+
},
|
|
22
|
+
messages: {
|
|
23
|
+
violationDetected: formatLLMMessage({
|
|
24
|
+
icon: MessageIcons.SECURITY,
|
|
25
|
+
issueName: 'Client-Side Auth Logic',
|
|
26
|
+
cwe: 'CWE-602',
|
|
27
|
+
description: 'Authentication logic in client code - easily bypassed',
|
|
28
|
+
severity: 'CRITICAL',
|
|
29
|
+
fix: 'Move authentication checks to the server',
|
|
30
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/602.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
|
+
const authKeywords = ['admin', 'authenticated', 'authorized', 'isAdmin', 'isAuthenticated', 'role'];
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
IfStatement(node: TSESTree.IfStatement) {
|
|
45
|
+
// Detect role/auth checks from localStorage
|
|
46
|
+
if (node.test.type === 'CallExpression' &&
|
|
47
|
+
node.test.callee.type === 'MemberExpression' &&
|
|
48
|
+
node.test.callee.object.type === 'Identifier' &&
|
|
49
|
+
node.test.callee.object.name === 'localStorage' &&
|
|
50
|
+
node.test.callee.property.type === 'Identifier' &&
|
|
51
|
+
node.test.callee.property.name === 'getItem') {
|
|
52
|
+
|
|
53
|
+
const keyArg = node.test.arguments[0];
|
|
54
|
+
if (keyArg && keyArg.type === 'Literal') {
|
|
55
|
+
const key = String(keyArg.value).toLowerCase();
|
|
56
|
+
if (authKeywords.some(kw => key.includes(kw))) {
|
|
57
|
+
report(node);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Detect password comparison
|
|
63
|
+
if (node.test.type === 'BinaryExpression') {
|
|
64
|
+
const checkMember = (expr: TSESTree.Expression) => {
|
|
65
|
+
if (expr.type === 'MemberExpression' &&
|
|
66
|
+
expr.property.type === 'Identifier' &&
|
|
67
|
+
['password', 'secret', 'token'].includes(expr.property.name)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (checkMember(node.test.left as TSESTree.Expression) ||
|
|
74
|
+
checkMember(node.test.right as TSESTree.Expression)) {
|
|
75
|
+
report(node);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for no-client-side-auth-logic
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { noClientSideAuthLogic } from './index';
|
|
7
|
+
|
|
8
|
+
const ruleTester = new RuleTester({
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
ruleTester.run('no-client-side-auth-logic', noClientSideAuthLogic, {
|
|
16
|
+
valid: [
|
|
17
|
+
// Server-side auth checks
|
|
18
|
+
{ code: "if (await verifyToken(token)) { proceed() }" },
|
|
19
|
+
{ code: "const isAuth = await authService.verify()" },
|
|
20
|
+
// Non-auth localStorage usage
|
|
21
|
+
{ code: "if (localStorage.getItem('theme')) { setTheme() }" },
|
|
22
|
+
{ code: "const x = 1" },
|
|
23
|
+
],
|
|
24
|
+
|
|
25
|
+
invalid: [
|
|
26
|
+
// Local storage auth checks
|
|
27
|
+
{ code: "if (localStorage.getItem('isAdmin')) { showAdmin() }", errors: [{ messageId: 'violationDetected' }] },
|
|
28
|
+
{ code: "if (localStorage.getItem('authenticated')) { proceed() }", errors: [{ messageId: 'violationDetected' }] },
|
|
29
|
+
{ code: "if (localStorage.getItem('role')) { checkRole() }", errors: [{ messageId: 'violationDetected' }] },
|
|
30
|
+
// Password comparison
|
|
31
|
+
{ code: "if (user.password === input) { login() }", errors: [{ messageId: 'violationDetected' }] },
|
|
32
|
+
],
|
|
33
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow credentials in URL query parameters
|
|
3
|
+
* @see https://owasp.org/www-project-mobile-top-10/
|
|
4
|
+
* @see https://cwe.mitre.org/data/definitions/598.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 noCredentialsInQueryParams = createRule<RuleOptions, MessageIds>({
|
|
18
|
+
name: 'no-credentials-in-query-params',
|
|
19
|
+
meta: {
|
|
20
|
+
type: 'problem',
|
|
21
|
+
docs: {
|
|
22
|
+
description: 'Disallow credentials in URL query parameters',
|
|
23
|
+
},
|
|
24
|
+
messages: {
|
|
25
|
+
violationDetected: formatLLMMessage({
|
|
26
|
+
icon: MessageIcons.SECURITY,
|
|
27
|
+
issueName: 'Credentials in Query Parameters',
|
|
28
|
+
cwe: 'CWE-798',
|
|
29
|
+
description: 'Credentials detected in URL query parameters - this is a security risk',
|
|
30
|
+
severity: 'CRITICAL',
|
|
31
|
+
fix: 'Use secure methods: POST body, headers (Authorization), or secure cookies',
|
|
32
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/798.html',
|
|
33
|
+
})
|
|
34
|
+
},
|
|
35
|
+
schema: [],
|
|
36
|
+
},
|
|
37
|
+
defaultOptions: [],
|
|
38
|
+
create(context) {
|
|
39
|
+
const sourceCode = context.sourceCode;
|
|
40
|
+
const sensitiveParams = ['password=', 'token=', 'apikey=', 'secret=', 'auth='];
|
|
41
|
+
|
|
42
|
+
function report(node: TSESTree.Node) {
|
|
43
|
+
context.report({
|
|
44
|
+
node,
|
|
45
|
+
messageId: 'violationDetected',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
Literal(node: TSESTree.Literal) {
|
|
51
|
+
if (typeof node.value === 'string') {
|
|
52
|
+
const url = node.value.toLowerCase();
|
|
53
|
+
|
|
54
|
+
if (sensitiveParams.some(param => url.includes('?' + param) || url.includes('&' + param))) {
|
|
55
|
+
report(node);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
TemplateLiteral(node: TSESTree.TemplateLiteral) {
|
|
61
|
+
const text = sourceCode.getText(node).toLowerCase();
|
|
62
|
+
|
|
63
|
+
if (sensitiveParams.some(param => text.includes(param))) {
|
|
64
|
+
report(node);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for no-credentials-in-query-params
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { noCredentialsInQueryParams } from './index';
|
|
7
|
+
|
|
8
|
+
const ruleTester = new RuleTester({
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
ruleTester.run('no-credentials-in-query-params', noCredentialsInQueryParams, {
|
|
16
|
+
valid: [
|
|
17
|
+
// Safe URLs without credentials
|
|
18
|
+
{ code: "const url = 'https://api.example.com/data'" },
|
|
19
|
+
{ code: "const url = 'https://api.example.com?user=john'" },
|
|
20
|
+
{ code: "fetch('https://api.example.com/users')" },
|
|
21
|
+
{ code: "const x = 1" },
|
|
22
|
+
],
|
|
23
|
+
|
|
24
|
+
invalid: [
|
|
25
|
+
// URLs with credentials in query params
|
|
26
|
+
{ code: "const url = 'https://api.example.com?password=secret123'", errors: [{ messageId: 'violationDetected' }] },
|
|
27
|
+
{ code: "const url = 'https://api.example.com?token=abc123'", errors: [{ messageId: 'violationDetected' }] },
|
|
28
|
+
{ code: "fetch('https://api.example.com?apikey=xyz789')", errors: [{ messageId: 'violationDetected' }] },
|
|
29
|
+
{ code: "const url = 'https://api.com?user=john&password=xyz'", errors: [{ messageId: 'violationDetected' }] },
|
|
30
|
+
// Template literals with credentials
|
|
31
|
+
{ code: "const url = `https://api.com?token=${token}`", errors: [{ messageId: 'violationDetected' }] },
|
|
32
|
+
],
|
|
33
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow storing credentials in browser/mobile storage APIs
|
|
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 noCredentialsInStorageApi = createRule<RuleOptions, MessageIds>({
|
|
16
|
+
name: 'no-credentials-in-storage-api',
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'problem',
|
|
19
|
+
docs: {
|
|
20
|
+
description: 'Disallow storing credentials in browser/mobile storage APIs',
|
|
21
|
+
},
|
|
22
|
+
messages: {
|
|
23
|
+
violationDetected: formatLLMMessage({
|
|
24
|
+
icon: MessageIcons.SECURITY,
|
|
25
|
+
issueName: 'Credentials in Storage',
|
|
26
|
+
cwe: 'CWE-522',
|
|
27
|
+
description: 'Credentials stored in insecure browser/mobile storage',
|
|
28
|
+
severity: 'CRITICAL',
|
|
29
|
+
fix: 'Use secure storage like Keychain, SecureStore, or encrypted storage',
|
|
30
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/522.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
|
+
const sensitiveKeys = ['password', 'token', 'apikey', 'secret', 'credential', 'auth', 'key'];
|
|
42
|
+
const storageObjects = ['localStorage', 'sessionStorage', 'AsyncStorage'];
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
CallExpression(node: TSESTree.CallExpression) {
|
|
46
|
+
// Check localStorage.setItem/sessionStorage.setItem/AsyncStorage.setItem
|
|
47
|
+
if (node.callee.type === 'MemberExpression' &&
|
|
48
|
+
node.callee.object.type === 'Identifier' &&
|
|
49
|
+
storageObjects.includes(node.callee.object.name) &&
|
|
50
|
+
node.callee.property.type === 'Identifier' &&
|
|
51
|
+
node.callee.property.name === 'setItem') {
|
|
52
|
+
|
|
53
|
+
const keyArg = node.arguments[0];
|
|
54
|
+
if (keyArg && keyArg.type === 'Literal' && typeof keyArg.value === 'string') {
|
|
55
|
+
const key = keyArg.value.toLowerCase();
|
|
56
|
+
if (sensitiveKeys.some(k => key.includes(k))) {
|
|
57
|
+
report(node);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for no-credentials-in-storage-api
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { noCredentialsInStorageApi } from './index';
|
|
7
|
+
|
|
8
|
+
const ruleTester = new RuleTester({
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
ruleTester.run('no-credentials-in-storage-api', noCredentialsInStorageApi, {
|
|
16
|
+
valid: [
|
|
17
|
+
// Safe storage usage
|
|
18
|
+
{ code: "localStorage.setItem('theme', 'dark')" },
|
|
19
|
+
{ code: "sessionStorage.setItem('language', 'en')" },
|
|
20
|
+
{ code: "AsyncStorage.setItem('settings', json)" },
|
|
21
|
+
{ code: "const x = 1" },
|
|
22
|
+
],
|
|
23
|
+
|
|
24
|
+
invalid: [
|
|
25
|
+
// Storing credentials
|
|
26
|
+
{ code: "localStorage.setItem('password', pwd)", errors: [{ messageId: 'violationDetected' }] },
|
|
27
|
+
{ code: "localStorage.setItem('authToken', token)", errors: [{ messageId: 'violationDetected' }] },
|
|
28
|
+
{ code: "sessionStorage.setItem('apiKey', key)", errors: [{ messageId: 'violationDetected' }] },
|
|
29
|
+
{ code: "AsyncStorage.setItem('secret', data)", errors: [{ messageId: 'violationDetected' }] },
|
|
30
|
+
],
|
|
31
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prevent sensitive data in temp directories
|
|
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 noDataInTempStorage = createRule<RuleOptions, MessageIds>({
|
|
16
|
+
name: 'no-data-in-temp-storage',
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'problem',
|
|
19
|
+
docs: {
|
|
20
|
+
description: 'Prevent sensitive data in temp directories',
|
|
21
|
+
},
|
|
22
|
+
messages: {
|
|
23
|
+
violationDetected: formatLLMMessage({
|
|
24
|
+
icon: MessageIcons.SECURITY,
|
|
25
|
+
issueName: 'Temp Storage Data',
|
|
26
|
+
cwe: 'CWE-312',
|
|
27
|
+
description: 'Sensitive data written to temp directory - not secure',
|
|
28
|
+
severity: 'HIGH',
|
|
29
|
+
fix: 'Use secure storage location or encrypt data before writing',
|
|
30
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/312.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
|
+
const tempPaths = ['/tmp', '/var/tmp', 'temp/', '/temp'];
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
CallExpression(node: TSESTree.CallExpression) {
|
|
45
|
+
// Detect fs.writeFileSync or fs.writeFile with temp path
|
|
46
|
+
if (node.callee.type === 'MemberExpression' &&
|
|
47
|
+
node.callee.object.type === 'Identifier' &&
|
|
48
|
+
node.callee.object.name === 'fs' &&
|
|
49
|
+
node.callee.property.type === 'Identifier' &&
|
|
50
|
+
['writeFileSync', 'writeFile'].includes(node.callee.property.name)) {
|
|
51
|
+
|
|
52
|
+
const pathArg = node.arguments[0];
|
|
53
|
+
if (pathArg && pathArg.type === 'Literal' && typeof pathArg.value === 'string') {
|
|
54
|
+
if (tempPaths.some(tp => pathArg.value.includes(tp))) {
|
|
55
|
+
report(node);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
Literal(node: TSESTree.Literal) {
|
|
62
|
+
// Detect temp path literals
|
|
63
|
+
if (typeof node.value === 'string') {
|
|
64
|
+
if (tempPaths.some(tp => node.value.includes(tp))) {
|
|
65
|
+
// Only flag if parent is assignment or variable declaration
|
|
66
|
+
const parent = node.parent;
|
|
67
|
+
if (parent?.type === 'VariableDeclarator' || parent?.type === 'AssignmentExpression') {
|
|
68
|
+
report(node);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for no-data-in-temp-storage
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
6
|
+
import { noDataInTempStorage } from './index';
|
|
7
|
+
|
|
8
|
+
const ruleTester = new RuleTester({
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
ruleTester.run('no-data-in-temp-storage', noDataInTempStorage, {
|
|
16
|
+
valid: [
|
|
17
|
+
// Secure storage locations
|
|
18
|
+
{ code: "fs.writeFileSync('/app/data/file.txt', data)" },
|
|
19
|
+
{ code: "fs.writeFile('/secure/path/file.json', data, callback)" },
|
|
20
|
+
// Non-path literals
|
|
21
|
+
{ code: "console.log('temp data')" },
|
|
22
|
+
{ code: "const x = 1" },
|
|
23
|
+
],
|
|
24
|
+
|
|
25
|
+
invalid: [
|
|
26
|
+
// Temp path writes
|
|
27
|
+
{ code: "fs.writeFileSync('/tmp/secrets.json', data)", errors: [{ messageId: 'violationDetected' }] },
|
|
28
|
+
{ code: "fs.writeFile('/var/tmp/auth.txt', data, cb)", errors: [{ messageId: 'violationDetected' }] },
|
|
29
|
+
// Temp path variables
|
|
30
|
+
{ code: "const path = '/tmp/sensitive.txt'", errors: [{ messageId: 'violationDetected' }] },
|
|
31
|
+
{ code: "let file = '/var/tmp/data.json'", errors: [{ messageId: 'violationDetected' }] },
|
|
32
|
+
],
|
|
33
|
+
});
|