@sun-asterisk/sunlint 1.0.6 → 1.1.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/.sunlint.json +35 -0
- package/CHANGELOG.md +135 -169
- package/CONTRIBUTING.md +235 -0
- package/PROJECT_STRUCTURE.md +60 -0
- package/README.md +77 -50
- package/cli.js +1 -0
- package/config/README.md +88 -0
- package/config/defaults/ai-rules-context.json +231 -0
- package/config/engines/engines.json +49 -0
- package/config/engines/eslint-rule-mapping.json +74 -0
- package/config/eslint-rule-mapping.json +126 -0
- package/config/{typescript/eslint.config.js → integrations/eslint/typescript.config.js} +4 -0
- package/config/presets/beginner.json +1 -1
- package/config/presets/ci.json +3 -2
- package/config/presets/recommended.json +1 -1
- package/config/presets/strict.json +2 -2
- package/config/rule-analysis-strategies.js +74 -0
- package/config/{rules-registry.json → rules/rules-registry.json} +82 -0
- package/core/analysis-orchestrator.js +383 -591
- package/core/ast-modules/README.md +103 -0
- package/core/ast-modules/base-parser.js +90 -0
- package/core/ast-modules/index.js +97 -0
- package/core/ast-modules/package.json +37 -0
- package/core/ast-modules/parsers/eslint-js-parser.js +147 -0
- package/core/ast-modules/parsers/eslint-ts-parser.js +106 -0
- package/core/ast-modules/parsers/javascript-parser.js +187 -0
- package/core/ast-modules/parsers/typescript-parser.js +187 -0
- package/core/cli-action-handler.js +271 -255
- package/core/cli-program.js +18 -4
- package/core/config-manager.js +18 -11
- package/core/config-merger.js +52 -1
- package/core/config-validator.js +2 -2
- package/core/enhanced-rules-registry.js +331 -0
- package/core/file-targeting-service.js +93 -29
- package/core/interfaces/analysis-engine.interface.js +100 -0
- package/core/multi-rule-runner.js +0 -221
- package/core/output-service.js +1 -1
- package/core/rule-mapping-service.js +9 -1
- package/core/rule-selection-service.js +10 -2
- package/docs/CONFIGURATION.md +414 -0
- package/docs/DEPLOYMENT-STRATEGIES.md +270 -0
- package/engines/eslint-engine.js +601 -0
- package/engines/heuristic-engine.js +860 -0
- package/engines/openai-engine.js +374 -0
- package/integrations/eslint/README.md +99 -0
- package/{eslint-integration → integrations/eslint/configs}/.eslintrc.js +1 -1
- package/integrations/eslint/configs/eslint.config.js +133 -0
- package/integrations/eslint/configs/eslint.config.simple.js +24 -0
- package/integrations/eslint/plugin/index.js +164 -0
- package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c006-function-name-verb-noun.js +11 -2
- package/integrations/eslint/plugin/rules/common/c013-no-dead-code.js +78 -0
- package/integrations/eslint/plugin/rules/common/c017-limit-constructor-logic.js +146 -0
- package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c029-catch-block-logging.js +35 -0
- package/integrations/eslint/plugin/rules/common/c035-no-empty-catch.js +162 -0
- package/integrations/eslint/plugin/rules/common/c041-no-config-inline.js +122 -0
- package/integrations/eslint/plugin/rules/common/c072-one-assert-per-test.js +184 -0
- package/integrations/eslint/plugin/rules/common/c075-explicit-function-return-types.js +168 -0
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +254 -0
- package/integrations/eslint/plugin/rules/security/s001-fail-securely.js +381 -0
- package/integrations/eslint/plugin/rules/security/s002-idor-check.js +945 -0
- package/integrations/eslint/plugin/rules/security/s007-no-plaintext-otp.js +74 -0
- package/integrations/eslint/plugin/rules/security/s013-verify-tls-connection.js +47 -0
- package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/typescript}/t003-ts-ignore-reason.js +3 -3
- package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/typescript}/t007-no-fn-in-constructor.js +1 -1
- package/integrations/eslint/plugin/rules/typescript/t019-no-this-assign.js +81 -0
- package/integrations/eslint/plugin/rules/typescript/t020-no-default-multi-export.js +127 -0
- package/integrations/eslint/plugin/rules/typescript/t021-limit-nested-generics.js +150 -0
- package/integrations/eslint/test-c041-rule.js +87 -0
- package/package.json +29 -19
- package/rules/README.md +252 -0
- package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
- package/rules/common/C002_no_duplicate_code/config.json +23 -0
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
- package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
- package/rules/{C006_function_naming → common/C006_function_naming}/analyzer.js +13 -2
- package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
- package/rules/common/C013_no_dead_code/analyzer.js +206 -0
- package/rules/common/C014_dependency_injection/analyzer.js +338 -0
- package/rules/common/C017_constructor_logic/analyzer.js +314 -0
- package/rules/{C019_log_level_usage → common/C019_log_level_usage}/analyzer.js +5 -2
- package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/analyzer.js +49 -15
- package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
- package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
- package/rules/common/C043_no_console_or_print/analyzer.js +304 -0
- package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +351 -0
- package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
- package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
- package/rules/docs/C002_no_duplicate_code.md +57 -0
- package/rules/index.js +149 -0
- package/rules/migration/converter.js +385 -0
- package/rules/migration/mapping.json +164 -0
- package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
- package/rules/security/S026_json_schema_validation/config.json +27 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +263 -0
- package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
- package/rules/security/S029_csrf_protection/analyzer.js +264 -0
- package/rules/tests/C002_no_duplicate_code.test.js +50 -0
- package/rules/utils/ast-utils.js +191 -0
- package/rules/utils/base-analyzer.js +98 -0
- package/rules/utils/pattern-matchers.js +239 -0
- package/rules/utils/rule-helpers.js +264 -0
- package/rules/utils/severity-constants.js +93 -0
- package/scripts/build-release.sh +117 -0
- package/scripts/ci-report.js +179 -0
- package/scripts/install.sh +196 -0
- package/scripts/manual-release.sh +338 -0
- package/scripts/merge-reports.js +424 -0
- package/scripts/pre-release-test.sh +175 -0
- package/scripts/prepare-release.sh +202 -0
- package/scripts/setup-github-registry.sh +42 -0
- package/scripts/test-scripts/README.md +22 -0
- package/scripts/test-scripts/test-c041-comparison.js +114 -0
- package/scripts/test-scripts/test-c041-eslint.js +67 -0
- package/scripts/test-scripts/test-eslint-rules.js +146 -0
- package/scripts/test-scripts/test-real-world.js +44 -0
- package/scripts/test-scripts/test-rules-on-real-projects.js +86 -0
- package/scripts/trigger-release.sh +285 -0
- package/scripts/validate-rule-structure.js +148 -0
- package/scripts/verify-install.sh +82 -0
- package/cli-legacy.js +0 -355
- package/config/sunlint-schema.json +0 -166
- package/config/typescript/custom-rules-new.js +0 -0
- package/config/typescript/custom-rules.js +0 -9
- package/config/typescript/package-lock.json +0 -1585
- package/config/typescript/package.json +0 -13
- package/config/typescript/security-rules/index.js +0 -90
- package/config/typescript/security-rules/s005-no-origin-auth.js +0 -95
- package/config/typescript/security-rules/s006-activation-recovery-secret-not-plaintext.js +0 -69
- package/config/typescript/security-rules/s008-crypto-agility.js +0 -62
- package/config/typescript/security-rules/s009-no-insecure-crypto.js +0 -103
- package/config/typescript/security-rules/s010-no-insecure-random-in-sensitive-context.js +0 -123
- package/config/typescript/security-rules/s011-no-insecure-uuid.js +0 -66
- package/config/typescript/security-rules/s012-hardcode-secret.js +0 -71
- package/config/typescript/security-rules/s014-insecure-tls-version.js +0 -50
- package/config/typescript/security-rules/s015-insecure-tls-certificate.js +0 -43
- package/config/typescript/security-rules/s016-sensitive-query-parameter.js +0 -59
- package/config/typescript/security-rules/s017-no-sql-injection.js +0 -193
- package/config/typescript/security-rules/s018-positive-input-validation.js +0 -56
- package/config/typescript/security-rules/s019-no-raw-user-input-in-email.js +0 -113
- package/config/typescript/security-rules/s020-no-eval-dynamic-execution.js +0 -89
- package/config/typescript/security-rules/s022-output-encoding.js +0 -78
- package/config/typescript/security-rules/s023-no-json-injection.js +0 -300
- package/config/typescript/security-rules/s025-server-side-input-validation.js +0 -217
- package/config/typescript/security-rules/s026-json-schema-validation.js +0 -68
- package/config/typescript/security-rules/s027-no-hardcoded-secrets.js +0 -80
- package/config/typescript/security-rules/s029-require-csrf-protection.js +0 -79
- package/config/typescript/security-rules/s030-no-directory-browsing.js +0 -78
- package/config/typescript/security-rules/s033-require-samesite-cookie.js +0 -80
- package/config/typescript/security-rules/s034-require-host-cookie-prefix.js +0 -77
- package/config/typescript/security-rules/s035-cookie-specific-path.js +0 -74
- package/config/typescript/security-rules/s036-no-unsafe-file-include.js +0 -68
- package/config/typescript/security-rules/s037-require-anti-cache-headers.js +0 -70
- package/config/typescript/security-rules/s038-no-version-disclosure.js +0 -74
- package/config/typescript/security-rules/s039-no-session-token-in-url.js +0 -63
- package/config/typescript/security-rules/s041-require-session-invalidate-on-logout.js +0 -211
- package/config/typescript/security-rules/s042-require-periodic-reauthentication.js +0 -294
- package/config/typescript/security-rules/s043-terminate-sessions-on-password-change.js +0 -254
- package/config/typescript/security-rules/s044-require-full-session-for-sensitive-operations.js +0 -292
- package/config/typescript/security-rules/s045-anti-automation-controls.js +0 -46
- package/config/typescript/security-rules/s046-secure-notification-on-auth-change.js +0 -44
- package/config/typescript/security-rules/s048-password-credential-recovery.js +0 -54
- package/config/typescript/security-rules/s050-session-token-weak-hash.js +0 -94
- package/config/typescript/security-rules/s052-secure-random-authentication-code.js +0 -66
- package/config/typescript/security-rules/s054-verification-default-account.js +0 -109
- package/config/typescript/security-rules/s057-utc-logging.js +0 -54
- package/config/typescript/security-rules/s058-no-ssrf.js +0 -73
- package/config/typescript/tsconfig.json +0 -29
- package/core/ai-analyzer.js +0 -169
- package/core/eslint-engine-service.js +0 -312
- package/core/eslint-instance-manager.js +0 -104
- package/core/eslint-integration-service.js +0 -363
- package/core/sunlint-engine-service.js +0 -23
- package/core/typescript-analyzer.js +0 -262
- package/core/typescript-engine.js +0 -313
- package/docs/ENHANCED_FILE_TARGETING.md +0 -0
- package/docs/FILE_TARGETING_COMPARISON.md +0 -0
- package/docs/RULE-RESPONSIBILITY-MATRIX.md +0 -204
- package/eslint-integration/cli.js +0 -35
- package/eslint-integration/eslint-plugin-custom/c013-no-dead-code.js +0 -43
- package/eslint-integration/eslint-plugin-custom/c017-limit-constructor-logic.js +0 -39
- package/eslint-integration/eslint-plugin-custom/c027-limit-function-nesting.js +0 -50
- package/eslint-integration/eslint-plugin-custom/c034-no-implicit-return.js +0 -34
- package/eslint-integration/eslint-plugin-custom/c035-no-empty-catch.js +0 -32
- package/eslint-integration/eslint-plugin-custom/c041-no-config-inline.js +0 -64
- package/eslint-integration/eslint-plugin-custom/c048-no-var-declaration.js +0 -31
- package/eslint-integration/eslint-plugin-custom/index.js +0 -155
- package/eslint-integration/eslint-plugin-custom/package.json.bak +0 -9
- package/eslint-integration/eslint-plugin-custom/t004-interface-public-only.js +0 -160
- package/eslint-integration/eslint-plugin-custom/t011-no-real-time-dependency.js +0 -175
- package/eslint-integration/eslint-plugin-custom/t026-limit-nested-generics.js +0 -377
- package/eslint-integration/sample.ts +0 -53
- package/eslint-integration/test-s003.js +0 -5
- package/examples/.github/workflows/code-quality.yml +0 -111
- package/examples/README.md +0 -69
- package/examples/basic-typescript-demo/.eslintrc.json +0 -18
- package/examples/basic-typescript-demo/.next/cache/eslint/.cache_1othrmo +0 -1
- package/examples/basic-typescript-demo/.sunlint.json +0 -29
- package/examples/basic-typescript-demo/eslint.config.mjs +0 -37
- package/examples/basic-typescript-demo/next-env.d.ts +0 -5
- package/examples/basic-typescript-demo/next.config.mjs +0 -4
- package/examples/basic-typescript-demo/package-lock.json +0 -5656
- package/examples/basic-typescript-demo/package.json +0 -34
- package/examples/basic-typescript-demo/src/app/layout.tsx +0 -18
- package/examples/basic-typescript-demo/src/app/page.tsx +0 -48
- package/examples/basic-typescript-demo/src/config.ts +0 -14
- package/examples/basic-typescript-demo/src/good-practices.ts +0 -58
- package/examples/basic-typescript-demo/src/types.generated.ts +0 -13
- package/examples/basic-typescript-demo/src/user.test.ts +0 -19
- package/examples/basic-typescript-demo/src/violations.ts +0 -61
- package/examples/basic-typescript-demo/tsconfig.json +0 -27
- package/examples/eslint-integration-demo/.eslintrc.js +0 -38
- package/examples/eslint-integration-demo/.sunlint.json +0 -42
- package/examples/eslint-integration-demo/next-env.d.ts +0 -5
- package/examples/eslint-integration-demo/next.config.js +0 -8
- package/examples/eslint-integration-demo/package-lock.json +0 -5740
- package/examples/eslint-integration-demo/package.json +0 -37
- package/examples/eslint-integration-demo/src/api.test.ts +0 -20
- package/examples/eslint-integration-demo/src/conflict-test.tsx +0 -44
- package/examples/eslint-integration-demo/src/naming-conflicts.ts +0 -50
- package/examples/eslint-integration-demo/tsconfig.json +0 -26
- package/examples/file-targeting-demo/global.d.ts +0 -11
- package/examples/file-targeting-demo/jest.config.js +0 -8
- package/examples/file-targeting-demo/sample.ts +0 -53
- package/examples/file-targeting-demo/src/server.js +0 -11
- package/examples/file-targeting-demo/src/server.test.js +0 -11
- package/examples/file-targeting-demo/src/types.d.ts +0 -4
- package/examples/file-targeting-demo/src/types.generated.ts +0 -10
- package/examples/file-targeting-demo/user-service.test.ts +0 -15
- package/examples/file-targeting-demo/user-service.ts +0 -13
- package/examples/file-targeting-demo/utils.js +0 -15
- package/examples/multi-language-project/.eslintrc.json +0 -38
- package/examples/multi-language-project/package.json +0 -37
- package/examples/multi-language-project/src/sample.ts +0 -39
- package/examples/rule-test-fixtures/README.md +0 -67
- package/examples/rule-test-fixtures/rules/C006_function_naming/clean/typescript-clean.ts +0 -64
- package/examples/rule-test-fixtures/rules/C006_function_naming/violations/dart-violations.dart +0 -56
- package/examples/rule-test-fixtures/rules/C006_function_naming/violations/typescript-violations.ts +0 -47
- package/examples/rule-test-fixtures/rules/C019_log_level_usage/clean/typescript-clean.ts +0 -93
- package/examples/rule-test-fixtures/rules/C019_log_level_usage/violations/dart-violations.dart +0 -75
- package/examples/rule-test-fixtures/rules/C019_log_level_usage/violations/typescript-violations.ts +0 -84
- package/examples/rule-test-fixtures/rules/C029_catch_block_logging/violations/typescript-violations.ts +0 -37
- /package/config/{default.json → defaults/default.json} +0 -0
- /package/{eslint-integration/eslint.config.js → config/integrations/eslint/base.config.js} +0 -0
- /package/{eslint-integration/eslint.config.simple.js → config/integrations/eslint/simple.config.js} +0 -0
- /package/{examples/rule-test-fixtures/rules/C029_catch_block_logging/clean/typescript-clean.ts → config/schemas/sunlint-schema.json} +0 -0
- /package/config/{typescript → testing}/test-s005-working.ts +0 -0
- /package/{examples/eslint-integration-demo/test-file-targeting.sh → engines/tree-sitter-parser.js} +0 -0
- /package/{examples/enhanced-config.json → engines/universal-ast-engine.js} +0 -0
- /package/{eslint-integration → integrations/eslint}/package.json +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin}/package.json +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c002-no-duplicate-code.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c003-no-vague-abbreviations.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c010-limit-block-nesting.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c014-abstract-dependency-preferred.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c018-no-generic-throw.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c023-no-duplicate-variable-name-in-scope.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c030-use-custom-error-classes.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c042-boolean-name-prefix.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c043-no-console-or-print.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/common}/c047-no-duplicate-retry-logic.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s003-no-unvalidated-redirect.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s005-no-origin-auth.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s006-activation-recovery-secret-not-plaintext.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s008-crypto-agility.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s009-no-insecure-crypto.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s010-no-insecure-random-in-sensitive-context.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s011-no-insecure-uuid.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s012-hardcode-secret.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s014-insecure-tls-version.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s015-insecure-tls-certificate.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s016-sensitive-query-parameter.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s017-no-sql-injection.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s018-positive-input-validation.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s019-no-raw-user-input-in-email.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s020-no-eval-dynamic-execution.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s022-output-encoding.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s023-no-json-injection.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s025-server-side-input-validation.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s026-json-schema-validation.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s027-no-hardcoded-secrets.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s029-require-csrf-protection.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s030-no-directory-browsing.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s033-require-samesite-cookie.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s034-require-host-cookie-prefix.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s035-cookie-specific-path.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s036-no-unsafe-file-include.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s037-require-anti-cache-headers.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s038-no-version-disclosure.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s039-no-session-token-in-url.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s041-require-session-invalidate-on-logout.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s042-require-periodic-reauthentication.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s043-terminate-sessions-on-password-change.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s044-require-full-session-for-sensitive-operations.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s045-anti-automation-controls.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s046-secure-notification-on-auth-change.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s047-secure-random-passwords.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s048-password-credential-recovery.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s050-session-token-weak-hash.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s052-secure-random-authentication-code.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s054-verification-default-account.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s055-verification-rest-check-the-incoming-content-type.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s057-utc-logging.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/security}/s058-no-ssrf.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom → integrations/eslint/plugin/rules/typescript}/t002-interface-prefix-i.js +0 -0
- /package/{eslint-integration/eslint-plugin-custom/t019-no-empty-type.js → integrations/eslint/plugin/rules/typescript/t004-no-empty-type.js} +0 -0
- /package/{eslint-integration/eslint-plugin-custom/t025-no-nested-union-tuple.js → integrations/eslint/plugin/rules/typescript/t010-no-nested-union-tuple.js} +0 -0
- /package/{eslint-integration → integrations/eslint}/tsconfig.json +0 -0
- /package/rules/{C006_function_naming → common/C006_function_naming}/config.json +0 -0
- /package/rules/{C019_log_level_usage → common/C019_log_level_usage}/config.json +0 -0
- /package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/config.json +0 -0
- /package/rules/{C031_validation_separation → common/C031_validation_separation}/analyzer.js +0 -0
- /package/rules/{C031_validation_separation/README.md → docs/C031_validation_separation.md} +0 -0
- /package/{examples/basic-typescript-demo/test-file-targeting.sh → rules/universal/C010/generic.js} +0 -0
- /package/{examples/basic-typescript-demo/test-config-priority.sh → rules/universal/C010/tree-sitter-analyzer.js} +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SunLint ESLint Plugin - Organized Rules Index
|
|
3
|
+
* 🔹 22 Common Rules | 🔒 49 Security Rules | 📘 13 TypeScript Rules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 🔹 Common Rules (C-series) - General coding standards
|
|
7
|
+
const c002 = require("./rules/common/c002-no-duplicate-code.js");
|
|
8
|
+
const c003 = require("./rules/common/c003-no-vague-abbreviations.js");
|
|
9
|
+
const c006 = require("./rules/common/c006-function-name-verb-noun.js");
|
|
10
|
+
const c010 = require("./rules/common/c010-limit-block-nesting.js");
|
|
11
|
+
const c013 = require("./rules/common/c013-no-dead-code.js");
|
|
12
|
+
const c014 = require("./rules/common/c014-abstract-dependency-preferred.js");
|
|
13
|
+
const c017 = require("./rules/common/c017-limit-constructor-logic.js");
|
|
14
|
+
const c018 = require("./rules/common/c018-no-generic-throw.js");
|
|
15
|
+
const c023 = require("./rules/common/c023-no-duplicate-variable-name-in-scope.js");
|
|
16
|
+
const c041 = require("./rules/common/c041-no-config-inline.js");
|
|
17
|
+
const c029 = require("./rules/common/c029-catch-block-logging.js");
|
|
18
|
+
const c030 = require("./rules/common/c030-use-custom-error-classes.js");
|
|
19
|
+
const c035 = require("./rules/common/c035-no-empty-catch.js");
|
|
20
|
+
const c042 = require("./rules/common/c042-boolean-name-prefix.js");
|
|
21
|
+
const c043 = require("./rules/common/c043-no-console-or-print.js");
|
|
22
|
+
const c047 = require("./rules/common/c047-no-duplicate-retry-logic.js");
|
|
23
|
+
const c072 = require("./rules/common/c072-one-assert-per-test.js");
|
|
24
|
+
const c075 = require("./rules/common/c075-explicit-function-return-types.js");
|
|
25
|
+
const c076 = require("./rules/common/c076-single-behavior-per-test.js");
|
|
26
|
+
|
|
27
|
+
// 📘 TypeScript Rules (T-series)
|
|
28
|
+
const t002 = require("./rules/typescript/t002-interface-prefix-i.js");
|
|
29
|
+
const t003 = require("./rules/typescript/t003-ts-ignore-reason.js");
|
|
30
|
+
const t004 = require("./rules/typescript/t004-no-empty-type.js");
|
|
31
|
+
const t007 = require("./rules/typescript/t007-no-fn-in-constructor.js");
|
|
32
|
+
const t010 = require("./rules/typescript/t010-no-nested-union-tuple.js");
|
|
33
|
+
const t019 = require("./rules/typescript/t019-no-this-assign.js");
|
|
34
|
+
const t020 = require("./rules/typescript/t020-no-default-multi-export.js");
|
|
35
|
+
const t021 = require("./rules/typescript/t021-limit-nested-generics.js");
|
|
36
|
+
|
|
37
|
+
// 🔒 Security Rules (S-series)
|
|
38
|
+
const s001 = require("./rules/security/s001-fail-securely.js");
|
|
39
|
+
const s002 = require("./rules/security/s002-idor-check.js");
|
|
40
|
+
const s003 = require("./rules/security/s003-no-unvalidated-redirect.js");
|
|
41
|
+
const s005 = require("./rules/security/s005-no-origin-auth.js");
|
|
42
|
+
const s006 = require("./rules/security/s006-activation-recovery-secret-not-plaintext.js");
|
|
43
|
+
const s007 = require("./rules/security/s007-no-plaintext-otp.js");
|
|
44
|
+
const s008 = require("./rules/security/s008-crypto-agility.js");
|
|
45
|
+
const s009 = require("./rules/security/s009-no-insecure-crypto.js");
|
|
46
|
+
const s010 = require("./rules/security/s010-no-insecure-random-in-sensitive-context.js");
|
|
47
|
+
const s011 = require("./rules/security/s011-no-insecure-uuid.js");
|
|
48
|
+
const s012 = require("./rules/security/s012-hardcode-secret.js");
|
|
49
|
+
const s013 = require("./rules/security/s013-verify-tls-connection.js");
|
|
50
|
+
const s014 = require("./rules/security/s014-insecure-tls-version.js");
|
|
51
|
+
const s015 = require("./rules/security/s015-insecure-tls-certificate.js");
|
|
52
|
+
const s016 = require("./rules/security/s016-sensitive-query-parameter.js");
|
|
53
|
+
const s017 = require("./rules/security/s017-no-sql-injection.js");
|
|
54
|
+
const s018 = require("./rules/security/s018-positive-input-validation.js");
|
|
55
|
+
const s019 = require("./rules/security/s019-no-raw-user-input-in-email.js");
|
|
56
|
+
const s020 = require("./rules/security/s020-no-eval-dynamic-execution.js");
|
|
57
|
+
const s022 = require("./rules/security/s022-output-encoding.js");
|
|
58
|
+
const s023 = require("./rules/security/s023-no-json-injection.js");
|
|
59
|
+
const s025 = require("./rules/security/s025-server-side-input-validation.js");
|
|
60
|
+
const s026 = require("./rules/security/s026-json-schema-validation.js");
|
|
61
|
+
const s027 = require("./rules/security/s027-no-hardcoded-secrets.js");
|
|
62
|
+
const s029 = require("./rules/security/s029-require-csrf-protection.js");
|
|
63
|
+
const s030 = require("./rules/security/s030-no-directory-browsing.js");
|
|
64
|
+
const s033 = require("./rules/security/s033-require-samesite-cookie.js");
|
|
65
|
+
const s034 = require("./rules/security/s034-require-host-cookie-prefix.js");
|
|
66
|
+
const s035 = require("./rules/security/s035-cookie-specific-path.js");
|
|
67
|
+
const s036 = require("./rules/security/s036-no-unsafe-file-include.js");
|
|
68
|
+
const s037 = require("./rules/security/s037-require-anti-cache-headers.js");
|
|
69
|
+
const s038 = require("./rules/security/s038-no-version-disclosure.js");
|
|
70
|
+
const s039 = require("./rules/security/s039-no-session-token-in-url.js");
|
|
71
|
+
const s041 = require("./rules/security/s041-require-session-invalidate-on-logout.js");
|
|
72
|
+
const s042 = require("./rules/security/s042-require-periodic-reauthentication.js");
|
|
73
|
+
const s043 = require("./rules/security/s043-terminate-sessions-on-password-change.js");
|
|
74
|
+
const s044 = require("./rules/security/s044-require-full-session-for-sensitive-operations.js");
|
|
75
|
+
const s045 = require("./rules/security/s045-anti-automation-controls.js");
|
|
76
|
+
const s046 = require("./rules/security/s046-secure-notification-on-auth-change.js");
|
|
77
|
+
const s047 = require("./rules/security/s047-secure-random-passwords.js");
|
|
78
|
+
const s048 = require("./rules/security/s048-password-credential-recovery.js");
|
|
79
|
+
const s050 = require("./rules/security/s050-session-token-weak-hash.js");
|
|
80
|
+
const s052 = require("./rules/security/s052-secure-random-authentication-code.js");
|
|
81
|
+
const s054 = require("./rules/security/s054-verification-default-account.js");
|
|
82
|
+
const s055 = require("./rules/security/s055-verification-rest-check-the-incoming-content-type.js");
|
|
83
|
+
const s057 = require("./rules/security/s057-utc-logging.js");
|
|
84
|
+
const s058 = require("./rules/security/s058-no-ssrf.js");
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
rules: {
|
|
88
|
+
"c002": c002,
|
|
89
|
+
"c003": c003,
|
|
90
|
+
"c006": c006,
|
|
91
|
+
"c010": c010,
|
|
92
|
+
"c013": c013,
|
|
93
|
+
"c014": c014,
|
|
94
|
+
"c017": c017,
|
|
95
|
+
"c018": c018,
|
|
96
|
+
"c030": c030,
|
|
97
|
+
"c035": c035,
|
|
98
|
+
"c023": c023,
|
|
99
|
+
"c029": c029,
|
|
100
|
+
"c041": c041,
|
|
101
|
+
"c042": c042,
|
|
102
|
+
"c043": c043,
|
|
103
|
+
"c047": c047,
|
|
104
|
+
"c072": c072,
|
|
105
|
+
"c075": c075,
|
|
106
|
+
"c076": c076,
|
|
107
|
+
"t002": t002,
|
|
108
|
+
"t003": t003,
|
|
109
|
+
"t004": t004,
|
|
110
|
+
"t007": t007,
|
|
111
|
+
"t010": t010,
|
|
112
|
+
"t019": t019,
|
|
113
|
+
"t020": t020,
|
|
114
|
+
"t021": t021,
|
|
115
|
+
// Security rules
|
|
116
|
+
"typescript_s001": s001,
|
|
117
|
+
"typescript_s002": s002,
|
|
118
|
+
"typescript_s003": s003,
|
|
119
|
+
"typescript_s005": s005,
|
|
120
|
+
"typescript_s006": s006,
|
|
121
|
+
"typescript_s007": s007,
|
|
122
|
+
"typescript_s008": s008,
|
|
123
|
+
"typescript_s009": s009,
|
|
124
|
+
"typescript_s010": s010,
|
|
125
|
+
"typescript_s011": s011,
|
|
126
|
+
"typescript_s012": s012,
|
|
127
|
+
"typescript_s013": s013,
|
|
128
|
+
"typescript_s014": s014,
|
|
129
|
+
"typescript_s015": s015,
|
|
130
|
+
"typescript_s016": s016,
|
|
131
|
+
"typescript_s017": s017,
|
|
132
|
+
"typescript_s018": s018,
|
|
133
|
+
"typescript_s019": s019,
|
|
134
|
+
"typescript_s020": s020,
|
|
135
|
+
"typescript_s022": s022,
|
|
136
|
+
"typescript_s023": s023,
|
|
137
|
+
"typescript_s025": s025,
|
|
138
|
+
"typescript_s026": s026,
|
|
139
|
+
"typescript_s027": s027,
|
|
140
|
+
"typescript_s029": s029,
|
|
141
|
+
"typescript_s030": s030,
|
|
142
|
+
"typescript_s033": s033,
|
|
143
|
+
"typescript_s034": s034,
|
|
144
|
+
"typescript_s035": s035,
|
|
145
|
+
"typescript_s036": s036,
|
|
146
|
+
"typescript_s037": s037,
|
|
147
|
+
"typescript_s038": s038,
|
|
148
|
+
"typescript_s039": s039,
|
|
149
|
+
"typescript_s041": s041,
|
|
150
|
+
"typescript_s042": s042,
|
|
151
|
+
"typescript_s043": s043,
|
|
152
|
+
"typescript_s044": s044,
|
|
153
|
+
"typescript_s045": s045,
|
|
154
|
+
"typescript_s046": s046,
|
|
155
|
+
"typescript_s047": s047,
|
|
156
|
+
"typescript_s048": s048,
|
|
157
|
+
"typescript_s050": s050,
|
|
158
|
+
"typescript_s052": s052,
|
|
159
|
+
"typescript_s054": s054,
|
|
160
|
+
"typescript_s055": s055,
|
|
161
|
+
"typescript_s057": s057,
|
|
162
|
+
"typescript_s058": s058
|
|
163
|
+
}
|
|
164
|
+
};
|
|
@@ -56,9 +56,18 @@ const c006Rule = {
|
|
|
56
56
|
'show', 'hide', 'display', 'render', 'draw', 'paint',
|
|
57
57
|
'connect', 'disconnect', 'link', 'unlink', 'attach', 'detach',
|
|
58
58
|
'enable', 'disable', 'activate', 'deactivate', 'toggle',
|
|
59
|
-
'is', 'has', 'can', 'should', 'will', 'must', 'may',
|
|
59
|
+
'is', 'has', 'can', 'should', 'will', 'must', 'may', 'does',
|
|
60
60
|
'handle', 'manage', 'control', 'execute', 'run', 'invoke',
|
|
61
|
-
'reset', 'clear', 'clean', 'refresh', 'reload', 'restore'
|
|
61
|
+
'reset', 'clear', 'clean', 'refresh', 'reload', 'restore',
|
|
62
|
+
|
|
63
|
+
// Based on user feedback - missing important verbs
|
|
64
|
+
'count', 'reopen', 'request', 'use', 'go', 'retry', 'redirect',
|
|
65
|
+
|
|
66
|
+
// Event handler prefixes
|
|
67
|
+
'on',
|
|
68
|
+
|
|
69
|
+
// Common patterns that should be allowed as verbs
|
|
70
|
+
'process', // can be both noun and verb - when standalone should be allowed
|
|
62
71
|
]);
|
|
63
72
|
|
|
64
73
|
const allowedPrefixes = new Set([
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ESLint rule for: C013 – Do not leave dead code commented out
|
|
3
|
+
* Rule ID: custom/c013
|
|
4
|
+
* Purpose: Prevent commented-out code from being left in the codebase to maintain cleanliness
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
meta: {
|
|
9
|
+
type: "suggestion",
|
|
10
|
+
docs: {
|
|
11
|
+
description: "Do not leave dead code commented out",
|
|
12
|
+
recommended: false
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
deadCode: "Unreachable code detected after return statement. Remove dead code or restructure logic.",
|
|
17
|
+
commentedCode: "Do not leave dead code commented out. Remove it or use version control to track changes."
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
create(context) {
|
|
21
|
+
function isTerminatingStatement(stmt) {
|
|
22
|
+
return stmt.type === "ReturnStatement" ||
|
|
23
|
+
stmt.type === "ThrowStatement" ||
|
|
24
|
+
stmt.type === "ContinueStatement" ||
|
|
25
|
+
stmt.type === "BreakStatement";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isExecutableStatement(stmt) {
|
|
29
|
+
// Exclude declarations and empty statements
|
|
30
|
+
return stmt.type !== "EmptyStatement" &&
|
|
31
|
+
stmt.type !== "FunctionDeclaration" &&
|
|
32
|
+
stmt.type !== "VariableDeclaration" &&
|
|
33
|
+
!stmt.type.includes("Declaration");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function checkBlockForUnreachableCode(node) {
|
|
37
|
+
let unreachable = false;
|
|
38
|
+
for (let i = 0; i < node.body.length; i++) {
|
|
39
|
+
const stmt = node.body[i];
|
|
40
|
+
|
|
41
|
+
if (unreachable && isExecutableStatement(stmt)) {
|
|
42
|
+
context.report({
|
|
43
|
+
node: stmt,
|
|
44
|
+
messageId: "deadCode"
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (isTerminatingStatement(stmt)) {
|
|
49
|
+
unreachable = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
// Handle unreachable code in all block statements
|
|
56
|
+
BlockStatement: checkBlockForUnreachableCode,
|
|
57
|
+
|
|
58
|
+
// Handle switch cases
|
|
59
|
+
SwitchCase(node) {
|
|
60
|
+
let unreachable = false;
|
|
61
|
+
for (let i = 0; i < node.consequent.length; i++) {
|
|
62
|
+
const stmt = node.consequent[i];
|
|
63
|
+
|
|
64
|
+
if (unreachable && isExecutableStatement(stmt)) {
|
|
65
|
+
context.report({
|
|
66
|
+
node: stmt,
|
|
67
|
+
messageId: "deadCode"
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (isTerminatingStatement(stmt)) {
|
|
72
|
+
unreachable = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ESLint rule for: C017 – Limit constructor logic
|
|
3
|
+
* Rule ID: custom/c017
|
|
4
|
+
* Purpose: Enforce minimal logic in constructors to maintain clean initialization
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
meta: {
|
|
9
|
+
type: "suggestion",
|
|
10
|
+
docs: {
|
|
11
|
+
description: "Limit constructor logic",
|
|
12
|
+
recommended: false
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
constructorLogic: "Constructor contains complex logic: {{description}}. Move to initialization methods",
|
|
17
|
+
tooManyStatements: "Constructor has too many statements ({{count}}). Consider simplifying or moving logic to initialization methods"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
create(context) {
|
|
21
|
+
function isSimpleAssignment(node) {
|
|
22
|
+
// Simple property assignments like this.prop = value
|
|
23
|
+
if (node.type === "ExpressionStatement" &&
|
|
24
|
+
node.expression.type === "AssignmentExpression" &&
|
|
25
|
+
node.expression.left.type === "MemberExpression" &&
|
|
26
|
+
node.expression.left.object.type === "ThisExpression") {
|
|
27
|
+
|
|
28
|
+
// Check if right side is a simple value (not complex computation)
|
|
29
|
+
const right = node.expression.right;
|
|
30
|
+
return right.type === "Literal" ||
|
|
31
|
+
right.type === "Identifier" ||
|
|
32
|
+
(right.type === "MemberExpression" && right.property.name === "length") ||
|
|
33
|
+
(right.type === "CallExpression" && right.callee.type === "Identifier" &&
|
|
34
|
+
['require', 'process'].includes(right.callee.name));
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isSimpleDeclaration(node) {
|
|
40
|
+
// Simple variable declarations
|
|
41
|
+
return node.type === "VariableDeclaration";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isSuperCall(node) {
|
|
45
|
+
// super() calls
|
|
46
|
+
return node.type === "ExpressionStatement" &&
|
|
47
|
+
node.expression.type === "CallExpression" &&
|
|
48
|
+
node.expression.callee.type === "Super";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isComplexLogic(node) {
|
|
52
|
+
// Complex patterns that shouldn't be in constructor
|
|
53
|
+
switch (node.type) {
|
|
54
|
+
case "IfStatement":
|
|
55
|
+
case "WhileStatement":
|
|
56
|
+
case "ForStatement":
|
|
57
|
+
case "SwitchStatement":
|
|
58
|
+
case "TryStatement":
|
|
59
|
+
return { type: "control_flow", description: "control flow statements" };
|
|
60
|
+
|
|
61
|
+
case "ExpressionStatement":
|
|
62
|
+
if (node.expression.type === "CallExpression") {
|
|
63
|
+
const callee = node.expression.callee;
|
|
64
|
+
|
|
65
|
+
// Method calls that aren't simple setters
|
|
66
|
+
if (callee.type === "MemberExpression") {
|
|
67
|
+
const methodName = callee.property.name;
|
|
68
|
+
|
|
69
|
+
// Allow simple MobX observable setup
|
|
70
|
+
if (methodName === "makeObservable" || methodName === "makeAutoObservable") {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Flag complex method calls
|
|
75
|
+
if (!['push', 'set', 'add'].includes(methodName)) {
|
|
76
|
+
return { type: "method_call", description: "complex method calls" };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Direct function calls (not method calls)
|
|
81
|
+
if (callee.type === "Identifier" &&
|
|
82
|
+
!['require', 'parseInt', 'parseFloat', 'Boolean', 'Number', 'String'].includes(callee.name)) {
|
|
83
|
+
return { type: "function_call", description: "function calls" };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Complex assignments with computations
|
|
88
|
+
if (node.expression.type === "AssignmentExpression") {
|
|
89
|
+
const right = node.expression.right;
|
|
90
|
+
if (right.type === "BinaryExpression" ||
|
|
91
|
+
right.type === "ConditionalExpression" ||
|
|
92
|
+
(right.type === "CallExpression" &&
|
|
93
|
+
right.callee.type === "MemberExpression" &&
|
|
94
|
+
!['map', 'filter', 'toString', 'slice'].includes(right.callee.property.name))) {
|
|
95
|
+
return { type: "complex_assignment", description: "complex computations" };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
MethodDefinition(node) {
|
|
106
|
+
if (node.kind === "constructor" && node.value && node.value.body) {
|
|
107
|
+
const statements = node.value.body.body;
|
|
108
|
+
let complexLogicCount = 0;
|
|
109
|
+
|
|
110
|
+
for (const stmt of statements) {
|
|
111
|
+
// Skip simple assignments, declarations, and super calls
|
|
112
|
+
if (isSimpleAssignment(stmt) ||
|
|
113
|
+
isSimpleDeclaration(stmt) ||
|
|
114
|
+
isSuperCall(stmt)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check for complex logic
|
|
119
|
+
const complexity = isComplexLogic(stmt);
|
|
120
|
+
if (complexity) {
|
|
121
|
+
context.report({
|
|
122
|
+
node: stmt,
|
|
123
|
+
messageId: "constructorLogic",
|
|
124
|
+
data: {
|
|
125
|
+
description: complexity.description
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
complexLogicCount++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Also flag constructors with too many statements overall
|
|
133
|
+
if (statements.length > 10) {
|
|
134
|
+
context.report({
|
|
135
|
+
node: node,
|
|
136
|
+
messageId: "tooManyStatements",
|
|
137
|
+
data: {
|
|
138
|
+
count: statements.length
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
};
|
|
@@ -42,6 +42,41 @@ module.exports = {
|
|
|
42
42
|
return true;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Check for test assertions (Jest patterns)
|
|
46
|
+
if (stmt.type === "ExpressionStatement" &&
|
|
47
|
+
stmt.expression.type === "CallExpression" &&
|
|
48
|
+
stmt.expression.callee &&
|
|
49
|
+
stmt.expression.callee.name === "expect") {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check for Redux thunk error handling patterns
|
|
54
|
+
if (stmt.type === "VariableDeclaration" &&
|
|
55
|
+
stmt.declarations.some(decl =>
|
|
56
|
+
decl.init &&
|
|
57
|
+
decl.init.type === "CallExpression" &&
|
|
58
|
+
decl.init.callee &&
|
|
59
|
+
decl.init.callee.name === "handleAxiosError")) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check for return rejectWithValue
|
|
64
|
+
if (stmt.type === "ReturnStatement" &&
|
|
65
|
+
stmt.argument &&
|
|
66
|
+
stmt.argument.type === "CallExpression" &&
|
|
67
|
+
stmt.argument.callee &&
|
|
68
|
+
stmt.argument.callee.name === "rejectWithValue") {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check for dispatch calls (Redux patterns)
|
|
73
|
+
if (stmt.type === "ExpressionStatement" &&
|
|
74
|
+
stmt.expression.type === "CallExpression" &&
|
|
75
|
+
stmt.expression.callee &&
|
|
76
|
+
stmt.expression.callee.name === "dispatch") {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
45
80
|
// Check for console.log, console.error, console.warn
|
|
46
81
|
if (stmt.type === "ExpressionStatement" &&
|
|
47
82
|
stmt.expression.type === "CallExpression" &&
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ESLint rule for: C035 – No empty catch blocks (without error handling or logging)
|
|
3
|
+
* Rule ID: custom/c035
|
|
4
|
+
* Purpose: Prevent silently swallowing errors in catch blocks without logging or handling them
|
|
5
|
+
* Note: Primarily intended for backend code. Frontend may handle errors through UI.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
meta: {
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
description: "No empty catch blocks (without error handling or logging)",
|
|
13
|
+
recommended: true
|
|
14
|
+
},
|
|
15
|
+
schema: [
|
|
16
|
+
{
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
allowFrontend: {
|
|
20
|
+
type: "boolean",
|
|
21
|
+
default: true
|
|
22
|
+
},
|
|
23
|
+
allowIgnoredParams: {
|
|
24
|
+
type: "boolean",
|
|
25
|
+
default: true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
additionalProperties: false
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
messages: {
|
|
32
|
+
emptyCatch: "Empty catch blocks are not allowed. Errors should be logged or handled explicitly.",
|
|
33
|
+
emptyCatchBackend: "Backend code must log errors in catch blocks. Consider using console.error(), logger, or proper error handling."
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
create(context) {
|
|
37
|
+
const options = context.options[0] || {};
|
|
38
|
+
const allowFrontend = options.allowFrontend !== false;
|
|
39
|
+
const allowIgnoredParams = options.allowIgnoredParams !== false;
|
|
40
|
+
|
|
41
|
+
function isBackendFile(filename) {
|
|
42
|
+
// Heuristics to determine if it's a backend file
|
|
43
|
+
const backendPatterns = [
|
|
44
|
+
/server/i, /backend/i, /api/i, /service/i,
|
|
45
|
+
/controller/i, /model/i, /dao/i, /repository/i,
|
|
46
|
+
/middleware/i, /route/i, /endpoint/i
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
return backendPatterns.some(pattern => pattern.test(filename));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isFrontendFile(filename) {
|
|
53
|
+
// Heuristics to determine if it's a frontend file
|
|
54
|
+
const frontendPatterns = [
|
|
55
|
+
/component/i, /page/i, /screen/i, /view/i,
|
|
56
|
+
/ui/i, /frontend/i, /client/i, /app/i,
|
|
57
|
+
/hook/i, /context/i, /store/i, /reducer/i
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
return frontendPatterns.some(pattern => pattern.test(filename));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isIntentionallyIgnored(param) {
|
|
64
|
+
if (!param || !param.name) return false;
|
|
65
|
+
|
|
66
|
+
const ignoredPatterns = [
|
|
67
|
+
'ignored', '_', '__', 'unused', 'ignore',
|
|
68
|
+
'_error', '_err', '_e', 'ignored_error'
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const paramName = param.name.toLowerCase();
|
|
72
|
+
return ignoredPatterns.some(pattern =>
|
|
73
|
+
paramName === pattern || paramName.includes('ignored')
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function hasFrontendErrorHandling(body) {
|
|
78
|
+
// Check for common frontend error handling patterns
|
|
79
|
+
const bodyText = context.getSourceCode().getText(body);
|
|
80
|
+
const frontendPatterns = [
|
|
81
|
+
/show.*error/i, /display.*error/i, /toast/i, /alert/i,
|
|
82
|
+
/notification/i, /modal/i, /snackbar/i, /message/i,
|
|
83
|
+
/set.*error/i, /error.*state/i, /ui.*error/i
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
return frontendPatterns.some(pattern => pattern.test(bodyText));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
CatchClause(node) {
|
|
91
|
+
const filename = context.getFilename();
|
|
92
|
+
const body = node.body && node.body.body;
|
|
93
|
+
const param = node.param;
|
|
94
|
+
|
|
95
|
+
// Allow intentionally ignored parameters
|
|
96
|
+
if (allowIgnoredParams && isIntentionallyIgnored(param)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check if catch block is empty
|
|
101
|
+
if (!Array.isArray(body) || body.length === 0) {
|
|
102
|
+
const isBackend = isBackendFile(filename);
|
|
103
|
+
const isFrontend = isFrontendFile(filename);
|
|
104
|
+
|
|
105
|
+
// For backend files, always report
|
|
106
|
+
if (isBackend) {
|
|
107
|
+
context.report({
|
|
108
|
+
node,
|
|
109
|
+
messageId: "emptyCatchBackend"
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// For frontend files, be more lenient if option is set
|
|
115
|
+
if (isFrontend && allowFrontend) {
|
|
116
|
+
return; // Allow empty catch in frontend
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Default behavior for unclassified files
|
|
120
|
+
context.report({
|
|
121
|
+
node,
|
|
122
|
+
messageId: "emptyCatch"
|
|
123
|
+
});
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check for proper error handling in non-empty blocks
|
|
128
|
+
const hasLogging = body.some(statement => {
|
|
129
|
+
const text = context.getSourceCode().getText(statement);
|
|
130
|
+
return /console\.(error|warn|log)|logger\.|log\(|throw\s/i.test(text);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const isBackend = isBackendFile(filename);
|
|
134
|
+
const isFrontend = isFrontendFile(filename);
|
|
135
|
+
|
|
136
|
+
// Backend should have logging or re-throwing
|
|
137
|
+
if (isBackend && !hasLogging) {
|
|
138
|
+
// Check if there's at least some error handling
|
|
139
|
+
const hasAnyHandling = body.some(statement => {
|
|
140
|
+
const text = context.getSourceCode().getText(statement);
|
|
141
|
+
return /error|err|exception|fail/i.test(text);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!hasAnyHandling) {
|
|
145
|
+
context.report({
|
|
146
|
+
node,
|
|
147
|
+
messageId: "emptyCatchBackend"
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Frontend can handle through UI
|
|
153
|
+
if (isFrontend && allowFrontend) {
|
|
154
|
+
const hasFrontendHandling = hasFrontendErrorHandling(node.body);
|
|
155
|
+
if (hasLogging || hasFrontendHandling) {
|
|
156
|
+
return; // OK for frontend
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
};
|