@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
|
@@ -47,48 +47,59 @@ class FileTargetingService {
|
|
|
47
47
|
*/
|
|
48
48
|
applyFiltering(files, config, cliOptions) {
|
|
49
49
|
let filteredFiles = [...files];
|
|
50
|
+
const debug = cliOptions?.debug || false;
|
|
51
|
+
|
|
52
|
+
if (debug) console.log(`🔍 [DEBUG] applyFiltering: start with ${filteredFiles.length} files`);
|
|
53
|
+
if (debug) console.log(`🔍 [DEBUG] config.include:`, config.include);
|
|
54
|
+
if (debug) console.log(`🔍 [DEBUG] cliOptions.include:`, cliOptions.include);
|
|
50
55
|
|
|
51
56
|
// 1. Apply config include patterns first (medium priority)
|
|
52
57
|
if (config.include && config.include.length > 0) {
|
|
53
|
-
filteredFiles = this.applyIncludePatterns(filteredFiles, config.include);
|
|
58
|
+
filteredFiles = this.applyIncludePatterns(filteredFiles, config.include, debug);
|
|
59
|
+
if (debug) console.log(`🔍 [DEBUG] After config include: ${filteredFiles.length} files`);
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
// 2. Apply CLI include overrides (highest priority - completely overrides config)
|
|
57
63
|
if (cliOptions.include) {
|
|
58
64
|
// CLI include completely replaces config include - start fresh from all files
|
|
59
|
-
filteredFiles = this.applyIncludePatterns([...files], cliOptions.include);
|
|
65
|
+
filteredFiles = this.applyIncludePatterns([...files], cliOptions.include, debug);
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
// 3. Apply config exclude patterns
|
|
63
69
|
if (config.exclude && config.exclude.length > 0) {
|
|
64
|
-
|
|
70
|
+
if (debug) console.log(`🔍 [DEBUG] About to apply config exclude patterns: ${config.exclude}`);
|
|
71
|
+
if (debug) console.log(`🔍 [DEBUG] Files before config exclude: ${filteredFiles.length}`);
|
|
72
|
+
filteredFiles = this.applyExcludePatterns(filteredFiles, config.exclude, debug);
|
|
73
|
+
if (debug) console.log(`🔍 [DEBUG] Files after config exclude: ${filteredFiles.length}`);
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
// 4. Apply CLI exclude overrides (highest priority)
|
|
68
77
|
if (cliOptions.exclude) {
|
|
69
|
-
|
|
78
|
+
if (debug) console.log(`🔍 [DEBUG] About to apply CLI exclude patterns: ${cliOptions.exclude}`);
|
|
79
|
+
if (debug) console.log(`🔍 [DEBUG] Files before CLI exclude: ${filteredFiles.length}`);
|
|
80
|
+
filteredFiles = this.applyExcludePatterns(filteredFiles, cliOptions.exclude, debug);
|
|
81
|
+
if (debug) console.log(`🔍 [DEBUG] Files after CLI exclude: ${filteredFiles.length}`);
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
// 5. Apply language-specific filtering
|
|
73
85
|
if (cliOptions.languages || config.languages) {
|
|
74
|
-
filteredFiles = this.applyLanguageFiltering(filteredFiles, config, cliOptions);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// 6. Apply legacy ignorePatterns (backward compatibility)
|
|
78
|
-
if (config.ignorePatterns && config.ignorePatterns.length > 0) {
|
|
79
|
-
filteredFiles = this.applyExcludePatterns(filteredFiles, config.ignorePatterns);
|
|
86
|
+
filteredFiles = this.applyLanguageFiltering(filteredFiles, config, cliOptions, debug);
|
|
87
|
+
if (debug) console.log(`🔍 [DEBUG] After language filtering: ${filteredFiles.length} files`);
|
|
80
88
|
}
|
|
81
89
|
|
|
82
|
-
//
|
|
90
|
+
// 6. Apply only-source filtering (exclude tests, configs, etc.)
|
|
83
91
|
if (cliOptions.onlySource) {
|
|
84
92
|
filteredFiles = this.applyOnlySourceFiltering(filteredFiles);
|
|
93
|
+
if (debug) console.log(`🔍 [DEBUG] After onlySource filtering: ${filteredFiles.length} files`);
|
|
85
94
|
} else {
|
|
86
95
|
// 8. Handle test files normally
|
|
87
96
|
if (config.testPatterns) {
|
|
88
97
|
filteredFiles = this.handleTestFiles(filteredFiles, config.testPatterns, cliOptions, config);
|
|
98
|
+
if (debug) console.log(`🔍 [DEBUG] After test files handling: ${filteredFiles.length} files`);
|
|
89
99
|
}
|
|
90
100
|
}
|
|
91
101
|
|
|
102
|
+
if (debug) console.log(`🔍 [DEBUG] Final filtered files: ${filteredFiles.length}`);
|
|
92
103
|
return filteredFiles;
|
|
93
104
|
}
|
|
94
105
|
|
|
@@ -96,7 +107,11 @@ class FileTargetingService {
|
|
|
96
107
|
* Apply language-specific filtering
|
|
97
108
|
* Rule C005: Single responsibility - language filtering only
|
|
98
109
|
*/
|
|
99
|
-
applyLanguageFiltering(files, config, cliOptions) {
|
|
110
|
+
applyLanguageFiltering(files, config, cliOptions, debug = false) {
|
|
111
|
+
if (debug) console.log(`🔍 [DEBUG] === applyLanguageFiltering ENTRY ===`);
|
|
112
|
+
if (debug) console.log(`🔍 [DEBUG] Input files.length: ${files.length}`);
|
|
113
|
+
if (debug) console.log(`🔍 [DEBUG] Sample input files:`, files.slice(0, 3));
|
|
114
|
+
|
|
100
115
|
// Determine target languages from CLI or config
|
|
101
116
|
let targetLanguages;
|
|
102
117
|
if (cliOptions.languages) {
|
|
@@ -107,78 +122,118 @@ class FileTargetingService {
|
|
|
107
122
|
targetLanguages = Object.keys(config.languages || {});
|
|
108
123
|
}
|
|
109
124
|
|
|
125
|
+
if (debug) console.log(`🔍 [DEBUG] applyLanguageFiltering: cliOptions.languages = ${cliOptions.languages}`);
|
|
126
|
+
if (debug) console.log(`🔍 [DEBUG] applyLanguageFiltering: config.languages =`, config.languages);
|
|
127
|
+
if (debug) console.log(`🔍 [DEBUG] applyLanguageFiltering: targetLanguages =`, targetLanguages);
|
|
128
|
+
|
|
110
129
|
if (targetLanguages.length === 0) {
|
|
130
|
+
if (debug) console.log(`🔍 [DEBUG] applyLanguageFiltering: No language filtering, returning all files`);
|
|
111
131
|
return files; // No language filtering
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
let languageFiles = [];
|
|
115
135
|
|
|
116
136
|
for (const language of targetLanguages) {
|
|
137
|
+
if (debug) console.log(`🔍 [DEBUG] Processing language: ${language}`);
|
|
117
138
|
if (Array.isArray(config.languages)) {
|
|
118
139
|
// New array format - use isLanguageFile method
|
|
119
140
|
const langFiles = files.filter(file => this.isLanguageFile(file, language));
|
|
120
141
|
languageFiles.push(...langFiles);
|
|
142
|
+
if (debug) console.log(`🔍 [DEBUG] Array format - found ${langFiles.length} files for ${language}`);
|
|
121
143
|
} else {
|
|
122
144
|
// Legacy object format - use include/exclude patterns
|
|
123
145
|
const langConfig = config.languages[language];
|
|
124
|
-
if (!langConfig)
|
|
146
|
+
if (!langConfig) {
|
|
147
|
+
if (debug) console.log(`🔍 [DEBUG] No config for language: ${language}`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
125
150
|
|
|
126
151
|
let langFiles = [...files];
|
|
152
|
+
if (debug) console.log(`🔍 [DEBUG] Starting with ${langFiles.length} files for ${language}`);
|
|
127
153
|
|
|
128
154
|
// Apply language-specific include patterns
|
|
129
155
|
if (langConfig.include && langConfig.include.length > 0) {
|
|
130
|
-
langFiles = this.applyIncludePatterns(langFiles, langConfig.include);
|
|
156
|
+
langFiles = this.applyIncludePatterns(langFiles, langConfig.include, debug);
|
|
157
|
+
if (debug) console.log(`🔍 [DEBUG] After include patterns ${langConfig.include}: ${langFiles.length} files`);
|
|
131
158
|
}
|
|
132
159
|
|
|
133
160
|
// Apply language-specific exclude patterns
|
|
134
161
|
if (langConfig.exclude && langConfig.exclude.length > 0) {
|
|
135
|
-
langFiles = this.applyExcludePatterns(langFiles, langConfig.exclude);
|
|
162
|
+
langFiles = this.applyExcludePatterns(langFiles, langConfig.exclude, debug);
|
|
163
|
+
if (debug) console.log(`🔍 [DEBUG] After exclude patterns ${langConfig.exclude}: ${langFiles.length} files`);
|
|
136
164
|
}
|
|
137
165
|
|
|
138
166
|
languageFiles.push(...langFiles);
|
|
167
|
+
if (debug) console.log(`🔍 [DEBUG] Added ${langFiles.length} files for ${language}, total: ${languageFiles.length}`);
|
|
139
168
|
}
|
|
140
169
|
}
|
|
141
170
|
|
|
142
171
|
// Remove duplicates
|
|
143
|
-
|
|
172
|
+
const finalFiles = [...new Set(languageFiles)];
|
|
173
|
+
if (debug) console.log(`🔍 [DEBUG] Final language files after dedup: ${finalFiles.length}`);
|
|
174
|
+
return finalFiles;
|
|
144
175
|
}
|
|
145
176
|
|
|
146
177
|
/**
|
|
147
178
|
* Apply include patterns using minimatch
|
|
148
179
|
* Rule C006: applyIncludePatterns - verb-noun naming
|
|
149
180
|
*/
|
|
150
|
-
applyIncludePatterns(files, patterns) {
|
|
181
|
+
applyIncludePatterns(files, patterns, debug = false) {
|
|
151
182
|
if (!patterns) return files;
|
|
152
183
|
|
|
153
184
|
// Normalize patterns to array
|
|
154
185
|
const patternArray = Array.isArray(patterns) ? patterns : [patterns];
|
|
155
186
|
if (patternArray.length === 0) return files;
|
|
156
187
|
|
|
157
|
-
|
|
188
|
+
if (debug) console.log(`🔍 [DEBUG] applyIncludePatterns - input files:`, files.length);
|
|
189
|
+
if (debug) console.log(`🔍 [DEBUG] applyIncludePatterns - patterns:`, patternArray);
|
|
190
|
+
if (debug) console.log(`🔍 [DEBUG] applyIncludePatterns - sample input files:`, files.slice(0, 3));
|
|
191
|
+
|
|
192
|
+
const result = files.filter(file => {
|
|
158
193
|
return patternArray.some(pattern => {
|
|
159
194
|
const normalizedFile = this.normalizePath(file);
|
|
160
|
-
|
|
195
|
+
const match = minimatch(normalizedFile, pattern, { dot: true });
|
|
196
|
+
if (debug && file.includes('.ts') && !file.includes('.test.')) {
|
|
197
|
+
console.log(`🔍 [DEBUG] Testing: '${file}' -> '${normalizedFile}' vs '${pattern}' = ${match}`);
|
|
198
|
+
}
|
|
199
|
+
return match;
|
|
161
200
|
});
|
|
162
201
|
});
|
|
202
|
+
|
|
203
|
+
if (debug) console.log(`🔍 [DEBUG] applyIncludePatterns - result:`, result.length);
|
|
204
|
+
return result;
|
|
163
205
|
}
|
|
164
206
|
|
|
165
207
|
/**
|
|
166
208
|
* Apply exclude patterns using minimatch
|
|
167
209
|
* Rule C006: applyExcludePatterns - verb-noun naming
|
|
168
210
|
*/
|
|
169
|
-
applyExcludePatterns(files, patterns) {
|
|
211
|
+
applyExcludePatterns(files, patterns, debug = false) {
|
|
170
212
|
if (!patterns) return files;
|
|
171
213
|
|
|
172
214
|
// Normalize patterns to array
|
|
173
215
|
const patternArray = Array.isArray(patterns) ? patterns : [patterns];
|
|
174
216
|
if (patternArray.length === 0) return files;
|
|
175
217
|
|
|
176
|
-
|
|
177
|
-
|
|
218
|
+
// Filter out negation patterns (starting with !) - these should not be in exclude patterns
|
|
219
|
+
const excludePatterns = patternArray.filter(pattern => !pattern.startsWith('!'));
|
|
220
|
+
|
|
221
|
+
if (debug) console.log(`🔍 [DEBUG] applyExcludePatterns - input files: ${files.length}`);
|
|
222
|
+
if (debug) console.log(`🔍 [DEBUG] applyExcludePatterns - original patterns:`, patternArray);
|
|
223
|
+
if (debug) console.log(`🔍 [DEBUG] applyExcludePatterns - filtered patterns:`, excludePatterns);
|
|
224
|
+
|
|
225
|
+
if (excludePatterns.length === 0) return files;
|
|
226
|
+
|
|
227
|
+
const result = files.filter(file => {
|
|
228
|
+
return !excludePatterns.some(pattern => {
|
|
178
229
|
const normalizedFile = this.normalizePath(file);
|
|
179
|
-
|
|
230
|
+
const match = minimatch(normalizedFile, pattern, { dot: true });
|
|
231
|
+
return match;
|
|
180
232
|
});
|
|
181
233
|
});
|
|
234
|
+
|
|
235
|
+
if (debug) console.log(`🔍 [DEBUG] applyExcludePatterns - result: ${result.length}`);
|
|
236
|
+
return result;
|
|
182
237
|
}
|
|
183
238
|
|
|
184
239
|
/**
|
|
@@ -213,13 +268,22 @@ class FileTargetingService {
|
|
|
213
268
|
*/
|
|
214
269
|
async collectFiles(inputPath) {
|
|
215
270
|
const files = [];
|
|
216
|
-
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const stats = fs.statSync(inputPath);
|
|
217
274
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
275
|
+
if (stats.isFile()) {
|
|
276
|
+
files.push(path.resolve(inputPath));
|
|
277
|
+
} else if (stats.isDirectory()) {
|
|
278
|
+
const dirFiles = await this.collectFilesFromDirectory(inputPath);
|
|
279
|
+
files.push(...dirFiles);
|
|
280
|
+
}
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (error.code === 'ENOENT') {
|
|
283
|
+
console.warn(`⚠️ Path not found: ${inputPath}`);
|
|
284
|
+
return files; // Return empty array instead of throwing
|
|
285
|
+
}
|
|
286
|
+
throw error; // Re-throw other errors
|
|
223
287
|
}
|
|
224
288
|
|
|
225
289
|
return files;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analysis Engine Interface
|
|
3
|
+
* Following Rule C014: Dependency Injection interface
|
|
4
|
+
* Following Rule C015: Use domain language - clear interface naming
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class AnalysisEngineInterface {
|
|
8
|
+
/**
|
|
9
|
+
* Constructor for Analysis Engine
|
|
10
|
+
* @param {string} id - Engine ID (e.g., 'eslint', 'heuristic', 'openai')
|
|
11
|
+
* @param {string} version - Engine version
|
|
12
|
+
* @param {string[]} supportedLanguages - Array of supported languages
|
|
13
|
+
*/
|
|
14
|
+
constructor(id, version, supportedLanguages = []) {
|
|
15
|
+
this.id = id;
|
|
16
|
+
this.version = version;
|
|
17
|
+
this.supportedLanguages = supportedLanguages;
|
|
18
|
+
this.initialized = false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialize the analysis engine
|
|
23
|
+
* Following Rule C006: Verb-noun naming
|
|
24
|
+
* @param {Object} config - Engine-specific configuration
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
27
|
+
async initialize(config) {
|
|
28
|
+
throw new Error(`Method initialize() must be implemented by ${this.constructor.name}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Analyze files with given rules
|
|
33
|
+
* Following Rule C006: Verb-noun naming
|
|
34
|
+
* @param {string[]} files - Array of file paths to analyze
|
|
35
|
+
* @param {Object[]} rules - Array of rule objects to apply
|
|
36
|
+
* @param {Object} options - Analysis options
|
|
37
|
+
* @returns {Promise<Object>} Analysis results
|
|
38
|
+
*/
|
|
39
|
+
async analyze(files, rules, options) {
|
|
40
|
+
throw new Error(`Method analyze() must be implemented by ${this.constructor.name}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get supported rules for this engine
|
|
45
|
+
* Following Rule C006: Verb-noun naming
|
|
46
|
+
* @returns {string[]} Array of supported rule IDs
|
|
47
|
+
*/
|
|
48
|
+
getSupportedRules() {
|
|
49
|
+
throw new Error(`Method getSupportedRules() must be implemented by ${this.constructor.name}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a specific rule is supported
|
|
54
|
+
* Following Rule C006: Verb-noun naming
|
|
55
|
+
* @param {string} ruleId - Rule ID to check
|
|
56
|
+
* @returns {boolean} True if rule is supported
|
|
57
|
+
*/
|
|
58
|
+
isRuleSupported(ruleId) {
|
|
59
|
+
return this.getSupportedRules().includes(ruleId);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Check if a language is supported
|
|
64
|
+
* Following Rule C006: Verb-noun naming
|
|
65
|
+
* @param {string} language - Language to check
|
|
66
|
+
* @returns {boolean} True if language is supported
|
|
67
|
+
*/
|
|
68
|
+
isLanguageSupported(language) {
|
|
69
|
+
return this.supportedLanguages.includes(language) ||
|
|
70
|
+
this.supportedLanguages.includes('all') ||
|
|
71
|
+
this.supportedLanguages.length === 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get engine metadata
|
|
76
|
+
* Following Rule C006: Verb-noun naming
|
|
77
|
+
* @returns {Object} Engine metadata
|
|
78
|
+
*/
|
|
79
|
+
getEngineInfo() {
|
|
80
|
+
return {
|
|
81
|
+
name: this.name,
|
|
82
|
+
version: this.version,
|
|
83
|
+
supportedLanguages: this.supportedLanguages,
|
|
84
|
+
supportedRules: this.getSupportedRules(),
|
|
85
|
+
initialized: this.initialized
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Cleanup engine resources
|
|
91
|
+
* Following Rule C006: Verb-noun naming
|
|
92
|
+
* @returns {Promise<void>}
|
|
93
|
+
*/
|
|
94
|
+
async cleanup() {
|
|
95
|
+
// Default implementation - engines can override if needed
|
|
96
|
+
this.initialized = false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = AnalysisEngineInterface;
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const glob = require('glob');
|
|
4
|
-
const chalk = require('chalk');
|
|
5
|
-
const FileTargetingService = require('./file-targeting-service');
|
|
6
|
-
|
|
7
|
-
class MultiRuleRunner {
|
|
8
|
-
constructor(config, options) {
|
|
9
|
-
this.config = config;
|
|
10
|
-
this.options = options;
|
|
11
|
-
this.ruleCache = new Map();
|
|
12
|
-
this.fileTargetingService = new FileTargetingService();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async runRules(rules, inputPath, options) {
|
|
16
|
-
console.log(chalk.blue(`🔄 Analyzing ${rules.length} rules...`));
|
|
17
|
-
|
|
18
|
-
// Get files to analyze
|
|
19
|
-
const files = await this.getFilesToAnalyze(inputPath, options);
|
|
20
|
-
|
|
21
|
-
console.log(chalk.gray(`📁 Found ${files ? files.length : 'undefined'} files to analyze`));
|
|
22
|
-
|
|
23
|
-
if (!files || files.length === 0) {
|
|
24
|
-
console.log(chalk.yellow('⚠️ No files found to analyze'));
|
|
25
|
-
return [];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Group files by language
|
|
29
|
-
const filesByLanguage = this.groupFilesByLanguage(files);
|
|
30
|
-
|
|
31
|
-
// Run rules in parallel (with concurrency limit)
|
|
32
|
-
const maxConcurrent = parseInt(options.maxConcurrent) || 5;
|
|
33
|
-
const timeout = parseInt(options.timeout) || 30000;
|
|
34
|
-
|
|
35
|
-
const results = [];
|
|
36
|
-
|
|
37
|
-
// Process rules in batches
|
|
38
|
-
for (let i = 0; i < rules.length; i += maxConcurrent) {
|
|
39
|
-
const batch = rules.slice(i, i + maxConcurrent);
|
|
40
|
-
|
|
41
|
-
const batchPromises = batch.map(async (rule) => {
|
|
42
|
-
try {
|
|
43
|
-
return await this.runSingleRule(rule, filesByLanguage, options, timeout);
|
|
44
|
-
} catch (error) {
|
|
45
|
-
console.error(chalk.red(`❌ Rule ${rule.id} failed:`), error.message);
|
|
46
|
-
return {
|
|
47
|
-
ruleId: rule.id,
|
|
48
|
-
ruleName: rule.name,
|
|
49
|
-
violations: [],
|
|
50
|
-
error: error.message,
|
|
51
|
-
status: 'failed'
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const batchResults = await Promise.all(batchPromises);
|
|
57
|
-
results.push(...batchResults);
|
|
58
|
-
|
|
59
|
-
if (options.verbose) {
|
|
60
|
-
console.log(chalk.gray(`✅ Completed batch ${Math.floor(i/maxConcurrent) + 1}/${Math.ceil(rules.length/maxConcurrent)}`));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return this.consolidateResults(results, files.length);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async runSingleRule(rule, filesByLanguage, options, timeout) {
|
|
68
|
-
if (options.verbose) {
|
|
69
|
-
console.log(chalk.gray(`🔍 Running rule ${rule.id}: ${rule.name}`));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const startTime = Date.now();
|
|
73
|
-
|
|
74
|
-
// Load rule analyzer
|
|
75
|
-
const analyzer = await this.loadRuleAnalyzer(rule);
|
|
76
|
-
|
|
77
|
-
if (!analyzer) {
|
|
78
|
-
return {
|
|
79
|
-
ruleId: rule.id,
|
|
80
|
-
ruleName: rule.name,
|
|
81
|
-
violations: [],
|
|
82
|
-
error: 'Analyzer not found',
|
|
83
|
-
status: 'skipped'
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const violations = [];
|
|
88
|
-
|
|
89
|
-
// Run analyzer for each supported language
|
|
90
|
-
for (const language of rule.languages) {
|
|
91
|
-
const languageFiles = filesByLanguage[language] || [];
|
|
92
|
-
|
|
93
|
-
if (languageFiles.length === 0) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
// Run with timeout
|
|
99
|
-
const languageViolations = await Promise.race([
|
|
100
|
-
analyzer.analyze(languageFiles, language, this.config),
|
|
101
|
-
new Promise((_, reject) =>
|
|
102
|
-
setTimeout(() => reject(new Error('Timeout')), timeout)
|
|
103
|
-
)
|
|
104
|
-
]);
|
|
105
|
-
|
|
106
|
-
violations.push(...languageViolations);
|
|
107
|
-
} catch (error) {
|
|
108
|
-
console.error(chalk.yellow(`⚠️ Rule ${rule.id} failed for ${language}:`), error.message);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const duration = Date.now() - startTime;
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
ruleId: rule.id,
|
|
116
|
-
ruleName: rule.name,
|
|
117
|
-
category: rule.category,
|
|
118
|
-
severity: rule.severity,
|
|
119
|
-
violations,
|
|
120
|
-
filesAnalyzed: Object.values(filesByLanguage).flat().length,
|
|
121
|
-
duration,
|
|
122
|
-
status: 'completed'
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async loadRuleAnalyzer(rule) {
|
|
127
|
-
// Check cache first
|
|
128
|
-
if (this.ruleCache.has(rule.id)) {
|
|
129
|
-
return this.ruleCache.get(rule.id);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
const analyzerPath = path.resolve(__dirname, '..', rule.analyzer);
|
|
134
|
-
|
|
135
|
-
if (!fs.existsSync(analyzerPath)) {
|
|
136
|
-
console.error(chalk.yellow(`⚠️ Analyzer not found for rule ${rule.id}: ${analyzerPath}`));
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const analyzer = require(analyzerPath);
|
|
141
|
-
this.ruleCache.set(rule.id, analyzer);
|
|
142
|
-
return analyzer;
|
|
143
|
-
} catch (error) {
|
|
144
|
-
console.error(chalk.red(`❌ Failed to load analyzer for rule ${rule.id}:`), error.message);
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async getFilesToAnalyze(inputPath, options) {
|
|
150
|
-
// Use centralized file targeting service - convert single path to array
|
|
151
|
-
const inputPaths = Array.isArray(inputPath) ? inputPath : [inputPath];
|
|
152
|
-
const result = await this.fileTargetingService.getTargetFiles(inputPaths, this.config, options);
|
|
153
|
-
return result.files; // Extract files array from result object
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
groupFilesByLanguage(files) {
|
|
157
|
-
const rulesRegistry = require('../config/rules-registry.json');
|
|
158
|
-
const languageConfig = rulesRegistry.languages;
|
|
159
|
-
|
|
160
|
-
const filesByLanguage = {};
|
|
161
|
-
|
|
162
|
-
files.forEach(file => {
|
|
163
|
-
const ext = path.extname(file);
|
|
164
|
-
|
|
165
|
-
for (const [language, config] of Object.entries(languageConfig)) {
|
|
166
|
-
if (config.extensions.includes(ext)) {
|
|
167
|
-
if (!filesByLanguage[language]) {
|
|
168
|
-
filesByLanguage[language] = [];
|
|
169
|
-
}
|
|
170
|
-
filesByLanguage[language].push(file);
|
|
171
|
-
break; // File belongs to first matching language
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
return filesByLanguage;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
consolidateResults(results, totalFiles) {
|
|
180
|
-
const consolidatedResults = {
|
|
181
|
-
filesAnalyzed: totalFiles,
|
|
182
|
-
rulesRun: results.length,
|
|
183
|
-
totalViolations: 0,
|
|
184
|
-
violationsBySeverity: { error: 0, warning: 0, info: 0 },
|
|
185
|
-
violationsByRule: {},
|
|
186
|
-
violationsByFile: {},
|
|
187
|
-
results: []
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
results.forEach(result => {
|
|
191
|
-
consolidatedResults.results.push(result);
|
|
192
|
-
|
|
193
|
-
if (result.violations) {
|
|
194
|
-
consolidatedResults.totalViolations += result.violations.length;
|
|
195
|
-
|
|
196
|
-
// Count by severity
|
|
197
|
-
result.violations.forEach(violation => {
|
|
198
|
-
const severity = violation.severity || result.severity || 'warning';
|
|
199
|
-
consolidatedResults.violationsBySeverity[severity] =
|
|
200
|
-
(consolidatedResults.violationsBySeverity[severity] || 0) + 1;
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// Count by rule
|
|
204
|
-
consolidatedResults.violationsByRule[result.ruleId] = result.violations.length;
|
|
205
|
-
|
|
206
|
-
// Count by file
|
|
207
|
-
result.violations.forEach(violation => {
|
|
208
|
-
const file = violation.file || violation.filePath;
|
|
209
|
-
if (file) {
|
|
210
|
-
consolidatedResults.violationsByFile[file] =
|
|
211
|
-
(consolidatedResults.violationsByFile[file] || 0) + 1;
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
return consolidatedResults;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
module.exports = MultiRuleRunner;
|
package/core/output-service.js
CHANGED
|
@@ -35,7 +35,7 @@ class OutputService {
|
|
|
35
35
|
|
|
36
36
|
generateReport(results, metadata, options = {}) {
|
|
37
37
|
const allViolations = [];
|
|
38
|
-
let totalFiles = results.filesAnalyzed || results.totalFiles || results.fileCount || 0;
|
|
38
|
+
let totalFiles = results.filesAnalyzed || results.summary?.totalFiles || results.totalFiles || results.fileCount || 0;
|
|
39
39
|
|
|
40
40
|
// Collect all violations - handle both file-based and rule-based results
|
|
41
41
|
if (results.results) {
|
|
@@ -16,7 +16,7 @@ class RuleMappingService {
|
|
|
16
16
|
*/
|
|
17
17
|
createEslintToSunlintMapping() {
|
|
18
18
|
return {
|
|
19
|
-
// Custom rules mapping (using actual rule IDs from eslint
|
|
19
|
+
// Custom rules mapping (using actual rule IDs from integrations/eslint/plugin/rules)
|
|
20
20
|
'custom/c002': 'C002',
|
|
21
21
|
'custom/c003': 'C003',
|
|
22
22
|
'custom/c006': 'C006',
|
|
@@ -50,14 +50,18 @@ class RuleMappingService {
|
|
|
50
50
|
'custom/no-console-error': 'C019',
|
|
51
51
|
|
|
52
52
|
// Security rules mapping
|
|
53
|
+
'custom/typescript_s001': 'S001',
|
|
54
|
+
'custom/typescript_s002': 'S002',
|
|
53
55
|
'custom/typescript_s003': 'S003',
|
|
54
56
|
'custom/typescript_s005': 'S005',
|
|
55
57
|
'custom/typescript_s006': 'S006',
|
|
58
|
+
'custom/typescript_s007': 'S007',
|
|
56
59
|
'custom/typescript_s008': 'S008',
|
|
57
60
|
'custom/typescript_s009': 'S009',
|
|
58
61
|
'custom/typescript_s010': 'S010',
|
|
59
62
|
'custom/typescript_s011': 'S011',
|
|
60
63
|
'custom/typescript_s012': 'S012',
|
|
64
|
+
'custom/typescript_s013': 'S013',
|
|
61
65
|
'custom/typescript_s014': 'S014',
|
|
62
66
|
'custom/typescript_s015': 'S015',
|
|
63
67
|
'custom/typescript_s016': 'S016',
|
|
@@ -173,13 +177,17 @@ class RuleMappingService {
|
|
|
173
177
|
mapping['C076'] = ['custom/c076']; // One assert per test
|
|
174
178
|
|
|
175
179
|
// Security rules mapping
|
|
180
|
+
mapping['S001'] = ['custom/typescript_s001']; // Fail securely
|
|
181
|
+
mapping['S002'] = ['custom/typescript_s002']; // IDOR check
|
|
176
182
|
mapping['S005'] = ['custom/typescript_s005']; // No Origin header auth
|
|
177
183
|
mapping['S006'] = ['custom/typescript_s006']; // Activation recovery secret
|
|
184
|
+
mapping['S007'] = ['custom/typescript_s007']; // No plaintext OTP
|
|
178
185
|
mapping['S008'] = ['custom/typescript_s008']; // Crypto agility
|
|
179
186
|
mapping['S009'] = ['custom/typescript_s009']; // No insecure crypto
|
|
180
187
|
mapping['S010'] = ['custom/typescript_s010']; // No insecure random
|
|
181
188
|
mapping['S011'] = ['custom/typescript_s011']; // No insecure UUID
|
|
182
189
|
mapping['S012'] = ['custom/typescript_s012']; // No hardcoded secrets
|
|
190
|
+
mapping['S013'] = ['custom/typescript_s013']; // Verify TLS connection
|
|
183
191
|
mapping['S014'] = ['custom/typescript_s014']; // Insecure TLS version
|
|
184
192
|
mapping['S015'] = ['custom/typescript_s015']; // Insecure TLS certificate
|
|
185
193
|
mapping['S016'] = ['custom/typescript_s016']; // Sensitive query parameter
|
|
@@ -15,7 +15,7 @@ class RuleSelectionService {
|
|
|
15
15
|
async selectRules(config, options) {
|
|
16
16
|
// Load rules registry
|
|
17
17
|
try {
|
|
18
|
-
this.rulesRegistry = require('../config/rules-registry.json');
|
|
18
|
+
this.rulesRegistry = require('../config/rules/rules-registry.json');
|
|
19
19
|
} catch (error) {
|
|
20
20
|
console.log(chalk.yellow('⚠️ Rules registry not found, using minimal rule set'));
|
|
21
21
|
this.rulesRegistry = this.getMinimalRuleSet();
|
|
@@ -40,6 +40,14 @@ class RuleSelectionService {
|
|
|
40
40
|
if (options.verbose) {
|
|
41
41
|
console.log(chalk.blue(`📋 Found ${selectedRules.length} total rules (${registryRules.length} registry + ${eslintSupportedRules.length} ESLint)`));
|
|
42
42
|
}
|
|
43
|
+
} else if (options.quality) {
|
|
44
|
+
// Handle --quality shortcut
|
|
45
|
+
const categoryRules = this.getRulesByCategory('quality');
|
|
46
|
+
selectedRules = categoryRules;
|
|
47
|
+
} else if (options.security) {
|
|
48
|
+
// Handle --security shortcut
|
|
49
|
+
const categoryRules = this.getRulesByCategory('security');
|
|
50
|
+
selectedRules = categoryRules;
|
|
43
51
|
} else if (options.category) {
|
|
44
52
|
const categoryRules = this.getRulesByCategory(options.category);
|
|
45
53
|
selectedRules = categoryRules;
|
|
@@ -96,7 +104,7 @@ class RuleSelectionService {
|
|
|
96
104
|
'C043', 'C047', 'C048', 'C076', 'T002', 'T003', 'T004', 'T007', 'T011',
|
|
97
105
|
'T019', 'T025', 'T026'
|
|
98
106
|
],
|
|
99
|
-
'security': ['S003', 'S005', 'S006', 'S008', 'S009', 'S010', 'S011', 'S012', 'S014', 'S015', 'S016', 'S017', 'S018', 'S019', 'S020', 'S022', 'S023', 'S025', 'S026', 'S027', 'S029', 'S030', 'S033', 'S034', 'S035', 'S036', 'S037', 'S038', 'S039', 'S041', 'S042', 'S043', 'S044', 'S045', 'S046', 'S047', 'S048', 'S050', 'S052', 'S054', 'S055', 'S057', 'S058'],
|
|
107
|
+
'security': ['S001', 'S002', 'S003', 'S005', 'S006', 'S007', 'S008', 'S009', 'S010', 'S011', 'S012', 'S013', 'S014', 'S015', 'S016', 'S017', 'S018', 'S019', 'S020', 'S022', 'S023', 'S025', 'S026', 'S027', 'S029', 'S030', 'S033', 'S034', 'S035', 'S036', 'S037', 'S038', 'S039', 'S041', 'S042', 'S043', 'S044', 'S045', 'S046', 'S047', 'S048', 'S050', 'S052', 'S054', 'S055', 'S057', 'S058'],
|
|
100
108
|
'naming': ['C006'],
|
|
101
109
|
'logging': ['C019', 'S057'],
|
|
102
110
|
'validation': ['C031', 'S018', 'S025', 'S026']
|