@sun-asterisk/sunlint 1.3.39 → 1.3.40
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/config/rules/rules-registry-generated.json +134 -108
- package/docs/GENERATED_FILES_QUICK_REFERENCE.md +96 -0
- package/docs/GENERATED_FILE_HANDLING_SUMMARY.md +152 -0
- package/docs/skills/CREATE_NEW_DART_RULE.md +161 -14
- package/origin-rules/dart-en.md +151 -163
- package/package.json +2 -1
- package/rules/dart/D002_dispose_resources/config.json +25 -0
- package/rules/dart/D003_prefer_widgets_over_methods/config.json +14 -0
- package/rules/dart/D004_avoid_shrinkwrap_listview/config.json +13 -0
- package/rules/dart/D005_limit_widget_nesting/config.json +13 -0
- package/rules/dart/D006_prefer_extracting_large_callbacks/config.json +25 -0
- package/rules/dart/D007_prefer_init_first_dispose_last/config.json +10 -0
- package/rules/dart/D008_avoid_long_functions/config.json +12 -0
- package/rules/dart/D009_limit_function_parameters/config.json +13 -0
- package/rules/dart/D010_limit_cyclomatic_complexity/config.json +12 -0
- package/rules/dart/D011_prefer_named_parameters/config.json +12 -0
- package/rules/dart/D012_prefer_named_boolean_parameters/config.json +9 -0
- package/rules/dart/D013_single_public_class/config.json +10 -0
- package/rules/dart/D014_unsafe_collection_access/config.json +10 -0
- package/rules/dart/D015_copywith_all_parameters/config.json +9 -0
- package/rules/dart/D016_project_should_have_tests/config.json +24 -0
- package/rules/dart/D017_pubspec_dependencies_review/config.json +23 -0
- package/rules/dart/D018_remove_commented_code/config.json +13 -0
- package/rules/dart/D019_avoid_single_child_multi_child_widget/config.json +21 -0
- package/rules/dart/D020_limit_if_else_branches/config.json +12 -0
- package/rules/dart/D021_avoid_negated_boolean_checks/config.json +14 -0
- package/rules/dart/D022_use_setstate_correctly/config.json +14 -0
- package/rules/dart/D023_avoid_unnecessary_method_overrides/config.json +13 -0
- package/rules/dart/D024_avoid_unnecessary_stateful_widget/config.json +9 -0
- package/rules/dart/D025_avoid_nested_conditional_expressions/config.json +9 -0
- package/skill-assets/sunlint-code-quality/AGENTS.md +80 -0
- package/skill-assets/sunlint-code-quality/SKILL.md +176 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C006-verb-noun-functions.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C013-no-dead-code.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C014-dependency-injection.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C017-no-constructor-logic.md +46 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C018-generic-errors.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C019-error-log-level.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C020-no-unused-imports.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C022-no-unused-variables.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C023-no-duplicate-names.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C024-centralize-constants.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C029-catch-log-root-cause.md +40 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C030-custom-error-classes.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C033-separate-data-access.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C035-error-context-logging.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C041-no-hardcoded-secrets.md +25 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C042-boolean-naming.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C052-controller-parsing.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C060-superclass-logic.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/C067-no-hardcoded-config.md +24 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S003-open-redirect.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S004-no-log-credentials.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S005-server-authorization.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S006-default-credentials.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S007-output-encoding.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S009-approved-crypto.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S010-csprng.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S011-encrypted-client-hello.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S012-secrets-management.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S013-tls-connections.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S016-no-sensitive-query-string.md +39 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S017-parameterized-queries.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S019-email-input-sanitization.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S020-eval-code-execution.md +56 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S022-context-escaping.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S023-dynamic-js-encoding.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S025-server-validation.md +56 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S026-tls-encryption.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S027-mtls-validation.md +40 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S028-upload-limits.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S029-csrf-protection.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S030-directory-browsing.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S031-secure-cookie-flag.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S032-httponly-cookie.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S033-samesite-cookie.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S034-host-prefix-cookie.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S035-app-hostnames.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S036-internal-file-paths.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S037-anti-cache-headers.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S039-tls-certificate-validation.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S041-logout-invalidation.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S042-long-lived-sessions.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S044-critical-changes-reauth.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S045-brute-force-protection.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S047-oauth-csrf-protection.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S048-oauth-redirect-validation.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S049-auth-code-expiry.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S050-token-entropy.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S051-password-length.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S052-otp-entropy.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S053-generic-error-messages.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S054-no-default-admin.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S055-content-type-validation.md +44 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S056-log-injection.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S057-synchronized-time.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/csharp/S058-ssrf-protection.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/java/C006-verb-noun-functions.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/java/C013-no-dead-code.md +175 -0
- package/skill-assets/sunlint-code-quality/rules/java/C014-dependency-injection.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/java/C017-no-constructor-logic.md +39 -0
- package/skill-assets/sunlint-code-quality/rules/java/C018-generic-errors.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/java/C019-error-log-level.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/java/C020-no-unused-imports.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/java/C022-no-unused-variables.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/java/C023-no-duplicate-names.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/java/C024-centralize-constants.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/java/C029-catch-log-root-cause.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/java/C030-custom-error-classes.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/java/C033-separate-data-access.md +46 -0
- package/skill-assets/sunlint-code-quality/rules/java/C035-error-context-logging.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/java/C041-no-hardcoded-secrets.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/java/C042-boolean-naming.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/java/C052-controller-parsing.md +39 -0
- package/skill-assets/sunlint-code-quality/rules/java/C060-superclass-logic.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/java/C067-no-hardcoded-config.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/java/S003-open-redirect.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/java/S004-no-log-credentials.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/java/S005-server-authorization.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/java/S006-default-credentials.md +39 -0
- package/skill-assets/sunlint-code-quality/rules/java/S007-output-encoding.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/java/S009-approved-crypto.md +40 -0
- package/skill-assets/sunlint-code-quality/rules/java/S010-csprng.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/java/S011-encrypted-client-hello.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/java/S012-secrets-management.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/java/S013-tls-connections.md +40 -0
- package/skill-assets/sunlint-code-quality/rules/java/S016-no-sensitive-query-string.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/java/S017-parameterized-queries.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/java/S019-email-input-sanitization.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/java/S020-eval-code-execution.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/java/S022-context-escaping.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/java/S023-dynamic-js-encoding.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/java/S025-server-validation.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/java/S026-tls-encryption.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/java/S027-mtls-validation.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/java/S028-upload-limits.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/java/S029-csrf-protection.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/java/S030-directory-browsing.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/java/S031-secure-cookie-flag.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/java/S032-httponly-cookie.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/java/S033-samesite-cookie.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/java/S034-host-prefix-cookie.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/java/S035-app-hostnames.md +23 -0
- package/skill-assets/sunlint-code-quality/rules/java/S036-internal-file-paths.md +39 -0
- package/skill-assets/sunlint-code-quality/rules/java/S037-anti-cache-headers.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/java/S039-tls-certificate-validation.md +43 -0
- package/skill-assets/sunlint-code-quality/rules/java/S041-logout-invalidation.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/java/S042-long-lived-sessions.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/java/S044-critical-changes-reauth.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/java/S045-brute-force-protection.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/java/S047-oauth-csrf-protection.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/java/S048-oauth-redirect-validation.md +25 -0
- package/skill-assets/sunlint-code-quality/rules/java/S049-auth-code-expiry.md +23 -0
- package/skill-assets/sunlint-code-quality/rules/java/S050-token-entropy.md +20 -0
- package/skill-assets/sunlint-code-quality/rules/java/S051-password-length.md +20 -0
- package/skill-assets/sunlint-code-quality/rules/java/S052-otp-entropy.md +23 -0
- package/skill-assets/sunlint-code-quality/rules/java/S053-generic-error-messages.md +21 -0
- package/skill-assets/sunlint-code-quality/rules/java/S054-no-default-admin.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/java/S055-content-type-validation.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/java/S056-log-injection.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/java/S057-synchronized-time.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/java/S058-ssrf-protection.md +56 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C006-verb-noun-functions.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C013-no-dead-code.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C014-dependency-injection.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C017-no-constructor-logic.md +68 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C018-generic-errors.md +46 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C019-error-log-level.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C020-no-unused-imports.md +44 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C022-no-unused-variables.md +39 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C023-no-duplicate-names.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C024-centralize-constants.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C029-catch-log-root-cause.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C030-custom-error-classes.md +72 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C033-separate-data-access.md +69 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C035-error-context-logging.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C041-no-hardcoded-secrets.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C042-boolean-naming.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C052-controller-parsing.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C060-superclass-logic.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/C067-no-hardcoded-config.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S003-open-redirect.md +66 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S004-no-log-credentials.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S005-server-authorization.md +75 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S006-default-credentials.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S007-output-encoding.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S009-approved-crypto.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S010-csprng.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S011-encrypted-client-hello.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S012-secrets-management.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S013-tls-connections.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S016-no-sensitive-query-string.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S017-parameterized-queries.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S019-email-input-sanitization.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S020-eval-code-execution.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S022-context-escaping.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S023-dynamic-js-encoding.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S025-server-validation.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S026-tls-encryption.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S027-mtls-validation.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S028-upload-limits.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S029-csrf-protection.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S030-directory-browsing.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S031-secure-cookie-flag.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S032-httponly-cookie.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S033-samesite-cookie.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S034-host-prefix-cookie.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S035-app-hostnames.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S036-internal-file-paths.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S037-anti-cache-headers.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S039-tls-certificate-validation.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S041-logout-invalidation.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S042-long-lived-sessions.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S044-critical-changes-reauth.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S045-brute-force-protection.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S047-oauth-csrf-protection.md +74 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S048-oauth-redirect-validation.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S049-auth-code-expiry.md +70 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S050-token-entropy.md +65 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S051-password-length.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S052-otp-entropy.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S053-generic-error-messages.md +66 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S054-no-default-admin.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S055-content-type-validation.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S056-log-injection.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S057-synchronized-time.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/kotlin/S058-ssrf-protection.md +69 -0
- package/skill-assets/sunlint-code-quality/rules/php/C006-verb-noun-functions.md +46 -0
- package/skill-assets/sunlint-code-quality/rules/php/C013-no-dead-code.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/php/C014-dependency-injection.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/php/C017-no-constructor-logic.md +68 -0
- package/skill-assets/sunlint-code-quality/rules/php/C018-generic-errors.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/php/C019-error-log-level.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/php/C020-no-unused-imports.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/php/C022-no-unused-variables.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/php/C023-no-duplicate-names.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/php/C024-centralize-constants.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/php/C029-catch-log-root-cause.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/php/C030-custom-error-classes.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/php/C033-separate-data-access.md +79 -0
- package/skill-assets/sunlint-code-quality/rules/php/C035-error-context-logging.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/php/C041-no-hardcoded-secrets.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/php/C042-boolean-naming.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/php/C052-controller-parsing.md +66 -0
- package/skill-assets/sunlint-code-quality/rules/php/C060-superclass-logic.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/php/C067-no-hardcoded-config.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/php/S003-open-redirect.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/php/S004-no-log-credentials.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/php/S005-server-authorization.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/php/S006-default-credentials.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/php/S007-output-encoding.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/php/S009-approved-crypto.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/php/S010-csprng.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/php/S011-encrypted-client-hello.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/php/S012-secrets-management.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/php/S013-tls-connections.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/php/S016-no-sensitive-query-string.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/php/S017-parameterized-queries.md +44 -0
- package/skill-assets/sunlint-code-quality/rules/php/S019-email-input-sanitization.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/php/S020-eval-code-execution.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/php/S022-context-escaping.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/php/S023-dynamic-js-encoding.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/php/S025-server-validation.md +63 -0
- package/skill-assets/sunlint-code-quality/rules/php/S026-tls-encryption.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/php/S027-mtls-validation.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/php/S028-upload-limits.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/php/S029-csrf-protection.md +65 -0
- package/skill-assets/sunlint-code-quality/rules/php/S030-directory-browsing.md +40 -0
- package/skill-assets/sunlint-code-quality/rules/php/S031-secure-cookie-flag.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/php/S032-httponly-cookie.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/php/S033-samesite-cookie.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/php/S034-host-prefix-cookie.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/php/S035-app-hostnames.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/php/S036-internal-file-paths.md +56 -0
- package/skill-assets/sunlint-code-quality/rules/php/S037-anti-cache-headers.md +56 -0
- package/skill-assets/sunlint-code-quality/rules/php/S039-tls-certificate-validation.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/php/S041-logout-invalidation.md +63 -0
- package/skill-assets/sunlint-code-quality/rules/php/S042-long-lived-sessions.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/php/S044-critical-changes-reauth.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/php/S045-brute-force-protection.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/php/S047-oauth-csrf-protection.md +72 -0
- package/skill-assets/sunlint-code-quality/rules/php/S048-oauth-redirect-validation.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/php/S049-auth-code-expiry.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/php/S050-token-entropy.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/php/S051-password-length.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/php/S052-otp-entropy.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/php/S053-generic-error-messages.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/php/S054-no-default-admin.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/php/S055-content-type-validation.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/php/S056-log-injection.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/php/S057-synchronized-time.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/php/S058-ssrf-protection.md +65 -0
- package/skill-assets/sunlint-code-quality/rules/python/C006-verb-noun-functions.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/python/C013-no-dead-code.md +24 -0
- package/skill-assets/sunlint-code-quality/rules/python/C014-dependency-injection.md +68 -0
- package/skill-assets/sunlint-code-quality/rules/python/C017-no-constructor-logic.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/python/C018-generic-errors.md +25 -0
- package/skill-assets/sunlint-code-quality/rules/python/C019-error-log-level.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/python/C020-no-unused-imports.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/python/C022-no-unused-variables.md +24 -0
- package/skill-assets/sunlint-code-quality/rules/python/C023-no-duplicate-names.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/python/C024-centralize-constants.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/python/C029-catch-log-root-cause.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/python/C030-custom-error-classes.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/python/C033-separate-data-access.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/python/C035-error-context-logging.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/python/C041-no-hardcoded-secrets.md +23 -0
- package/skill-assets/sunlint-code-quality/rules/python/C042-boolean-naming.md +24 -0
- package/skill-assets/sunlint-code-quality/rules/python/C052-controller-parsing.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/python/C060-superclass-logic.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/python/C067-no-hardcoded-config.md +22 -0
- package/skill-assets/sunlint-code-quality/rules/python/S003-open-redirect.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S004-no-log-credentials.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S005-server-authorization.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S006-default-credentials.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S007-output-encoding.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S009-approved-crypto.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S010-csprng.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S011-encrypted-client-hello.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S012-secrets-management.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S013-tls-connections.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S016-no-sensitive-query-string.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S017-parameterized-queries.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/python/S019-email-input-sanitization.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S020-eval-code-execution.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/python/S022-context-escaping.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S023-dynamic-js-encoding.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S025-server-validation.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S026-tls-encryption.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S027-mtls-validation.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S028-upload-limits.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S029-csrf-protection.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S030-directory-browsing.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S031-secure-cookie-flag.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S032-httponly-cookie.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S033-samesite-cookie.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S034-host-prefix-cookie.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S035-app-hostnames.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S036-internal-file-paths.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/python/S037-anti-cache-headers.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S039-tls-certificate-validation.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S041-logout-invalidation.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S042-long-lived-sessions.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S044-critical-changes-reauth.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S045-brute-force-protection.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S047-oauth-csrf-protection.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S048-oauth-redirect-validation.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S049-auth-code-expiry.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S050-token-entropy.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S051-password-length.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S052-otp-entropy.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S053-generic-error-messages.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S054-no-default-admin.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S055-content-type-validation.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S056-log-injection.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S057-synchronized-time.md +16 -0
- package/skill-assets/sunlint-code-quality/rules/python/S058-ssrf-protection.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C006-verb-noun-functions.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C013-no-dead-code.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C014-dependency-injection.md +69 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C017-no-constructor-logic.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C018-generic-errors.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C019-error-log-level.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C020-no-unused-imports.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C022-no-unused-variables.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C023-no-duplicate-names.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C024-centralize-constants.md +56 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C029-catch-log-root-cause.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C030-custom-error-classes.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C033-separate-data-access.md +69 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C035-error-context-logging.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C041-no-hardcoded-secrets.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C042-boolean-naming.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C052-controller-parsing.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C060-superclass-logic.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/C067-no-hardcoded-config.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S003-open-redirect.md +76 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S004-no-log-credentials.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S005-server-authorization.md +68 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S006-default-credentials.md +69 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S007-output-encoding.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S009-approved-crypto.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S010-csprng.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S011-encrypted-client-hello.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S012-secrets-management.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S013-tls-connections.md +70 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S016-no-sensitive-query-string.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S017-parameterized-queries.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S019-email-input-sanitization.md +56 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S020-eval-code-execution.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S022-context-escaping.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S023-dynamic-js-encoding.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S025-server-validation.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S026-tls-encryption.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S027-mtls-validation.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S028-upload-limits.md +65 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S029-csrf-protection.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S030-directory-browsing.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S031-secure-cookie-flag.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S032-httponly-cookie.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S033-samesite-cookie.md +46 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S034-host-prefix-cookie.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S035-app-hostnames.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S036-internal-file-paths.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S037-anti-cache-headers.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S039-tls-certificate-validation.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S041-logout-invalidation.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S042-long-lived-sessions.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S044-critical-changes-reauth.md +69 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S045-brute-force-protection.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S047-oauth-csrf-protection.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S048-oauth-redirect-validation.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S049-auth-code-expiry.md +73 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S050-token-entropy.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S051-password-length.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S052-otp-entropy.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S053-generic-error-messages.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S054-no-default-admin.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S055-content-type-validation.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S056-log-injection.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S057-synchronized-time.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/typescript/S058-ssrf-protection.md +63 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Set Anti-cache Headers
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: prevents sensitive data caching
|
|
5
|
+
tags: headers, cache, sensitive-data, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Set Anti-cache Headers
|
|
9
|
+
|
|
10
|
+
Sensitive pages cached in browsers or proxies can be accessed by other users.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no cache control):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
app.get('/account', (req, res) => {
|
|
16
|
+
res.json(sensitiveData); // May be cached!
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (anti-cache headers):**
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// For sensitive pages
|
|
24
|
+
app.get('/account', (req, res) => {
|
|
25
|
+
res.set({
|
|
26
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate, private',
|
|
27
|
+
'Pragma': 'no-cache',
|
|
28
|
+
'Expires': '0'
|
|
29
|
+
});
|
|
30
|
+
res.json(sensitiveData);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Middleware for all authenticated routes
|
|
34
|
+
const noCacheMiddleware = (req, res, next) => {
|
|
35
|
+
res.set({
|
|
36
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate, private',
|
|
37
|
+
'Pragma': 'no-cache',
|
|
38
|
+
'Expires': '0'
|
|
39
|
+
});
|
|
40
|
+
next();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
app.use('/api/user', authMiddleware, noCacheMiddleware, userRoutes);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**When to use anti-cache:**
|
|
47
|
+
- Account pages
|
|
48
|
+
- Financial data
|
|
49
|
+
- Personal information
|
|
50
|
+
- Any authenticated content
|
|
51
|
+
|
|
52
|
+
**Tools:** helmet.js, Security Headers
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TLS Clients Must Validate Server Certificates
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents man-in-the-middle attacks
|
|
5
|
+
tags: tls, certificates, validation, mitm, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## TLS Clients Must Validate Server Certificates
|
|
9
|
+
|
|
10
|
+
Disabling certificate validation makes TLS useless - attackers can intercept all traffic with self-signed certificates.
|
|
11
|
+
|
|
12
|
+
**Incorrect (disabled validation):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Node.js - DANGEROUS
|
|
16
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
17
|
+
|
|
18
|
+
// Axios - DANGEROUS
|
|
19
|
+
axios.get('https://api.example.com', {
|
|
20
|
+
httpsAgent: new https.Agent({ rejectUnauthorized: false })
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Python - DANGEROUS
|
|
24
|
+
requests.get('https://api.example.com', verify=False)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (proper validation):**
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// Default behavior - validates certificates
|
|
31
|
+
axios.get('https://api.example.com');
|
|
32
|
+
|
|
33
|
+
// Custom CA for internal services
|
|
34
|
+
const agent = new https.Agent({
|
|
35
|
+
ca: fs.readFileSync('/path/to/internal-ca.pem'),
|
|
36
|
+
rejectUnauthorized: true
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
axios.get('https://internal-api.example.com', { httpsAgent: agent });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
# Python - with custom CA
|
|
44
|
+
import certifi
|
|
45
|
+
requests.get('https://api.example.com', verify=certifi.where())
|
|
46
|
+
|
|
47
|
+
# Or with internal CA
|
|
48
|
+
requests.get('https://internal.example.com', verify='/path/to/ca.pem')
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Tools:** SSL Labs, testssl.sh, OWASP ZAP
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Invalidate Session On Logout
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: ensures logout actually terminates access
|
|
5
|
+
tags: session, logout, invalidation, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Invalidate Session On Logout
|
|
9
|
+
|
|
10
|
+
If sessions persist after logout, stolen tokens remain valid.
|
|
11
|
+
|
|
12
|
+
**Incorrect (client-only logout):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Frontend-only logout (token still valid!)
|
|
16
|
+
const logout = () => {
|
|
17
|
+
localStorage.removeItem('token');
|
|
18
|
+
navigate('/login');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Server doesn't invalidate session
|
|
22
|
+
app.post('/logout', (req, res) => {
|
|
23
|
+
res.json({ success: true }); // Session still active!
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (server-side invalidation):**
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
app.post('/logout', async (req, res) => {
|
|
31
|
+
// Destroy server-side session
|
|
32
|
+
await sessionStore.destroy(req.sessionId);
|
|
33
|
+
|
|
34
|
+
// For JWT - add to blacklist
|
|
35
|
+
await tokenBlacklist.add(req.token, req.tokenExpiry);
|
|
36
|
+
|
|
37
|
+
// Clear cookie
|
|
38
|
+
res.clearCookie('session', {
|
|
39
|
+
httpOnly: true,
|
|
40
|
+
secure: true,
|
|
41
|
+
sameSite: 'strict'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Prevent caching
|
|
45
|
+
res.set('Cache-Control', 'no-store');
|
|
46
|
+
res.json({ success: true });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Frontend - clear all storage
|
|
50
|
+
const logout = async () => {
|
|
51
|
+
await fetch('/api/logout', { method: 'POST' });
|
|
52
|
+
localStorage.clear();
|
|
53
|
+
sessionStorage.clear();
|
|
54
|
+
window.location.href = '/login'; // Full reload
|
|
55
|
+
};
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Tools:** Session Libraries, Security Audit
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Re-authenticate For Long-lived Sessions
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: ensures continuous user identity verification
|
|
5
|
+
tags: session, authentication, timeout, reauthentication, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Re-authenticate For Long-lived Sessions
|
|
9
|
+
|
|
10
|
+
Long-running sessions may be hijacked. Periodic re-authentication ensures the original user is still present.
|
|
11
|
+
|
|
12
|
+
**Incorrect (sessions never expire):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Session lasts forever
|
|
16
|
+
app.use(session({
|
|
17
|
+
cookie: { maxAge: null } // Never expires
|
|
18
|
+
}));
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (periodic re-authentication):**
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
const SESSION_MAX_AGE = 24 * 60 * 60 * 1000; // 24 hours
|
|
25
|
+
const REAUTH_INTERVAL = 4 * 60 * 60 * 1000; // 4 hours
|
|
26
|
+
|
|
27
|
+
app.use((req, res, next) => {
|
|
28
|
+
if (!req.session.userId) return next();
|
|
29
|
+
|
|
30
|
+
const lastAuth = req.session.lastAuthenticated || 0;
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
|
|
33
|
+
// Check if re-authentication needed
|
|
34
|
+
if (now - lastAuth > REAUTH_INTERVAL) {
|
|
35
|
+
req.session.requiresReauth = true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
next();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Middleware for sensitive operations
|
|
42
|
+
const requireRecentAuth = (req, res, next) => {
|
|
43
|
+
if (req.session.requiresReauth) {
|
|
44
|
+
return res.status(401).json({
|
|
45
|
+
error: 'Re-authentication required',
|
|
46
|
+
code: 'REAUTH_REQUIRED'
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
next();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
app.post('/sensitive-action', requireRecentAuth, handler);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Tools:** Session Management Libraries, Manual Review
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Re-authenticate Before Critical Changes
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: prevents unauthorized critical operations
|
|
5
|
+
tags: authentication, critical, reauthentication, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Re-authenticate Before Critical Changes
|
|
9
|
+
|
|
10
|
+
Critical actions like password change, email change, or account deletion require fresh authentication.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no re-authentication):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Dangerous - no password confirmation
|
|
16
|
+
app.post('/account/delete', async (req, res) => {
|
|
17
|
+
await deleteAccount(req.user.id);
|
|
18
|
+
res.json({ success: true });
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (require password confirmation):**
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
app.post('/account/delete', async (req, res) => {
|
|
26
|
+
const { currentPassword } = req.body;
|
|
27
|
+
|
|
28
|
+
// Verify current password
|
|
29
|
+
const isValid = await verifyPassword(req.user.id, currentPassword);
|
|
30
|
+
if (!isValid) {
|
|
31
|
+
return res.status(401).json({ error: 'Invalid password' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Rate limit this endpoint
|
|
35
|
+
await rateLimiter.checkLimit(req.user.id, 'critical-action');
|
|
36
|
+
|
|
37
|
+
// Perform critical action
|
|
38
|
+
await deleteAccount(req.user.id);
|
|
39
|
+
|
|
40
|
+
// Log the action
|
|
41
|
+
logger.security('Account deleted', { userId: req.user.id });
|
|
42
|
+
|
|
43
|
+
res.json({ success: true });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// For 2FA-enabled accounts
|
|
47
|
+
app.post('/account/change-email', async (req, res) => {
|
|
48
|
+
const { newEmail, totpCode } = req.body;
|
|
49
|
+
|
|
50
|
+
// Verify 2FA code
|
|
51
|
+
const isValidTotp = await verify2FA(req.user.id, totpCode);
|
|
52
|
+
if (!isValidTotp) {
|
|
53
|
+
return res.status(401).json({ error: 'Invalid 2FA code' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await updateEmail(req.user.id, newEmail);
|
|
57
|
+
res.json({ success: true });
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Critical actions requiring re-auth:**
|
|
62
|
+
- Password change
|
|
63
|
+
- Email change
|
|
64
|
+
- Phone number change
|
|
65
|
+
- Account deletion
|
|
66
|
+
- Payment method changes
|
|
67
|
+
- Security settings changes
|
|
68
|
+
|
|
69
|
+
**Tools:** Manual Review, Security Audit
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Implement Brute-force Protection
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: prevents password guessing attacks
|
|
5
|
+
tags: brute-force, rate-limiting, authentication, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Implement Brute-force Protection
|
|
9
|
+
|
|
10
|
+
Without rate limiting, attackers can try millions of password combinations.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no protection):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
app.post('/login', async (req, res) => {
|
|
16
|
+
const user = await authenticate(req.body);
|
|
17
|
+
// No limit on attempts!
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (rate limiting):**
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import rateLimit from 'express-rate-limit';
|
|
25
|
+
|
|
26
|
+
const loginLimiter = rateLimit({
|
|
27
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
28
|
+
max: 5, // 5 attempts per window
|
|
29
|
+
message: 'Too many login attempts, try again later',
|
|
30
|
+
keyGenerator: (req) => req.body.email || req.ip,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
app.post('/login', loginLimiter, async (req, res) => {
|
|
34
|
+
const user = await authenticate(req.body);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Progressive delays
|
|
38
|
+
const failedAttempts = new Map();
|
|
39
|
+
|
|
40
|
+
async function handleLogin(email: string, password: string) {
|
|
41
|
+
const attempts = failedAttempts.get(email) || 0;
|
|
42
|
+
|
|
43
|
+
if (attempts >= 5) {
|
|
44
|
+
const lockoutTime = Math.min(Math.pow(2, attempts - 5) * 60, 3600);
|
|
45
|
+
throw new Error(`Account locked for ${lockoutTime} seconds`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const user = await authenticate(email, password);
|
|
50
|
+
failedAttempts.delete(email);
|
|
51
|
+
return user;
|
|
52
|
+
} catch {
|
|
53
|
+
failedAttempts.set(email, attempts + 1);
|
|
54
|
+
throw new Error('Invalid credentials');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Tools:** express-rate-limit, Redis, WAF
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Protect OAuth Code Flow Vs CSRF
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents OAuth authorization code theft
|
|
5
|
+
tags: oauth, csrf, state, authorization, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Protect OAuth Code Flow Vs CSRF
|
|
9
|
+
|
|
10
|
+
Without state parameter validation, attackers can use their own authorization codes to link their accounts.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no state parameter):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Initiating OAuth without state
|
|
16
|
+
app.get('/auth/google', (req, res) => {
|
|
17
|
+
res.redirect(`https://accounts.google.com/oauth/authorize?
|
|
18
|
+
client_id=${CLIENT_ID}&
|
|
19
|
+
redirect_uri=${REDIRECT_URI}&
|
|
20
|
+
response_type=code`);
|
|
21
|
+
// No state parameter - vulnerable to CSRF!
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (state parameter validation):**
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { randomBytes } from 'crypto';
|
|
29
|
+
|
|
30
|
+
app.get('/auth/google', (req, res) => {
|
|
31
|
+
// Generate random state
|
|
32
|
+
const state = randomBytes(32).toString('hex');
|
|
33
|
+
|
|
34
|
+
// Store in session
|
|
35
|
+
req.session.oauthState = state;
|
|
36
|
+
|
|
37
|
+
res.redirect(`https://accounts.google.com/oauth/authorize?
|
|
38
|
+
client_id=${CLIENT_ID}&
|
|
39
|
+
redirect_uri=${REDIRECT_URI}&
|
|
40
|
+
response_type=code&
|
|
41
|
+
state=${state}`);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
app.get('/auth/google/callback', (req, res) => {
|
|
45
|
+
const { code, state } = req.query;
|
|
46
|
+
|
|
47
|
+
// Validate state
|
|
48
|
+
if (state !== req.session.oauthState) {
|
|
49
|
+
return res.status(403).json({ error: 'Invalid state parameter' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Clear used state
|
|
53
|
+
delete req.session.oauthState;
|
|
54
|
+
|
|
55
|
+
// Exchange code for tokens
|
|
56
|
+
const tokens = await exchangeCodeForTokens(code);
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Tools:** OAuth Libraries, Security Audit
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Validate OAuth Redirect URIs Exactly
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents OAuth redirect hijacking
|
|
5
|
+
tags: oauth, redirect, uri, validation, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Validate OAuth Redirect URIs Exactly
|
|
9
|
+
|
|
10
|
+
Loose redirect URI validation allows attackers to steal authorization codes by redirecting users to malicious sites.
|
|
11
|
+
|
|
12
|
+
**Incorrect (partial validation):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Dangerous - substring match
|
|
16
|
+
if (redirectUri.includes('example.com')) {
|
|
17
|
+
// Allows evil.com?example.com
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Dangerous - prefix match only
|
|
21
|
+
if (redirectUri.startsWith('https://example.com')) {
|
|
22
|
+
// Allows https://example.com.evil.com
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct (exact match against registered URIs):**
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
const REGISTERED_REDIRECT_URIS = [
|
|
30
|
+
'https://app.example.com/callback',
|
|
31
|
+
'https://mobile.example.com/oauth/callback'
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
function validateRedirectUri(uri: string): boolean {
|
|
35
|
+
// Exact match required
|
|
36
|
+
return REGISTERED_REDIRECT_URIS.includes(uri);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
app.get('/oauth/authorize', (req, res) => {
|
|
40
|
+
const { redirect_uri, client_id } = req.query;
|
|
41
|
+
|
|
42
|
+
// Get registered URIs for this client
|
|
43
|
+
const client = await getClient(client_id);
|
|
44
|
+
|
|
45
|
+
if (!client.redirectUris.includes(redirect_uri)) {
|
|
46
|
+
return res.status(400).json({ error: 'invalid_redirect_uri' });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Proceed with authorization
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Requirements:**
|
|
54
|
+
- Exact string match for redirect URIs
|
|
55
|
+
- Pre-registered URIs per client
|
|
56
|
+
- No wildcards or pattern matching
|
|
57
|
+
- HTTPS required for production
|
|
58
|
+
|
|
59
|
+
**Tools:** OAuth Security Testing, Manual Review
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Authentication Codes Must Expire Quickly
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: limits window for code interception attacks
|
|
5
|
+
tags: authentication, codes, expiry, otp, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Authentication Codes Must Expire Quickly
|
|
9
|
+
|
|
10
|
+
Long-lived codes give attackers more time to intercept and use them. Short expiry limits the attack window.
|
|
11
|
+
|
|
12
|
+
**Incorrect (codes never expire or last too long):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Code valid for 24 hours - too long!
|
|
16
|
+
const code = generateCode();
|
|
17
|
+
await storeCode(userId, code, { expiresIn: '24h' });
|
|
18
|
+
|
|
19
|
+
// No expiry at all
|
|
20
|
+
await storeCode(userId, code); // Lives forever!
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Correct (short expiry with proper handling):**
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
const CODE_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
|
|
27
|
+
|
|
28
|
+
async function generateAuthCode(userId: string): Promise<string> {
|
|
29
|
+
const code = generateSecureOTP();
|
|
30
|
+
|
|
31
|
+
await redis.setex(
|
|
32
|
+
`auth_code:${userId}`,
|
|
33
|
+
300, // 5 minutes in seconds
|
|
34
|
+
JSON.stringify({
|
|
35
|
+
code,
|
|
36
|
+
createdAt: Date.now(),
|
|
37
|
+
attempts: 0
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return code;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function verifyAuthCode(userId: string, inputCode: string): Promise<boolean> {
|
|
45
|
+
const stored = await redis.get(`auth_code:${userId}`);
|
|
46
|
+
if (!stored) return false;
|
|
47
|
+
|
|
48
|
+
const { code, attempts } = JSON.parse(stored);
|
|
49
|
+
|
|
50
|
+
// Rate limit attempts
|
|
51
|
+
if (attempts >= 3) {
|
|
52
|
+
await redis.del(`auth_code:${userId}`);
|
|
53
|
+
throw new Error('Too many attempts');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await redis.incr(`auth_code:${userId}:attempts`);
|
|
57
|
+
|
|
58
|
+
if (code === inputCode) {
|
|
59
|
+
await redis.del(`auth_code:${userId}`); // Single use
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Recommended expiry times:**
|
|
68
|
+
- Email verification: 24 hours
|
|
69
|
+
- Password reset: 1 hour
|
|
70
|
+
- 2FA codes: 5-10 minutes
|
|
71
|
+
- Magic links: 15 minutes
|
|
72
|
+
|
|
73
|
+
**Tools:** Manual Review, Security Audit
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Reference Tokens 128-bit Entropy CSPRNG
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents token brute-forcing
|
|
5
|
+
tags: tokens, entropy, csprng, session, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Reference Tokens 128-bit Entropy CSPRNG
|
|
9
|
+
|
|
10
|
+
Low-entropy tokens can be brute-forced. 128 bits of entropy makes attacks computationally infeasible.
|
|
11
|
+
|
|
12
|
+
**Incorrect (low entropy tokens):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Low entropy - only 53 bits
|
|
16
|
+
const token = Math.random().toString(36);
|
|
17
|
+
|
|
18
|
+
// Sequential - predictable
|
|
19
|
+
const token = `session_${++counter}`;
|
|
20
|
+
|
|
21
|
+
// Short token - insufficient entropy
|
|
22
|
+
const token = randomBytes(4).toString('hex'); // Only 32 bits
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (high entropy tokens):**
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { randomBytes } from 'crypto';
|
|
29
|
+
|
|
30
|
+
// 128-bit minimum (16 bytes = 128 bits)
|
|
31
|
+
const sessionToken = randomBytes(16).toString('hex');
|
|
32
|
+
|
|
33
|
+
// 256-bit for extra security (32 bytes)
|
|
34
|
+
const refreshToken = randomBytes(32).toString('base64url');
|
|
35
|
+
|
|
36
|
+
// API keys - 256+ bits
|
|
37
|
+
const apiKey = `sk_${randomBytes(32).toString('base64url')}`;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Entropy levels:**
|
|
41
|
+
|
|
42
|
+
| Bytes | Bits | Security Level |
|
|
43
|
+
|-------|------|----------------|
|
|
44
|
+
| 8 | 64 | Weak |
|
|
45
|
+
| 16 | 128 | Minimum |
|
|
46
|
+
| 32 | 256 | Recommended |
|
|
47
|
+
|
|
48
|
+
**Tools:** Static Analysis, Security Audit
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Support 12-64 Character Passwords
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: enables secure passphrase usage
|
|
5
|
+
tags: password, length, passphrase, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Support 12-64 Character Passwords
|
|
9
|
+
|
|
10
|
+
Long passwords/passphrases are more secure than complex short ones. Don't impose restrictive limits.
|
|
11
|
+
|
|
12
|
+
**Incorrect (restrictive limits):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
const passwordSchema = z.string()
|
|
16
|
+
.min(8)
|
|
17
|
+
.max(16); // Too restrictive!
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (reasonable limits):**
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
const passwordSchema = z.string()
|
|
24
|
+
.min(12, 'Password must be at least 12 characters')
|
|
25
|
+
.max(64, 'Password cannot exceed 64 characters');
|
|
26
|
+
|
|
27
|
+
// Or with complexity options
|
|
28
|
+
const passwordOptions = {
|
|
29
|
+
minLength: 12,
|
|
30
|
+
maxLength: 64,
|
|
31
|
+
// Complexity is optional with long passwords
|
|
32
|
+
requireUppercase: password.length < 16,
|
|
33
|
+
requireNumber: password.length < 16,
|
|
34
|
+
requireSpecial: password.length < 16,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function validatePassword(password: string): boolean {
|
|
38
|
+
if (password.length < 12 || password.length > 64) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Long passphrases don't need symbol requirements
|
|
43
|
+
if (password.length >= 16) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Shorter passwords need complexity
|
|
48
|
+
return hasUppercase(password) &&
|
|
49
|
+
hasNumber(password) &&
|
|
50
|
+
hasSpecial(password);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**NIST Guidelines:**
|
|
55
|
+
- Minimum 8 characters (12+ recommended)
|
|
56
|
+
- Maximum 64+ characters
|
|
57
|
+
- No complexity requirements for long passwords
|
|
58
|
+
- Allow all printable ASCII + Unicode
|
|
59
|
+
|
|
60
|
+
**Tools:** Password Policy, Manual Review
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: OTPs Must Have 20-bit Entropy Minimum
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: prevents OTP brute-forcing
|
|
5
|
+
tags: otp, entropy, authentication, 2fa, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## OTPs Must Have 20-bit Entropy Minimum
|
|
9
|
+
|
|
10
|
+
Low-entropy OTPs can be brute-forced. 20 bits = ~1 million possibilities, requiring rate limiting.
|
|
11
|
+
|
|
12
|
+
**Incorrect (low entropy OTPs):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Only 4 digits = ~13 bits entropy
|
|
16
|
+
const otp = Math.floor(1000 + Math.random() * 9000).toString();
|
|
17
|
+
|
|
18
|
+
// Non-random - predictable
|
|
19
|
+
const otp = Date.now().toString().slice(-4);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (high entropy OTPs):**
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { randomBytes } from 'crypto';
|
|
26
|
+
|
|
27
|
+
// 6-digit numeric OTP (≈20 bits entropy)
|
|
28
|
+
function generateOTP(): string {
|
|
29
|
+
const bytes = randomBytes(4);
|
|
30
|
+
const num = bytes.readUInt32BE(0) % 1000000;
|
|
31
|
+
return num.toString().padStart(6, '0');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 8-digit for higher security (≈26 bits)
|
|
35
|
+
function generateStrongOTP(): string {
|
|
36
|
+
const bytes = randomBytes(4);
|
|
37
|
+
const num = bytes.readUInt32BE(0) % 100000000;
|
|
38
|
+
return num.toString().padStart(8, '0');
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**OTP requirements:**
|
|
43
|
+
- CSPRNG generation
|
|
44
|
+
- ≥20 bits entropy (6+ digits)
|
|
45
|
+
- Short expiry (5-10 minutes)
|
|
46
|
+
- Single use
|
|
47
|
+
- Rate limiting on verification
|
|
48
|
+
|
|
49
|
+
**Tools:** Manual Review, Unit Test
|