@opensip-cli/checks-typescript 0.1.0
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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/__tests__/all-checks-execute.test.d.ts +12 -0
- package/dist/__tests__/all-checks-execute.test.d.ts.map +1 -0
- package/dist/__tests__/all-checks-execute.test.js +846 -0
- package/dist/__tests__/all-checks-execute.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-2.test.d.ts +9 -0
- package/dist/__tests__/behavior-fixtures-2.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-2.test.js +625 -0
- package/dist/__tests__/behavior-fixtures-2.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-3.test.d.ts +7 -0
- package/dist/__tests__/behavior-fixtures-3.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-3.test.js +658 -0
- package/dist/__tests__/behavior-fixtures-3.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-4.test.d.ts +8 -0
- package/dist/__tests__/behavior-fixtures-4.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-4.test.js +590 -0
- package/dist/__tests__/behavior-fixtures-4.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-5.test.d.ts +7 -0
- package/dist/__tests__/behavior-fixtures-5.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-5.test.js +548 -0
- package/dist/__tests__/behavior-fixtures-5.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures-6.test.d.ts +18 -0
- package/dist/__tests__/behavior-fixtures-6.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures-6.test.js +1700 -0
- package/dist/__tests__/behavior-fixtures-6.test.js.map +1 -0
- package/dist/__tests__/behavior-fixtures.test.d.ts +10 -0
- package/dist/__tests__/behavior-fixtures.test.d.ts.map +1 -0
- package/dist/__tests__/behavior-fixtures.test.js +812 -0
- package/dist/__tests__/behavior-fixtures.test.js.map +1 -0
- package/dist/__tests__/branch-fixtures-2.test.d.ts +6 -0
- package/dist/__tests__/branch-fixtures-2.test.d.ts.map +1 -0
- package/dist/__tests__/branch-fixtures-2.test.js +1369 -0
- package/dist/__tests__/branch-fixtures-2.test.js.map +1 -0
- package/dist/__tests__/branch-fixtures-3.test.d.ts +7 -0
- package/dist/__tests__/branch-fixtures-3.test.d.ts.map +1 -0
- package/dist/__tests__/branch-fixtures-3.test.js +877 -0
- package/dist/__tests__/branch-fixtures-3.test.js.map +1 -0
- package/dist/__tests__/branch-fixtures.test.d.ts +6 -0
- package/dist/__tests__/branch-fixtures.test.d.ts.map +1 -0
- package/dist/__tests__/branch-fixtures.test.js +1072 -0
- package/dist/__tests__/branch-fixtures.test.js.map +1 -0
- package/dist/__tests__/checks.test.d.ts +2 -0
- package/dist/__tests__/checks.test.d.ts.map +1 -0
- package/dist/__tests__/checks.test.js +39 -0
- package/dist/__tests__/checks.test.js.map +1 -0
- package/dist/__tests__/fixture-coverage.allowlist.d.ts +19 -0
- package/dist/__tests__/fixture-coverage.allowlist.d.ts.map +1 -0
- package/dist/__tests__/fixture-coverage.allowlist.js +27 -0
- package/dist/__tests__/fixture-coverage.allowlist.js.map +1 -0
- package/dist/__tests__/fixture-coverage.test.d.ts +13 -0
- package/dist/__tests__/fixture-coverage.test.d.ts.map +1 -0
- package/dist/__tests__/fixture-coverage.test.js +57 -0
- package/dist/__tests__/fixture-coverage.test.js.map +1 -0
- package/dist/__tests__/no-bootstrap-tool-import.test.d.ts +2 -0
- package/dist/__tests__/no-bootstrap-tool-import.test.d.ts.map +1 -0
- package/dist/__tests__/no-bootstrap-tool-import.test.js +75 -0
- package/dist/__tests__/no-bootstrap-tool-import.test.js.map +1 -0
- package/dist/__tests__/phantom-dependency-detection.test.d.ts +12 -0
- package/dist/__tests__/phantom-dependency-detection.test.d.ts.map +1 -0
- package/dist/__tests__/phantom-dependency-detection.test.js +112 -0
- package/dist/__tests__/phantom-dependency-detection.test.js.map +1 -0
- package/dist/__tests__/typescript-frontend.test.d.ts +8 -0
- package/dist/__tests__/typescript-frontend.test.d.ts.map +1 -0
- package/dist/__tests__/typescript-frontend.test.js +57 -0
- package/dist/__tests__/typescript-frontend.test.js.map +1 -0
- package/dist/checks/architecture/circular-import-detection.d.ts +14 -0
- package/dist/checks/architecture/circular-import-detection.d.ts.map +1 -0
- package/dist/checks/architecture/circular-import-detection.js +55 -0
- package/dist/checks/architecture/circular-import-detection.js.map +1 -0
- package/dist/checks/architecture/contracts-schema-consistency.d.ts +11 -0
- package/dist/checks/architecture/contracts-schema-consistency.d.ts.map +1 -0
- package/dist/checks/architecture/contracts-schema-consistency.js +75 -0
- package/dist/checks/architecture/contracts-schema-consistency.js.map +1 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.d.ts +12 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.d.ts.map +1 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.js +92 -0
- package/dist/checks/architecture/drizzle-orm-migration-guardrails.js.map +1 -0
- package/dist/checks/architecture/index.d.ts +10 -0
- package/dist/checks/architecture/index.d.ts.map +1 -0
- package/dist/checks/architecture/index.js +10 -0
- package/dist/checks/architecture/index.js.map +1 -0
- package/dist/checks/architecture/missing-type-exports.d.ts +13 -0
- package/dist/checks/architecture/missing-type-exports.d.ts.map +1 -0
- package/dist/checks/architecture/missing-type-exports.js +245 -0
- package/dist/checks/architecture/missing-type-exports.js.map +1 -0
- package/dist/checks/architecture/module-coupling-fan-out.d.ts +20 -0
- package/dist/checks/architecture/module-coupling-fan-out.d.ts.map +1 -0
- package/dist/checks/architecture/module-coupling-fan-out.js +120 -0
- package/dist/checks/architecture/module-coupling-fan-out.js.map +1 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.d.ts +38 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.d.ts.map +1 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.js +95 -0
- package/dist/checks/architecture/no-bootstrap-tool-import.js.map +1 -0
- package/dist/checks/architecture/package-json-exports-field.d.ts +10 -0
- package/dist/checks/architecture/package-json-exports-field.d.ts.map +1 -0
- package/dist/checks/architecture/package-json-exports-field.js +56 -0
- package/dist/checks/architecture/package-json-exports-field.js.map +1 -0
- package/dist/checks/architecture/phantom-dependency-detection.d.ts +22 -0
- package/dist/checks/architecture/phantom-dependency-detection.d.ts.map +1 -0
- package/dist/checks/architecture/phantom-dependency-detection.js +330 -0
- package/dist/checks/architecture/phantom-dependency-detection.js.map +1 -0
- package/dist/checks/architecture/tsconfig-extends-validation.d.ts +10 -0
- package/dist/checks/architecture/tsconfig-extends-validation.d.ts.map +1 -0
- package/dist/checks/architecture/tsconfig-extends-validation.js +78 -0
- package/dist/checks/architecture/tsconfig-extends-validation.js.map +1 -0
- package/dist/checks/index.d.ts +6 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +6 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/checks/quality/api/api-contract-validation.d.ts +15 -0
- package/dist/checks/quality/api/api-contract-validation.d.ts.map +1 -0
- package/dist/checks/quality/api/api-contract-validation.js +316 -0
- package/dist/checks/quality/api/api-contract-validation.js.map +1 -0
- package/dist/checks/quality/api/api-response-validation.d.ts +14 -0
- package/dist/checks/quality/api/api-response-validation.d.ts.map +1 -0
- package/dist/checks/quality/api/api-response-validation.js +209 -0
- package/dist/checks/quality/api/api-response-validation.js.map +1 -0
- package/dist/checks/quality/api/fastify-route-validation.d.ts +14 -0
- package/dist/checks/quality/api/fastify-route-validation.d.ts.map +1 -0
- package/dist/checks/quality/api/fastify-route-validation.js +298 -0
- package/dist/checks/quality/api/fastify-route-validation.js.map +1 -0
- package/dist/checks/quality/api/fastify-schema-coverage.d.ts +11 -0
- package/dist/checks/quality/api/fastify-schema-coverage.d.ts.map +1 -0
- package/dist/checks/quality/api/fastify-schema-coverage.js +261 -0
- package/dist/checks/quality/api/fastify-schema-coverage.js.map +1 -0
- package/dist/checks/quality/api/index.d.ts +5 -0
- package/dist/checks/quality/api/index.d.ts.map +1 -0
- package/dist/checks/quality/api/index.js +5 -0
- package/dist/checks/quality/api/index.js.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts +32 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.js +451 -0
- package/dist/checks/quality/code-structure/duplicate-utility-functions.js.map +1 -0
- package/dist/checks/quality/code-structure/index.d.ts +3 -0
- package/dist/checks/quality/code-structure/index.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/index.js +3 -0
- package/dist/checks/quality/code-structure/index.js.map +1 -0
- package/dist/checks/quality/code-structure/no-any-types.d.ts +13 -0
- package/dist/checks/quality/code-structure/no-any-types.d.ts.map +1 -0
- package/dist/checks/quality/code-structure/no-any-types.js +116 -0
- package/dist/checks/quality/code-structure/no-any-types.js.map +1 -0
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.d.ts +15 -0
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js +51 -0
- package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js.map +1 -0
- package/dist/checks/quality/data-integrity/array-validation.d.ts +16 -0
- package/dist/checks/quality/data-integrity/array-validation.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/array-validation.js +508 -0
- package/dist/checks/quality/data-integrity/array-validation.js.map +1 -0
- package/dist/checks/quality/data-integrity/database-index-coverage.d.ts +14 -0
- package/dist/checks/quality/data-integrity/database-index-coverage.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/database-index-coverage.js +235 -0
- package/dist/checks/quality/data-integrity/database-index-coverage.js.map +1 -0
- package/dist/checks/quality/data-integrity/database-schema-validation.d.ts +16 -0
- package/dist/checks/quality/data-integrity/database-schema-validation.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/database-schema-validation.js +328 -0
- package/dist/checks/quality/data-integrity/database-schema-validation.js.map +1 -0
- package/dist/checks/quality/data-integrity/in-memory-repository-detection.d.ts +14 -0
- package/dist/checks/quality/data-integrity/in-memory-repository-detection.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/in-memory-repository-detection.js +157 -0
- package/dist/checks/quality/data-integrity/in-memory-repository-detection.js.map +1 -0
- package/dist/checks/quality/data-integrity/index.d.ts +8 -0
- package/dist/checks/quality/data-integrity/index.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/index.js +8 -0
- package/dist/checks/quality/data-integrity/index.js.map +1 -0
- package/dist/checks/quality/data-integrity/missing-input-validation.d.ts +12 -0
- package/dist/checks/quality/data-integrity/missing-input-validation.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/missing-input-validation.js +180 -0
- package/dist/checks/quality/data-integrity/missing-input-validation.js.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety.d.ts +33 -0
- package/dist/checks/quality/data-integrity/null-safety.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/null-safety.js +766 -0
- package/dist/checks/quality/data-integrity/null-safety.js.map +1 -0
- package/dist/checks/quality/data-integrity/numeric-validation.d.ts +12 -0
- package/dist/checks/quality/data-integrity/numeric-validation.d.ts.map +1 -0
- package/dist/checks/quality/data-integrity/numeric-validation.js +409 -0
- package/dist/checks/quality/data-integrity/numeric-validation.js.map +1 -0
- package/dist/checks/quality/frontend/a11y-form-labels.d.ts +14 -0
- package/dist/checks/quality/frontend/a11y-form-labels.d.ts.map +1 -0
- package/dist/checks/quality/frontend/a11y-form-labels.js +93 -0
- package/dist/checks/quality/frontend/a11y-form-labels.js.map +1 -0
- package/dist/checks/quality/frontend/a11y-semantic-html.d.ts +14 -0
- package/dist/checks/quality/frontend/a11y-semantic-html.d.ts.map +1 -0
- package/dist/checks/quality/frontend/a11y-semantic-html.js +88 -0
- package/dist/checks/quality/frontend/a11y-semantic-html.js.map +1 -0
- package/dist/checks/quality/frontend/index.d.ts +4 -0
- package/dist/checks/quality/frontend/index.d.ts.map +1 -0
- package/dist/checks/quality/frontend/index.js +4 -0
- package/dist/checks/quality/frontend/index.js.map +1 -0
- package/dist/checks/quality/frontend/test-only-frontend-modules.d.ts +13 -0
- package/dist/checks/quality/frontend/test-only-frontend-modules.d.ts.map +1 -0
- package/dist/checks/quality/frontend/test-only-frontend-modules.js +159 -0
- package/dist/checks/quality/frontend/test-only-frontend-modules.js.map +1 -0
- package/dist/checks/quality/incomplete-regex-escaping.d.ts +13 -0
- package/dist/checks/quality/incomplete-regex-escaping.d.ts.map +1 -0
- package/dist/checks/quality/incomplete-regex-escaping.js +207 -0
- package/dist/checks/quality/incomplete-regex-escaping.js.map +1 -0
- package/dist/checks/quality/index.d.ts +11 -0
- package/dist/checks/quality/index.d.ts.map +1 -0
- package/dist/checks/quality/index.js +11 -0
- package/dist/checks/quality/index.js.map +1 -0
- package/dist/checks/quality/linting/index.d.ts +2 -0
- package/dist/checks/quality/linting/index.d.ts.map +1 -0
- package/dist/checks/quality/linting/index.js +2 -0
- package/dist/checks/quality/linting/index.js.map +1 -0
- package/dist/checks/quality/linting/typescript-frontend.d.ts +25 -0
- package/dist/checks/quality/linting/typescript-frontend.d.ts.map +1 -0
- package/dist/checks/quality/linting/typescript-frontend.js +159 -0
- package/dist/checks/quality/linting/typescript-frontend.js.map +1 -0
- package/dist/checks/quality/observability/index.d.ts +5 -0
- package/dist/checks/quality/observability/index.d.ts.map +1 -0
- package/dist/checks/quality/observability/index.js +5 -0
- package/dist/checks/quality/observability/index.js.map +1 -0
- package/dist/checks/quality/observability/logger-event-name-format.d.ts +12 -0
- package/dist/checks/quality/observability/logger-event-name-format.d.ts.map +1 -0
- package/dist/checks/quality/observability/logger-event-name-format.js +124 -0
- package/dist/checks/quality/observability/logger-event-name-format.js.map +1 -0
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.d.ts +5 -0
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.d.ts.map +1 -0
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.js +77 -0
- package/dist/checks/quality/observability/no-hardcoded-correlation-id.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.d.ts +11 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.js +107 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/analyzer.test.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.d.ts +12 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.js +94 -0
- package/dist/checks/quality/observability/observability-coverage/__tests__/logger-detector.test.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/analyzer.d.ts +13 -0
- package/dist/checks/quality/observability/observability-coverage/analyzer.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/analyzer.js +117 -0
- package/dist/checks/quality/observability/observability-coverage/analyzer.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/index.d.ts +4 -0
- package/dist/checks/quality/observability/observability-coverage/index.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/index.js +4 -0
- package/dist/checks/quality/observability/observability-coverage/index.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/logger-detector.d.ts +29 -0
- package/dist/checks/quality/observability/observability-coverage/logger-detector.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/logger-detector.js +111 -0
- package/dist/checks/quality/observability/observability-coverage/logger-detector.js.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/types.d.ts +64 -0
- package/dist/checks/quality/observability/observability-coverage/types.d.ts.map +1 -0
- package/dist/checks/quality/observability/observability-coverage/types.js +6 -0
- package/dist/checks/quality/observability/observability-coverage/types.js.map +1 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.d.ts +22 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.d.ts.map +1 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.js +212 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.js.map +1 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.test.d.ts +11 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.test.d.ts.map +1 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.test.js +46 -0
- package/dist/checks/quality/observability/pii-exposure-in-logs.test.js.map +1 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.d.ts +14 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.d.ts.map +1 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js +61 -0
- package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js.map +1 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.d.ts +26 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.d.ts.map +1 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.js +410 -0
- package/dist/checks/quality/patterns/async-waterfall-detection.js.map +1 -0
- package/dist/checks/quality/patterns/dispose-pattern-completeness.d.ts +13 -0
- package/dist/checks/quality/patterns/dispose-pattern-completeness.d.ts.map +1 -0
- package/dist/checks/quality/patterns/dispose-pattern-completeness.js +220 -0
- package/dist/checks/quality/patterns/dispose-pattern-completeness.js.map +1 -0
- package/dist/checks/quality/patterns/error-handling-quality.d.ts +17 -0
- package/dist/checks/quality/patterns/error-handling-quality.d.ts.map +1 -0
- package/dist/checks/quality/patterns/error-handling-quality.js +335 -0
- package/dist/checks/quality/patterns/error-handling-quality.js.map +1 -0
- package/dist/checks/quality/patterns/index.d.ts +10 -0
- package/dist/checks/quality/patterns/index.d.ts.map +1 -0
- package/dist/checks/quality/patterns/index.js +10 -0
- package/dist/checks/quality/patterns/index.js.map +1 -0
- package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.d.ts +16 -0
- package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.d.ts.map +1 -0
- package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.js +205 -0
- package/dist/checks/quality/patterns/lifecycle-cleanup-enforcement.js.map +1 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.d.ts +16 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.d.ts.map +1 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.js +328 -0
- package/dist/checks/quality/patterns/result-pattern-consistency.js.map +1 -0
- package/dist/checks/quality/patterns/silent-early-returns.d.ts +23 -0
- package/dist/checks/quality/patterns/silent-early-returns.d.ts.map +1 -0
- package/dist/checks/quality/patterns/silent-early-returns.js +266 -0
- package/dist/checks/quality/patterns/silent-early-returns.js.map +1 -0
- package/dist/checks/quality/patterns/stream-buffer-size-limits.d.ts +13 -0
- package/dist/checks/quality/patterns/stream-buffer-size-limits.d.ts.map +1 -0
- package/dist/checks/quality/patterns/stream-buffer-size-limits.js +163 -0
- package/dist/checks/quality/patterns/stream-buffer-size-limits.js.map +1 -0
- package/dist/checks/quality/patterns/throws-documentation.d.ts +23 -0
- package/dist/checks/quality/patterns/throws-documentation.d.ts.map +1 -0
- package/dist/checks/quality/patterns/throws-documentation.js +519 -0
- package/dist/checks/quality/patterns/throws-documentation.js.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition.d.ts +48 -0
- package/dist/checks/quality/patterns/toctou-race-condition.d.ts.map +1 -0
- package/dist/checks/quality/patterns/toctou-race-condition.js +639 -0
- package/dist/checks/quality/patterns/toctou-race-condition.js.map +1 -0
- package/dist/checks/quality/stubbed-implementation-detection.d.ts +24 -0
- package/dist/checks/quality/stubbed-implementation-detection.d.ts.map +1 -0
- package/dist/checks/quality/stubbed-implementation-detection.js +355 -0
- package/dist/checks/quality/stubbed-implementation-detection.js.map +1 -0
- package/dist/checks/quality/unused-config-options.d.ts +12 -0
- package/dist/checks/quality/unused-config-options.d.ts.map +1 -0
- package/dist/checks/quality/unused-config-options.js +245 -0
- package/dist/checks/quality/unused-config-options.js.map +1 -0
- package/dist/checks/resilience/__tests__/callback-invocation-safe.test.d.ts +2 -0
- package/dist/checks/resilience/__tests__/callback-invocation-safe.test.d.ts.map +1 -0
- package/dist/checks/resilience/__tests__/callback-invocation-safe.test.js +79 -0
- package/dist/checks/resilience/__tests__/callback-invocation-safe.test.js.map +1 -0
- package/dist/checks/resilience/__tests__/context-leakage-fp.test.d.ts +12 -0
- package/dist/checks/resilience/__tests__/context-leakage-fp.test.d.ts.map +1 -0
- package/dist/checks/resilience/__tests__/context-leakage-fp.test.js +34 -0
- package/dist/checks/resilience/__tests__/context-leakage-fp.test.js.map +1 -0
- package/dist/checks/resilience/__tests__/context-mutation.test.d.ts +11 -0
- package/dist/checks/resilience/__tests__/context-mutation.test.d.ts.map +1 -0
- package/dist/checks/resilience/__tests__/context-mutation.test.js +54 -0
- package/dist/checks/resilience/__tests__/context-mutation.test.js.map +1 -0
- package/dist/checks/resilience/callback-invocation-safe.d.ts +34 -0
- package/dist/checks/resilience/callback-invocation-safe.d.ts.map +1 -0
- package/dist/checks/resilience/callback-invocation-safe.js +247 -0
- package/dist/checks/resilience/callback-invocation-safe.js.map +1 -0
- package/dist/checks/resilience/context-leakage.d.ts +25 -0
- package/dist/checks/resilience/context-leakage.d.ts.map +1 -0
- package/dist/checks/resilience/context-leakage.js +435 -0
- package/dist/checks/resilience/context-leakage.js.map +1 -0
- package/dist/checks/resilience/context-mutation.d.ts +21 -0
- package/dist/checks/resilience/context-mutation.d.ts.map +1 -0
- package/dist/checks/resilience/context-mutation.js +368 -0
- package/dist/checks/resilience/context-mutation.js.map +1 -0
- package/dist/checks/resilience/detached-promises.d.ts +40 -0
- package/dist/checks/resilience/detached-promises.d.ts.map +1 -0
- package/dist/checks/resilience/detached-promises.js +646 -0
- package/dist/checks/resilience/detached-promises.js.map +1 -0
- package/dist/checks/resilience/index.d.ts +7 -0
- package/dist/checks/resilience/index.d.ts.map +1 -0
- package/dist/checks/resilience/index.js +7 -0
- package/dist/checks/resilience/index.js.map +1 -0
- package/dist/checks/resilience/no-raw-fetch.d.ts +11 -0
- package/dist/checks/resilience/no-raw-fetch.d.ts.map +1 -0
- package/dist/checks/resilience/no-raw-fetch.js +110 -0
- package/dist/checks/resilience/no-raw-fetch.js.map +1 -0
- package/dist/checks/resilience/no-unbounded-concurrency.d.ts +11 -0
- package/dist/checks/resilience/no-unbounded-concurrency.d.ts.map +1 -0
- package/dist/checks/resilience/no-unbounded-concurrency.js +117 -0
- package/dist/checks/resilience/no-unbounded-concurrency.js.map +1 -0
- package/dist/checks/security/__tests__/sql-injection.test.d.ts +17 -0
- package/dist/checks/security/__tests__/sql-injection.test.d.ts.map +1 -0
- package/dist/checks/security/__tests__/sql-injection.test.js +97 -0
- package/dist/checks/security/__tests__/sql-injection.test.js.map +1 -0
- package/dist/checks/security/index.d.ts +4 -0
- package/dist/checks/security/index.d.ts.map +1 -0
- package/dist/checks/security/index.js +4 -0
- package/dist/checks/security/index.js.map +1 -0
- package/dist/checks/security/input-sanitization.d.ts +20 -0
- package/dist/checks/security/input-sanitization.d.ts.map +1 -0
- package/dist/checks/security/input-sanitization.js +255 -0
- package/dist/checks/security/input-sanitization.js.map +1 -0
- package/dist/checks/security/sql-injection.d.ts +24 -0
- package/dist/checks/security/sql-injection.d.ts.map +1 -0
- package/dist/checks/security/sql-injection.js +330 -0
- package/dist/checks/security/sql-injection.js.map +1 -0
- package/dist/checks/security/unsafe-secret-comparison.d.ts +17 -0
- package/dist/checks/security/unsafe-secret-comparison.d.ts.map +1 -0
- package/dist/checks/security/unsafe-secret-comparison.js +227 -0
- package/dist/checks/security/unsafe-secret-comparison.js.map +1 -0
- package/dist/checks/testing/index.d.ts +2 -0
- package/dist/checks/testing/index.d.ts.map +1 -0
- package/dist/checks/testing/index.js +2 -0
- package/dist/checks/testing/index.js.map +1 -0
- package/dist/checks/testing/mock-implementations-in-production.d.ts +12 -0
- package/dist/checks/testing/mock-implementations-in-production.d.ts.map +1 -0
- package/dist/checks/testing/mock-implementations-in-production.js +211 -0
- package/dist/checks/testing/mock-implementations-in-production.js.map +1 -0
- package/dist/display/architecture.d.ts +9 -0
- package/dist/display/architecture.d.ts.map +1 -0
- package/dist/display/architecture.js +18 -0
- package/dist/display/architecture.js.map +1 -0
- package/dist/display/index.d.ts +20 -0
- package/dist/display/index.d.ts.map +1 -0
- package/dist/display/index.js +30 -0
- package/dist/display/index.js.map +1 -0
- package/dist/display/quality.d.ts +7 -0
- package/dist/display/quality.d.ts.map +1 -0
- package/dist/display/quality.js +39 -0
- package/dist/display/quality.js.map +1 -0
- package/dist/display/resilience.d.ts +7 -0
- package/dist/display/resilience.d.ts.map +1 -0
- package/dist/display/resilience.js +13 -0
- package/dist/display/resilience.js.map +1 -0
- package/dist/display/security-testing.d.ts +9 -0
- package/dist/display/security-testing.d.ts.map +1 -0
- package/dist/display/security-testing.js +14 -0
- package/dist/display/security-testing.js.map +1 -0
- package/dist/display/types.d.ts +6 -0
- package/dist/display/types.d.ts.map +1 -0
- package/dist/display/types.js +6 -0
- package/dist/display/types.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,1072 @@
|
|
|
1
|
+
// @fitness-ignore-file file-length-limit -- behavior fixture suite; related scenarios stay together while checks are split into focused tests.
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Branch-behavior fixture suite: targets high-impact uncovered branches
|
|
4
|
+
* across many checks via rich, realistic source fixtures.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { LanguageRegistry, RunScope, runWithScope } from '@opensip-cli/core';
|
|
10
|
+
import { fileCache } from '@opensip-cli/fitness';
|
|
11
|
+
import { typescriptAdapter } from '@opensip-cli/lang-typescript';
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
13
|
+
import { checks } from '../index.js';
|
|
14
|
+
// Production simulation: register the TS adapter (see behavior-fixtures.test.ts).
|
|
15
|
+
const langRegistry = new LanguageRegistry();
|
|
16
|
+
langRegistry.register(typescriptAdapter);
|
|
17
|
+
const testScope = new RunScope({ languages: langRegistry });
|
|
18
|
+
let cwd;
|
|
19
|
+
let written = [];
|
|
20
|
+
function fx(rel, content) {
|
|
21
|
+
const abs = join(cwd, rel);
|
|
22
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
23
|
+
writeFileSync(abs, content);
|
|
24
|
+
written.push(abs);
|
|
25
|
+
return abs;
|
|
26
|
+
}
|
|
27
|
+
function findCheck(slug) {
|
|
28
|
+
const c = checks.find((x) => x.config.slug === slug);
|
|
29
|
+
if (!c)
|
|
30
|
+
throw new Error(`check not found: ${slug}`);
|
|
31
|
+
return c;
|
|
32
|
+
}
|
|
33
|
+
async function runCheck(slug) {
|
|
34
|
+
const check = findCheck(slug);
|
|
35
|
+
await fileCache.prewarm(cwd, ['**/*']);
|
|
36
|
+
return runWithScope(testScope, () => check.run(cwd, { targetFiles: written }));
|
|
37
|
+
}
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
cwd = mkdtempSync(join(tmpdir(), 'opensip-cov-branch-'));
|
|
40
|
+
written = [];
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
fileCache.clear();
|
|
44
|
+
rmSync(cwd, { recursive: true, force: true });
|
|
45
|
+
});
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// detached-promises (resilience/async-patterns) — drive sync-call branches
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
describe('detached-promises — many sync-call branches', () => {
|
|
50
|
+
it('exercises super(), this.method(), nested receivers, sync receivers, sync prefixes/suffixes', async () => {
|
|
51
|
+
fx('src/svc/detached.ts', [
|
|
52
|
+
'class Base {',
|
|
53
|
+
' init() { this.helper() }',
|
|
54
|
+
' helper() {}',
|
|
55
|
+
'}',
|
|
56
|
+
'export class Svc extends Base {',
|
|
57
|
+
' async run(input: string) {',
|
|
58
|
+
' super.init()',
|
|
59
|
+
' this.syncMethod()',
|
|
60
|
+
' this.helper()',
|
|
61
|
+
' logger.info("hello")',
|
|
62
|
+
' this.logger.warn("nested")',
|
|
63
|
+
' Pyroscope.default.start()',
|
|
64
|
+
' myLogger.error("pattern receiver")',
|
|
65
|
+
' setupRoute(input)',
|
|
66
|
+
' teardownClient()',
|
|
67
|
+
' isReady()',
|
|
68
|
+
' fs.readFileSync("/tmp/x")',
|
|
69
|
+
' process.nextTick(() => undefined)',
|
|
70
|
+
' void doStuff()',
|
|
71
|
+
' apiCall().then(() => undefined)',
|
|
72
|
+
' apiCall().catch(() => undefined)',
|
|
73
|
+
' apiCall().finally(() => undefined)',
|
|
74
|
+
' unwrap(await apiCall())',
|
|
75
|
+
' (await apiCall()).chain()',
|
|
76
|
+
' apiCall() // <-- detached',
|
|
77
|
+
' }',
|
|
78
|
+
' syncMethod() { return 1 }',
|
|
79
|
+
'}',
|
|
80
|
+
].join('\n'));
|
|
81
|
+
const result = await runCheck('detached-promises');
|
|
82
|
+
expect(result).toBeDefined();
|
|
83
|
+
});
|
|
84
|
+
it('handles non-async function with calls (skip), test files (skip)', async () => {
|
|
85
|
+
fx('src/util/sync.ts', ['export function notAsync() { doStuff() }'].join('\n'));
|
|
86
|
+
fx('src/x.test.ts', ['export async function t() { doStuff() }'].join('\n'));
|
|
87
|
+
const result = await runCheck('detached-promises');
|
|
88
|
+
expect(result).toBeDefined();
|
|
89
|
+
});
|
|
90
|
+
it('handles cli/script file skip patterns', async () => {
|
|
91
|
+
fx('src/cli/cmd.ts', ['export async function run() { doStuff(); other(); }'].join('\n'));
|
|
92
|
+
const result = await runCheck('detached-promises');
|
|
93
|
+
expect(result).toBeDefined();
|
|
94
|
+
});
|
|
95
|
+
it('exercises super-call kind branch', async () => {
|
|
96
|
+
fx('src/svc/super.ts', [
|
|
97
|
+
'class Parent {}',
|
|
98
|
+
'export class Child extends Parent {',
|
|
99
|
+
' constructor() { super() }',
|
|
100
|
+
'}',
|
|
101
|
+
].join('\n'));
|
|
102
|
+
const result = await runCheck('detached-promises');
|
|
103
|
+
expect(result).toBeDefined();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// no-unbounded-concurrency — bounded-pattern branches
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
describe('no-unbounded-concurrency — bounded-pattern branches', () => {
|
|
110
|
+
it('skips when content has p-limit, plimit, allSettled, concurrency:N, batch, throttle, rateLimit', async () => {
|
|
111
|
+
const variants = [
|
|
112
|
+
'import pLimit from "p-limit"\nawait Promise.all(items.map(fn))',
|
|
113
|
+
'const limit = plimit(4)\nawait Promise.all(arr.map(fn))',
|
|
114
|
+
'await Promise.allSettled(items.map(fn))',
|
|
115
|
+
'await Promise.all(items.map(fn)) // concurrency: 4',
|
|
116
|
+
'await Promise.all(items.map(fn)) // batch chunked',
|
|
117
|
+
'await Promise.all(items.map(fn)) // throttle 10/s',
|
|
118
|
+
'await Promise.all(items.map(fn)) // rateLimit 5',
|
|
119
|
+
];
|
|
120
|
+
for (const [i, src] of variants.entries()) {
|
|
121
|
+
fx(`src/api/u${i}.ts`, src);
|
|
122
|
+
}
|
|
123
|
+
fx('src/api/raw.ts', 'await Promise.all(items.map(fn))');
|
|
124
|
+
const result = await runCheck('no-unbounded-concurrency');
|
|
125
|
+
expect(result).toBeDefined();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// no-raw-fetch — many skip paths
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
describe('no-raw-fetch — skip-path branches', () => {
|
|
132
|
+
it('skips resilient-fetch, fitness/checks paths, llm paths, streaming files, test files', async () => {
|
|
133
|
+
fx('src/lib/resilient-fetch.ts', 'await fetch("/x")');
|
|
134
|
+
fx('src/llm/openai.ts', 'await fetch("/x")');
|
|
135
|
+
fx('src/llm-adapter/openai.ts', 'await fetch("/x")');
|
|
136
|
+
fx('src/sse/stream.ts', 'const r = new ReadableStream(); await fetch("/x")');
|
|
137
|
+
fx('src/sse/event.ts', 'const r = new EventSource(""); await fetch("/x")');
|
|
138
|
+
fx('src/sse/r.ts', 'const reader = body.getReader(); await fetch("/x")');
|
|
139
|
+
fx('src/x.test.ts', 'await fetch("/x")');
|
|
140
|
+
fx('src/x.spec.ts', 'await fetch("/x")');
|
|
141
|
+
fx('src/__tests__/y.ts', 'await fetch("/x")');
|
|
142
|
+
fx('src/api/legit.ts', '// uses fetch\nawait fetch("/legit")');
|
|
143
|
+
fx('src/api/comment.ts', '// fetch( comment\nawait fetch("/legit")');
|
|
144
|
+
const result = await runCheck('no-raw-fetch');
|
|
145
|
+
expect(result).toBeDefined();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// toctou-race-condition — exercise local/shared/atomic SQL/cache branches
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
describe('toctou-race-condition — many classification branches', () => {
|
|
152
|
+
it('local Map/Set, this.cache, drizzle update/insert/delete, sql template, shared, unknown receiver', async () => {
|
|
153
|
+
fx('src/svc/toc.ts', [
|
|
154
|
+
'class StringCache {',
|
|
155
|
+
' get(k: string): string | undefined { return undefined }',
|
|
156
|
+
' set(k: string, v: string): void {}',
|
|
157
|
+
'}',
|
|
158
|
+
'export class Service {',
|
|
159
|
+
' private headerCache: Map<string, string> = new Map()',
|
|
160
|
+
' #cache = new Map<string, number>()',
|
|
161
|
+
' cache: StringCache = new StringCache()',
|
|
162
|
+
' async fetchAndUpdate(id: string) {',
|
|
163
|
+
' const local = new Map<string, number>()',
|
|
164
|
+
' local.get(id)',
|
|
165
|
+
' local.set(id, 1)',
|
|
166
|
+
' this.headerCache.get(id)',
|
|
167
|
+
' this.headerCache.set(id, "x")',
|
|
168
|
+
' this.#cache.get(id)',
|
|
169
|
+
' this.#cache.set(id, 1)',
|
|
170
|
+
' this.cache.get(id)',
|
|
171
|
+
' this.cache.set(id, "x")',
|
|
172
|
+
' db.update(tableX)',
|
|
173
|
+
' db.insert(tableX)',
|
|
174
|
+
' db.delete(tableX)',
|
|
175
|
+
' tx.execute(sql`UPDATE x SET y=1`)',
|
|
176
|
+
' chain.foo().get(id)',
|
|
177
|
+
' chain.foo().update(id)',
|
|
178
|
+
' repo.findOne(id)',
|
|
179
|
+
' repo.put(id, 2)', // shared read+update
|
|
180
|
+
' return 1',
|
|
181
|
+
' }',
|
|
182
|
+
' async noPair(id: string) {',
|
|
183
|
+
' repo.find(id)',
|
|
184
|
+
' return null',
|
|
185
|
+
' }',
|
|
186
|
+
'}',
|
|
187
|
+
].join('\n'));
|
|
188
|
+
const result = await runCheck('toctou-race-condition');
|
|
189
|
+
expect(result).toBeDefined();
|
|
190
|
+
});
|
|
191
|
+
it('skips safe paths and atomic-comment files', async () => {
|
|
192
|
+
fx('src/cache/foo.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
193
|
+
fx('src/cli/cmd.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
194
|
+
fx('src/scripts/x.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
195
|
+
fx('src/testing/y.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
196
|
+
fx('src/test-utils/y.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
197
|
+
fx('src/config/y.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
198
|
+
fx('src/registry/y.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
199
|
+
fx('src/factories/y.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
200
|
+
fx('src/routes/y.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
201
|
+
fx('src/di/y.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
202
|
+
fx('src/schema/y.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
203
|
+
fx('src/whatever/x-cache.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
204
|
+
fx('src/whatever/x-prefetcher.ts', 'export function f() { repo.find(); repo.set(1) }');
|
|
205
|
+
fx('src/svc/atomic.ts', '// transaction wrapper\nexport function f() { repo.find(); repo.set(1) }');
|
|
206
|
+
fx('src/svc/version.ts', 'export function f() { repo.find({version: 1}); repo.update({expectedVersion: 1}) }');
|
|
207
|
+
const result = await runCheck('toctou-race-condition');
|
|
208
|
+
expect(result).toBeDefined();
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// async-waterfall-detection — many branches
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
describe('async-waterfall-detection — branches', () => {
|
|
215
|
+
it('handles independent awaits, dependent awaits, await in for/while, mixed', async () => {
|
|
216
|
+
fx('src/svc/waterfall.ts', [
|
|
217
|
+
'export async function independent(): Promise<void> {',
|
|
218
|
+
' const a = await fetchA()',
|
|
219
|
+
' const b = await fetchB()',
|
|
220
|
+
' const c = await fetchC()',
|
|
221
|
+
' console.log(a, b, c)',
|
|
222
|
+
'}',
|
|
223
|
+
'export async function dependent(): Promise<void> {',
|
|
224
|
+
' const a = await fetchA()',
|
|
225
|
+
' const b = await fetchB(a)',
|
|
226
|
+
' console.log(b)',
|
|
227
|
+
'}',
|
|
228
|
+
'export async function inLoop(items: number[]): Promise<void> {',
|
|
229
|
+
' for (const it of items) {',
|
|
230
|
+
' await process(it)',
|
|
231
|
+
' }',
|
|
232
|
+
'}',
|
|
233
|
+
'export async function inWhileLoop(): Promise<void> {',
|
|
234
|
+
' while (true) {',
|
|
235
|
+
' const r = await tick()',
|
|
236
|
+
' if (!r) break',
|
|
237
|
+
' }',
|
|
238
|
+
'}',
|
|
239
|
+
'export async function tryCatch(): Promise<void> {',
|
|
240
|
+
' try {',
|
|
241
|
+
' const a = await fetchA()',
|
|
242
|
+
' const b = await fetchB()',
|
|
243
|
+
' return a + b',
|
|
244
|
+
' } catch (e) { return 0 }',
|
|
245
|
+
'}',
|
|
246
|
+
'export async function withReturnAwait(): Promise<number> {',
|
|
247
|
+
' return await fetchOne()',
|
|
248
|
+
'}',
|
|
249
|
+
'export async function singleAwait(): Promise<number> {',
|
|
250
|
+
' return await fetchOne()',
|
|
251
|
+
'}',
|
|
252
|
+
].join('\n'));
|
|
253
|
+
const result = await runCheck('async-waterfall-detection');
|
|
254
|
+
expect(result).toBeDefined();
|
|
255
|
+
});
|
|
256
|
+
it('handles non-async functions and files without await', async () => {
|
|
257
|
+
fx('src/svc/nowait.ts', 'export function f() { return 1 }');
|
|
258
|
+
fx('src/svc/empty.ts', '// no await here');
|
|
259
|
+
const result = await runCheck('async-waterfall-detection');
|
|
260
|
+
expect(result).toBeDefined();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// memo-list-items — exercise visit branches deeply
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
// no-inline-functions — exercise trivial-callback branches
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// missing-type-exports
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
describe('missing-type-exports — branches', () => {
|
|
273
|
+
it('exercises declared-but-unexported types, exported types, type-only imports, ambient declarations', async () => {
|
|
274
|
+
fx('src/types/lib.ts', [
|
|
275
|
+
'export interface Public { id: string }',
|
|
276
|
+
'interface Internal { id: string }',
|
|
277
|
+
'export type T1 = string',
|
|
278
|
+
'type T2 = number',
|
|
279
|
+
'export class C { x = 1 }',
|
|
280
|
+
'class D { x = 1 }',
|
|
281
|
+
'export enum E { A, B }',
|
|
282
|
+
'enum F { A, B }',
|
|
283
|
+
'declare const G: unknown',
|
|
284
|
+
'export { Internal as RenamedInternal }',
|
|
285
|
+
].join('\n'));
|
|
286
|
+
fx('src/types/uses.ts', [
|
|
287
|
+
'import type { Public, T1 } from "./lib.js"',
|
|
288
|
+
'export const x: Public = { id: "1" }',
|
|
289
|
+
'export const y: T1 = "ok"',
|
|
290
|
+
].join('\n'));
|
|
291
|
+
fx('src/types/index.ts', 'export * from "./lib.js"');
|
|
292
|
+
const result = await runCheck('missing-type-exports');
|
|
293
|
+
expect(result).toBeDefined();
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// throws-documentation
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
describe('throws-documentation — branches', () => {
|
|
300
|
+
it('flags throws without @throws JSDoc, accepts documented throws', async () => {
|
|
301
|
+
fx('src/svc/throws.ts', [
|
|
302
|
+
'/** @throws {RangeError} when bad */',
|
|
303
|
+
'export function documented() { throw new RangeError("bad") }',
|
|
304
|
+
'export function undocumented() { throw new Error("oops") }',
|
|
305
|
+
'export function nested() {',
|
|
306
|
+
' if (true) {',
|
|
307
|
+
' if (Math.random() > 0.5) throw new Error("inner")',
|
|
308
|
+
' }',
|
|
309
|
+
'}',
|
|
310
|
+
'export function throwsNonError() { throw "string" }',
|
|
311
|
+
'export async function asyncThrows(): Promise<void> { throw new Error("a") }',
|
|
312
|
+
'export class Klass {',
|
|
313
|
+
' /** @throws Error */',
|
|
314
|
+
' m() { throw new Error("m") }',
|
|
315
|
+
' n() { throw new Error("n") }',
|
|
316
|
+
'}',
|
|
317
|
+
'export const arr = () => { throw new Error("a") }',
|
|
318
|
+
'export const exp = function() { throw new Error("e") }',
|
|
319
|
+
'export function caught() { try { throw new Error() } catch (_e) { return 1 } }',
|
|
320
|
+
'export function caughtRethrows() { try { return 1 } catch (e) { throw e } }',
|
|
321
|
+
].join('\n'));
|
|
322
|
+
const result = await runCheck('throws-documentation');
|
|
323
|
+
expect(result).toBeDefined();
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
// array-validation
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
describe('array-validation — branches', () => {
|
|
330
|
+
it('flags array.length, [0], unsafe destructuring, allows guards', async () => {
|
|
331
|
+
fx('src/api/handler.ts', [
|
|
332
|
+
'export function handle(items: number[]) {',
|
|
333
|
+
' const a = items[0]',
|
|
334
|
+
' const b = items[items.length - 1]',
|
|
335
|
+
' const [first] = items',
|
|
336
|
+
' if (items.length === 0) return null',
|
|
337
|
+
' if (items.length > 0) return items[0]',
|
|
338
|
+
' if (Array.isArray(items)) return items[0]',
|
|
339
|
+
' return a + b + first',
|
|
340
|
+
'}',
|
|
341
|
+
'export function safeUse(items?: number[]) {',
|
|
342
|
+
' return items?.[0]',
|
|
343
|
+
'}',
|
|
344
|
+
'export function nestedArr(rows: { items: number[] }[]) {',
|
|
345
|
+
' return rows[0].items[0]',
|
|
346
|
+
'}',
|
|
347
|
+
].join('\n'));
|
|
348
|
+
const result = await runCheck('array-validation');
|
|
349
|
+
expect(result).toBeDefined();
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
// numeric-validation
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
describe('numeric-validation — branches', () => {
|
|
356
|
+
it('flags missing isFinite/isNaN, allows guards', async () => {
|
|
357
|
+
fx('src/svc/num.ts', [
|
|
358
|
+
'export function calc(price: number, qty: number) {',
|
|
359
|
+
' return price * qty',
|
|
360
|
+
'}',
|
|
361
|
+
'export function safeCalc(price: number, qty: number) {',
|
|
362
|
+
' if (!Number.isFinite(price) || !Number.isFinite(qty)) return 0',
|
|
363
|
+
' if (Number.isNaN(price)) return 0',
|
|
364
|
+
' return price * qty',
|
|
365
|
+
'}',
|
|
366
|
+
'export function divide(a: number, b: number) {',
|
|
367
|
+
' return a / b',
|
|
368
|
+
'}',
|
|
369
|
+
'export function divideSafe(a: number, b: number) {',
|
|
370
|
+
' if (b === 0) throw new RangeError("div by zero")',
|
|
371
|
+
' return a / b',
|
|
372
|
+
'}',
|
|
373
|
+
'export function parseInt2(s: string) {',
|
|
374
|
+
' return parseInt(s, 10)',
|
|
375
|
+
'}',
|
|
376
|
+
'export function parseFloat2(s: string) {',
|
|
377
|
+
' return parseFloat(s)',
|
|
378
|
+
'}',
|
|
379
|
+
'export function modulo(a: number, b: number) { return a % b }',
|
|
380
|
+
].join('\n'));
|
|
381
|
+
const result = await runCheck('numeric-validation');
|
|
382
|
+
expect(result).toBeDefined();
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
// ---------------------------------------------------------------------------
|
|
386
|
+
// null-safety
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
describe('null-safety — branches', () => {
|
|
389
|
+
it('flags unchecked null, allows ?., ??, ===null guards', async () => {
|
|
390
|
+
fx('src/svc/ns.ts', [
|
|
391
|
+
'export function f1(x: { name: string } | null) {',
|
|
392
|
+
' return x.name', // unsafe
|
|
393
|
+
'}',
|
|
394
|
+
'export function f2(x: { name: string } | null) {',
|
|
395
|
+
' return x?.name', // safe
|
|
396
|
+
'}',
|
|
397
|
+
'export function f3(x: { name: string } | null) {',
|
|
398
|
+
' if (x === null) return ""',
|
|
399
|
+
' return x.name', // safe
|
|
400
|
+
'}',
|
|
401
|
+
'export function f4(x: { name: string } | undefined) {',
|
|
402
|
+
' return x?.name ?? "anon"', // safe
|
|
403
|
+
'}',
|
|
404
|
+
'export function f5(x: any) {',
|
|
405
|
+
' return x.name', // any-typed, may or may not flag
|
|
406
|
+
'}',
|
|
407
|
+
'export function f6(x?: { items?: string[] }) {',
|
|
408
|
+
' return x?.items?.length ?? 0',
|
|
409
|
+
'}',
|
|
410
|
+
'export class C {',
|
|
411
|
+
' private name?: string',
|
|
412
|
+
' greet() { return this.name?.toUpperCase() }',
|
|
413
|
+
' unsafeGreet() { return this.name!.toUpperCase() }',
|
|
414
|
+
'}',
|
|
415
|
+
].join('\n'));
|
|
416
|
+
const result = await runCheck('null-safety');
|
|
417
|
+
expect(result).toBeDefined();
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
// stubbed-implementation-detection
|
|
422
|
+
// ---------------------------------------------------------------------------
|
|
423
|
+
describe('stubbed-implementation-detection — branches', () => {
|
|
424
|
+
it('flags TODO bodies, throw not implemented, return null/[]/empty', async () => {
|
|
425
|
+
fx('src/svc/stub.ts', [
|
|
426
|
+
'export function todo() { /* TODO: implement me */ return null }',
|
|
427
|
+
'export function notImpl() { throw new Error("not implemented") }',
|
|
428
|
+
'export function notImpl2() { throw new Error("Not yet implemented") }',
|
|
429
|
+
'export function emptyArr() { return [] }',
|
|
430
|
+
'export function emptyObj() { return {} }',
|
|
431
|
+
'export function returnNull(): null { return null }',
|
|
432
|
+
'export async function asyncStub() { return null }',
|
|
433
|
+
'export function realImpl(a: number, b: number) { return a + b }',
|
|
434
|
+
'export class S {',
|
|
435
|
+
' // TODO finish me',
|
|
436
|
+
' m() { return null }',
|
|
437
|
+
' n() { throw new Error("TODO") }',
|
|
438
|
+
'}',
|
|
439
|
+
].join('\n'));
|
|
440
|
+
const result = await runCheck('stubbed-implementation-detection');
|
|
441
|
+
expect(result).toBeDefined();
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
// ---------------------------------------------------------------------------
|
|
445
|
+
// incomplete-regex-escaping
|
|
446
|
+
// ---------------------------------------------------------------------------
|
|
447
|
+
describe('incomplete-regex-escaping — branches', () => {
|
|
448
|
+
it('flags unescaped specials in user-input regex, allows escaped ones', async () => {
|
|
449
|
+
fx('src/util/re.ts', [
|
|
450
|
+
'export function bad(input: string) {',
|
|
451
|
+
' return new RegExp(input)',
|
|
452
|
+
'}',
|
|
453
|
+
'export function good(input: string) {',
|
|
454
|
+
' const escaped = input.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")',
|
|
455
|
+
' return new RegExp(escaped)',
|
|
456
|
+
'}',
|
|
457
|
+
String.raw `export function literal() { return /foo\.bar/ }`,
|
|
458
|
+
'export function fromTemplate(input: string) {',
|
|
459
|
+
' return new RegExp(`prefix-${input}-suffix`)',
|
|
460
|
+
'}',
|
|
461
|
+
'export const dyn = (i: string) => new RegExp("[" + i + "]")',
|
|
462
|
+
'export function strReplace(s: string, p: string) {',
|
|
463
|
+
' return s.replace(new RegExp(p, "g"), "")',
|
|
464
|
+
'}',
|
|
465
|
+
].join('\n'));
|
|
466
|
+
const result = await runCheck('incomplete-regex-escaping');
|
|
467
|
+
expect(result).toBeDefined();
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
// ---------------------------------------------------------------------------
|
|
471
|
+
// silent-early-returns
|
|
472
|
+
// ---------------------------------------------------------------------------
|
|
473
|
+
describe('silent-early-returns — branches', () => {
|
|
474
|
+
it('flags early returns without logging in business logic', async () => {
|
|
475
|
+
fx('src/svc/silent.ts', [
|
|
476
|
+
'export function f1(x: { id: string } | null) {',
|
|
477
|
+
' if (!x) return null',
|
|
478
|
+
' return x.id',
|
|
479
|
+
'}',
|
|
480
|
+
'export function f2(x: { id: string } | null) {',
|
|
481
|
+
' if (!x) {',
|
|
482
|
+
' logger.warn("missing x")',
|
|
483
|
+
' return null',
|
|
484
|
+
' }',
|
|
485
|
+
' return x.id',
|
|
486
|
+
'}',
|
|
487
|
+
'export function f3(items: number[]) {',
|
|
488
|
+
' if (items.length === 0) return',
|
|
489
|
+
' return items.reduce((a,b) => a+b)',
|
|
490
|
+
'}',
|
|
491
|
+
'export function f4(input?: string) {',
|
|
492
|
+
' return input ?? null',
|
|
493
|
+
'}',
|
|
494
|
+
'export async function f5(x?: string) {',
|
|
495
|
+
' if (!x) throw new Error("required")',
|
|
496
|
+
' return await fetchData(x)',
|
|
497
|
+
'}',
|
|
498
|
+
].join('\n'));
|
|
499
|
+
const result = await runCheck('silent-early-returns');
|
|
500
|
+
expect(result).toBeDefined();
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
// ---------------------------------------------------------------------------
|
|
504
|
+
// missing-input-validation
|
|
505
|
+
// ---------------------------------------------------------------------------
|
|
506
|
+
describe('missing-input-validation — branches', () => {
|
|
507
|
+
it('handlers with and without zod parse', async () => {
|
|
508
|
+
fx('src/api/h.ts', [
|
|
509
|
+
'import { z } from "zod"',
|
|
510
|
+
'const Body = z.object({ id: z.string() })',
|
|
511
|
+
'export async function safeHandler(req: any) {',
|
|
512
|
+
' const body = Body.parse(req.body)',
|
|
513
|
+
' return body',
|
|
514
|
+
'}',
|
|
515
|
+
'export async function unsafeHandler(req: any) {',
|
|
516
|
+
' return req.body.id',
|
|
517
|
+
'}',
|
|
518
|
+
'export async function partialHandler(req: any) {',
|
|
519
|
+
' const x = z.string().safeParse(req.params.id)',
|
|
520
|
+
' return x',
|
|
521
|
+
'}',
|
|
522
|
+
].join('\n'));
|
|
523
|
+
const result = await runCheck('missing-input-validation');
|
|
524
|
+
expect(result).toBeDefined();
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
// ---------------------------------------------------------------------------
|
|
528
|
+
// api-response-validation
|
|
529
|
+
// ---------------------------------------------------------------------------
|
|
530
|
+
describe('api-response-validation — branches', () => {
|
|
531
|
+
it('routes that return raw data vs validated', async () => {
|
|
532
|
+
fx('src/api/route.ts', [
|
|
533
|
+
'import { z } from "zod"',
|
|
534
|
+
'const RespSchema = z.object({ ok: z.boolean() })',
|
|
535
|
+
'export async function ok(): Promise<unknown> { return RespSchema.parse({ ok: true }) }',
|
|
536
|
+
'export async function leak(): Promise<unknown> { return rawDb.query() }',
|
|
537
|
+
'export async function unionRet(x: number): Promise<unknown> {',
|
|
538
|
+
' if (x > 0) return { ok: true }',
|
|
539
|
+
' return { ok: false }',
|
|
540
|
+
'}',
|
|
541
|
+
].join('\n'));
|
|
542
|
+
const result = await runCheck('api-response-validation');
|
|
543
|
+
expect(result).toBeDefined();
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
// ---------------------------------------------------------------------------
|
|
547
|
+
// fastify-route-validation
|
|
548
|
+
// ---------------------------------------------------------------------------
|
|
549
|
+
describe('fastify-route-validation — branches', () => {
|
|
550
|
+
it('routes with and without schema option', async () => {
|
|
551
|
+
fx('src/routes/users.ts', [
|
|
552
|
+
'import type { FastifyInstance } from "fastify"',
|
|
553
|
+
'export async function register(app: FastifyInstance) {',
|
|
554
|
+
' app.get("/users", { schema: { response: { 200: { type: "object" } } } }, async () => [])',
|
|
555
|
+
' app.post("/users", async (req) => req.body)',
|
|
556
|
+
' app.put("/users/:id", { schema: { params: { type: "object" } } }, async () => ({}))',
|
|
557
|
+
' app.delete("/users/:id", async () => ({}))',
|
|
558
|
+
' app.route({ method: "GET", url: "/x", handler: async () => ({}) })',
|
|
559
|
+
' app.route({ method: "POST", url: "/y", schema: { body: {} }, handler: async () => ({}) })',
|
|
560
|
+
'}',
|
|
561
|
+
].join('\n'));
|
|
562
|
+
const result = await runCheck('fastify-route-validation');
|
|
563
|
+
expect(result).toBeDefined();
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
// ---------------------------------------------------------------------------
|
|
567
|
+
// api-contract-validation
|
|
568
|
+
// ---------------------------------------------------------------------------
|
|
569
|
+
describe('api-contract-validation — branches', () => {
|
|
570
|
+
it('handlers with missing/with return types and try/catch', async () => {
|
|
571
|
+
fx('src/api/handlers.ts', [
|
|
572
|
+
'export async function noTypes(req: unknown, _res: unknown) {',
|
|
573
|
+
' const body = (req as any).body',
|
|
574
|
+
' return body',
|
|
575
|
+
'}',
|
|
576
|
+
'export async function withTypes(req: { body: string }): Promise<{ ok: boolean }> {',
|
|
577
|
+
' try {',
|
|
578
|
+
' return { ok: req.body !== "" }',
|
|
579
|
+
' } catch (e) {',
|
|
580
|
+
' return { ok: false }',
|
|
581
|
+
' }',
|
|
582
|
+
'}',
|
|
583
|
+
'export function nonAsync(req: any) { return req }',
|
|
584
|
+
'export const arrowHandler = async (req: any): Promise<unknown> => req',
|
|
585
|
+
'export class Ctrl {',
|
|
586
|
+
' async handle(req: any): Promise<unknown> { return req }',
|
|
587
|
+
' syncMethod(req: any) { return req }',
|
|
588
|
+
'}',
|
|
589
|
+
].join('\n'));
|
|
590
|
+
const result = await runCheck('api-contract-validation');
|
|
591
|
+
expect(result).toBeDefined();
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
// ---------------------------------------------------------------------------
|
|
595
|
+
// di-static-inject-usage
|
|
596
|
+
// ---------------------------------------------------------------------------
|
|
597
|
+
// ---------------------------------------------------------------------------
|
|
598
|
+
// duplicate-utility-functions
|
|
599
|
+
// ---------------------------------------------------------------------------
|
|
600
|
+
describe('duplicate-utility-functions — branches', () => {
|
|
601
|
+
it('multiple files with duplicate functions vs unique', async () => {
|
|
602
|
+
fx('src/util/a.ts', [
|
|
603
|
+
'export function add(a: number, b: number): number { return a + b }',
|
|
604
|
+
'export function uniqueA() { return 1 }',
|
|
605
|
+
'export function helper(s: string) { return s.toUpperCase() }',
|
|
606
|
+
].join('\n'));
|
|
607
|
+
fx('src/util/b.ts', [
|
|
608
|
+
'export function add(a: number, b: number): number { return a + b }', // duplicate
|
|
609
|
+
'export function uniqueB() { return 2 }',
|
|
610
|
+
'export function helper(s: string) { return s.toUpperCase() }', // duplicate
|
|
611
|
+
].join('\n'));
|
|
612
|
+
fx('src/util/c.ts', [
|
|
613
|
+
'export function add(a: number, b: number): number { return a + b }', // triple
|
|
614
|
+
].join('\n'));
|
|
615
|
+
const result = await runCheck('duplicate-utility-functions');
|
|
616
|
+
expect(result).toBeDefined();
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
// ---------------------------------------------------------------------------
|
|
620
|
+
// no-any-types
|
|
621
|
+
// ---------------------------------------------------------------------------
|
|
622
|
+
describe('no-any-types — branches', () => {
|
|
623
|
+
it('detects various any forms', async () => {
|
|
624
|
+
fx('src/types/anys.ts', [
|
|
625
|
+
'export const a: any = 1',
|
|
626
|
+
'export function f(x: any): any { return x }',
|
|
627
|
+
'export const obj: { [k: string]: any } = {}',
|
|
628
|
+
'export type T<T = any> = T',
|
|
629
|
+
'export const arr: any[] = []',
|
|
630
|
+
'export const cast = (x: unknown) => x as any',
|
|
631
|
+
'export interface I { x: any; y: unknown }',
|
|
632
|
+
].join('\n'));
|
|
633
|
+
const result = await runCheck('no-any-types');
|
|
634
|
+
expect(result).toBeDefined();
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
// ---------------------------------------------------------------------------
|
|
638
|
+
// pii-exposure-in-logs
|
|
639
|
+
// ---------------------------------------------------------------------------
|
|
640
|
+
describe('pii-exposure-in-logs — branches', () => {
|
|
641
|
+
it('logs that include PII keywords vs safe logs', async () => {
|
|
642
|
+
fx('src/svc/log.ts', [
|
|
643
|
+
'export function f(req: any) {',
|
|
644
|
+
' logger.info({ email: req.email })',
|
|
645
|
+
' logger.warn({ password: req.password })',
|
|
646
|
+
' logger.error({ ssn: req.ssn })',
|
|
647
|
+
' logger.info({ requestId: req.requestId })',
|
|
648
|
+
' logger.info("processing request")',
|
|
649
|
+
' console.log(req.body)',
|
|
650
|
+
' console.log({ ip: req.ip })',
|
|
651
|
+
' console.error(req)',
|
|
652
|
+
'}',
|
|
653
|
+
].join('\n'));
|
|
654
|
+
const result = await runCheck('pii-exposure-in-logs');
|
|
655
|
+
expect(result).toBeDefined();
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
// ---------------------------------------------------------------------------
|
|
659
|
+
// logger-event-name-format
|
|
660
|
+
// ---------------------------------------------------------------------------
|
|
661
|
+
describe('logger-event-name-format — branches', () => {
|
|
662
|
+
it('various event-name shapes (snake, kebab, camelCase, with periods, missing)', async () => {
|
|
663
|
+
fx('src/svc/events.ts', [
|
|
664
|
+
'logger.info("event_emitted")',
|
|
665
|
+
'logger.info("event-emitted")',
|
|
666
|
+
'logger.info("eventEmitted")',
|
|
667
|
+
'logger.info("Event Emitted")',
|
|
668
|
+
'logger.info("module.event_emitted")',
|
|
669
|
+
'logger.info({ event: "payment.processed", amount: 1 })',
|
|
670
|
+
'logger.info({ event: "WrongCase", x: 1 })',
|
|
671
|
+
'logger.warn({ a: 1 })',
|
|
672
|
+
'logger.error("simple")',
|
|
673
|
+
].join('\n'));
|
|
674
|
+
const result = await runCheck('logger-event-name-format');
|
|
675
|
+
expect(result).toBeDefined();
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
// ---------------------------------------------------------------------------
|
|
679
|
+
// platform-checks
|
|
680
|
+
// ---------------------------------------------------------------------------
|
|
681
|
+
// ---------------------------------------------------------------------------
|
|
682
|
+
// lazy-loading
|
|
683
|
+
// ---------------------------------------------------------------------------
|
|
684
|
+
// ---------------------------------------------------------------------------
|
|
685
|
+
// flashlist-enforcement
|
|
686
|
+
// ---------------------------------------------------------------------------
|
|
687
|
+
// ---------------------------------------------------------------------------
|
|
688
|
+
// client-boundary-placement
|
|
689
|
+
// ---------------------------------------------------------------------------
|
|
690
|
+
// ---------------------------------------------------------------------------
|
|
691
|
+
// test-only-frontend-modules
|
|
692
|
+
// ---------------------------------------------------------------------------
|
|
693
|
+
describe('test-only-frontend-modules — branches', () => {
|
|
694
|
+
it('mocks vs real implementations', async () => {
|
|
695
|
+
fx('src/api/__mocks__/users.ts', 'export const fetchUsers = () => []');
|
|
696
|
+
fx('src/api/users.ts', 'export const fetchUsers = async () => fetch("/users")');
|
|
697
|
+
fx('src/api/test.helpers.ts', 'export const mockUser = () => ({ id: "1" })');
|
|
698
|
+
const result = await runCheck('test-only-frontend-modules');
|
|
699
|
+
expect(result).toBeDefined();
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
// ---------------------------------------------------------------------------
|
|
703
|
+
// stream-buffer-size-limits
|
|
704
|
+
// ---------------------------------------------------------------------------
|
|
705
|
+
describe('stream-buffer-size-limits — branches', () => {
|
|
706
|
+
it('bounded vs unbounded streams', async () => {
|
|
707
|
+
fx('src/svc/stream.ts', [
|
|
708
|
+
'import { Readable } from "stream"',
|
|
709
|
+
'export const limited = new Readable({ highWaterMark: 1024 })',
|
|
710
|
+
'export const unlimited = new Readable({})',
|
|
711
|
+
'export const buffered = (s: Readable) => s.pipe(somethingElse, { highWaterMark: 4096 })',
|
|
712
|
+
].join('\n'));
|
|
713
|
+
const result = await runCheck('stream-buffer-size-limits');
|
|
714
|
+
expect(result).toBeDefined();
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
// ---------------------------------------------------------------------------
|
|
718
|
+
// dispose-pattern-completeness
|
|
719
|
+
// ---------------------------------------------------------------------------
|
|
720
|
+
describe('dispose-pattern-completeness — branches', () => {
|
|
721
|
+
it('classes with/without proper dispose impl', async () => {
|
|
722
|
+
fx('src/svc/disp.ts', [
|
|
723
|
+
'export class A {',
|
|
724
|
+
' private timer: NodeJS.Timer | null = null',
|
|
725
|
+
' start() { this.timer = setInterval(() => undefined, 1000) }',
|
|
726
|
+
' dispose() { if (this.timer) clearInterval(this.timer) }',
|
|
727
|
+
'}',
|
|
728
|
+
'export class B {',
|
|
729
|
+
' private timer: NodeJS.Timer | null = null',
|
|
730
|
+
' start() { this.timer = setInterval(() => undefined, 1000) }',
|
|
731
|
+
'}',
|
|
732
|
+
'export class C {',
|
|
733
|
+
' [Symbol.asyncDispose]() {}',
|
|
734
|
+
'}',
|
|
735
|
+
'export class D implements Disposable {',
|
|
736
|
+
' [Symbol.dispose]() {}',
|
|
737
|
+
'}',
|
|
738
|
+
].join('\n'));
|
|
739
|
+
const result = await runCheck('dispose-pattern-completeness');
|
|
740
|
+
expect(result).toBeDefined();
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
// ---------------------------------------------------------------------------
|
|
744
|
+
// typeorm-n-plus-one
|
|
745
|
+
// ---------------------------------------------------------------------------
|
|
746
|
+
// ---------------------------------------------------------------------------
|
|
747
|
+
// postgres-n-plus-one
|
|
748
|
+
// ---------------------------------------------------------------------------
|
|
749
|
+
// ---------------------------------------------------------------------------
|
|
750
|
+
// financial-transaction-ordering
|
|
751
|
+
// ---------------------------------------------------------------------------
|
|
752
|
+
// ---------------------------------------------------------------------------
|
|
753
|
+
// database-index-coverage
|
|
754
|
+
// ---------------------------------------------------------------------------
|
|
755
|
+
describe('database-index-coverage — branches', () => {
|
|
756
|
+
it('drizzle table with and without indexes', async () => {
|
|
757
|
+
fx('src/db/schema/users.ts', [
|
|
758
|
+
'import { pgTable, serial, text, varchar, integer, index } from "drizzle-orm/pg-core"',
|
|
759
|
+
'export const users = pgTable("users", {',
|
|
760
|
+
' id: serial("id").primaryKey(),',
|
|
761
|
+
' email: varchar("email", { length: 255 }),',
|
|
762
|
+
' tenantId: integer("tenant_id"),',
|
|
763
|
+
'}, (t) => ({ emailIdx: index("email_idx").on(t.email) }))',
|
|
764
|
+
'export const posts = pgTable("posts", {',
|
|
765
|
+
' id: serial("id").primaryKey(),',
|
|
766
|
+
' userId: integer("user_id"),',
|
|
767
|
+
' title: text("title"),',
|
|
768
|
+
'})',
|
|
769
|
+
].join('\n'));
|
|
770
|
+
const result = await runCheck('database-index-coverage');
|
|
771
|
+
expect(result).toBeDefined();
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
// ---------------------------------------------------------------------------
|
|
775
|
+
// database-schema-validation
|
|
776
|
+
// ---------------------------------------------------------------------------
|
|
777
|
+
describe('database-schema-validation — branches', () => {
|
|
778
|
+
it('drizzle types with and without notNull, unique, default', async () => {
|
|
779
|
+
fx('src/db/schema/all.ts', [
|
|
780
|
+
'import { pgTable, serial, text, varchar, timestamp, integer } from "drizzle-orm/pg-core"',
|
|
781
|
+
'export const t1 = pgTable("t1", {',
|
|
782
|
+
' id: serial("id").primaryKey(),',
|
|
783
|
+
' name: text("name").notNull(),',
|
|
784
|
+
' email: varchar("email", { length: 255 }).notNull().unique(),',
|
|
785
|
+
' createdAt: timestamp("created_at").defaultNow().notNull(),',
|
|
786
|
+
' optional: text("optional"),',
|
|
787
|
+
' count: integer("count").default(0),',
|
|
788
|
+
'})',
|
|
789
|
+
].join('\n'));
|
|
790
|
+
const result = await runCheck('database-schema-validation');
|
|
791
|
+
expect(result).toBeDefined();
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
// ---------------------------------------------------------------------------
|
|
795
|
+
// in-memory-repository-detection
|
|
796
|
+
// ---------------------------------------------------------------------------
|
|
797
|
+
describe('in-memory-repository-detection — extra branches', () => {
|
|
798
|
+
it('multiple Repository classes with different storage shapes', async () => {
|
|
799
|
+
fx('src/repos/multi.ts', [
|
|
800
|
+
'export class FooRepository {',
|
|
801
|
+
' private byMap = new Map<string, number>()',
|
|
802
|
+
' private bySet = new Set<string>()',
|
|
803
|
+
' private byArray: { id: string }[] = []',
|
|
804
|
+
' private byObject: Record<string, unknown> = {}',
|
|
805
|
+
' async listAll() { return [...this.byMap.values()] }',
|
|
806
|
+
'}',
|
|
807
|
+
'// FooRepository',
|
|
808
|
+
].join('\n'));
|
|
809
|
+
fx('src/repos/Cache.ts', ['export class CacheRepository {', ' private byMap = new Map<string, number>()', '}'].join('\n'));
|
|
810
|
+
fx('src/repos/Mock.ts', ['export class MockRepository {', ' private byMap = new Map<string, number>()', '}'].join('\n'));
|
|
811
|
+
const result = await runCheck('in-memory-repository-detection');
|
|
812
|
+
expect(result).toBeDefined();
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
// ---------------------------------------------------------------------------
|
|
816
|
+
// sql-injection
|
|
817
|
+
// ---------------------------------------------------------------------------
|
|
818
|
+
describe('sql-injection — branches', () => {
|
|
819
|
+
it('template injection vs parameterized', async () => {
|
|
820
|
+
fx('src/api/db.ts', [
|
|
821
|
+
'export async function bad(id: string) {',
|
|
822
|
+
' return await db.query(`SELECT * FROM users WHERE id = ${id}`)',
|
|
823
|
+
'}',
|
|
824
|
+
'export async function good(id: string) {',
|
|
825
|
+
' return await db.query("SELECT * FROM users WHERE id = $1", [id])',
|
|
826
|
+
'}',
|
|
827
|
+
'export async function bad2(name: string) {',
|
|
828
|
+
' const sql = "SELECT * FROM users WHERE name = \'" + name + "\'"',
|
|
829
|
+
' return await db.query(sql)',
|
|
830
|
+
'}',
|
|
831
|
+
'export async function good2(name: string) {',
|
|
832
|
+
' const stmt = db.prepare("SELECT * FROM users WHERE name = ?")',
|
|
833
|
+
' return stmt.execute([name])',
|
|
834
|
+
'}',
|
|
835
|
+
].join('\n'));
|
|
836
|
+
const result = await runCheck('sql-injection');
|
|
837
|
+
expect(result).toBeDefined();
|
|
838
|
+
});
|
|
839
|
+
});
|
|
840
|
+
// ---------------------------------------------------------------------------
|
|
841
|
+
// input-sanitization
|
|
842
|
+
// ---------------------------------------------------------------------------
|
|
843
|
+
describe('input-sanitization — branches', () => {
|
|
844
|
+
it('html, fs, exec, and url cases', async () => {
|
|
845
|
+
fx('src/api/risk.ts', [
|
|
846
|
+
'import { exec } from "child_process"',
|
|
847
|
+
'import * as fs from "fs"',
|
|
848
|
+
'export function htmlBad(req: any) {',
|
|
849
|
+
' return `<div>${req.body.html}</div>`',
|
|
850
|
+
'}',
|
|
851
|
+
'export function fsBad(req: any) {',
|
|
852
|
+
' return fs.readFileSync("/etc/" + req.params.file)',
|
|
853
|
+
'}',
|
|
854
|
+
'export function execBad(req: any) {',
|
|
855
|
+
' exec("ls " + req.body.dir, () => undefined)',
|
|
856
|
+
'}',
|
|
857
|
+
'export function urlBad(req: any) {',
|
|
858
|
+
' return fetch("https://api.com/" + req.params.id)',
|
|
859
|
+
'}',
|
|
860
|
+
'export function safe() {',
|
|
861
|
+
' return fs.readFileSync("/etc/hosts")',
|
|
862
|
+
'}',
|
|
863
|
+
].join('\n'));
|
|
864
|
+
const result = await runCheck('input-sanitization');
|
|
865
|
+
expect(result).toBeDefined();
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
// ---------------------------------------------------------------------------
|
|
869
|
+
// unsafe-secret-comparison
|
|
870
|
+
// ---------------------------------------------------------------------------
|
|
871
|
+
describe('unsafe-secret-comparison — branches', () => {
|
|
872
|
+
it('detects ===, ==, .equals, allows timingSafeEqual', async () => {
|
|
873
|
+
fx('src/auth/x.ts', [
|
|
874
|
+
'import { timingSafeEqual } from "crypto"',
|
|
875
|
+
'export function bad1(secret: string, given: string) { return secret === given }',
|
|
876
|
+
'export function bad2(secret: string, given: string) { return secret == given }',
|
|
877
|
+
'export function bad3(a: Buffer, b: Buffer) { return a.equals(b) }',
|
|
878
|
+
'export function good(a: Buffer, b: Buffer) { return timingSafeEqual(a, b) }',
|
|
879
|
+
'export function notSecret(a: number, b: number) { return a === b }',
|
|
880
|
+
].join('\n'));
|
|
881
|
+
const result = await runCheck('unsafe-secret-comparison');
|
|
882
|
+
expect(result).toBeDefined();
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
// ---------------------------------------------------------------------------
|
|
886
|
+
// observability-coverage (helper) — covered via direct unit tests
|
|
887
|
+
// ---------------------------------------------------------------------------
|
|
888
|
+
// ---------------------------------------------------------------------------
|
|
889
|
+
// error-handling-quality
|
|
890
|
+
// ---------------------------------------------------------------------------
|
|
891
|
+
describe('error-handling-quality — branches', () => {
|
|
892
|
+
it('try/catch shapes and rethrow patterns', async () => {
|
|
893
|
+
fx('src/svc/eh.ts', [
|
|
894
|
+
'export function f1() { try { return 1 } catch { return 0 } }',
|
|
895
|
+
'export function f2() { try { return 1 } catch (e) { console.log(e); throw e } }',
|
|
896
|
+
'export function f3() { try { return 1 } catch (e) { logger.error({ err: e }); throw new Error("wrap") } }',
|
|
897
|
+
'export async function f4() { try { return await x() } catch { } }', // empty catch
|
|
898
|
+
'export function f5() { try { return 1 } catch (e: any) { return e.message } }',
|
|
899
|
+
'export function f6() { try { return 1 } catch (_e) { return null } }',
|
|
900
|
+
].join('\n'));
|
|
901
|
+
const result = await runCheck('error-handling-quality');
|
|
902
|
+
expect(result).toBeDefined();
|
|
903
|
+
});
|
|
904
|
+
});
|
|
905
|
+
// ---------------------------------------------------------------------------
|
|
906
|
+
// result-pattern-consistency
|
|
907
|
+
// ---------------------------------------------------------------------------
|
|
908
|
+
describe('result-pattern-consistency — branches', () => {
|
|
909
|
+
it('mixed Result and throws', async () => {
|
|
910
|
+
fx('src/svc/res.ts', [
|
|
911
|
+
'type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }',
|
|
912
|
+
'export function ok<T>(v: T): Result<T, never> { return { ok: true, value: v } }',
|
|
913
|
+
'export function err<E>(e: E): Result<never, E> { return { ok: false, error: e } }',
|
|
914
|
+
'export function viaResult(): Result<number, string> { return ok(1) }',
|
|
915
|
+
'export function viaThrow(): number { throw new Error("oops") }',
|
|
916
|
+
'export function mixedKind(x: number) {',
|
|
917
|
+
' if (x < 0) return err("neg")',
|
|
918
|
+
' if (x === 0) throw new Error("zero")',
|
|
919
|
+
' return ok(x)',
|
|
920
|
+
'}',
|
|
921
|
+
].join('\n'));
|
|
922
|
+
const result = await runCheck('result-pattern-consistency');
|
|
923
|
+
expect(result).toBeDefined();
|
|
924
|
+
});
|
|
925
|
+
});
|
|
926
|
+
// ---------------------------------------------------------------------------
|
|
927
|
+
// lifecycle-cleanup-enforcement
|
|
928
|
+
// ---------------------------------------------------------------------------
|
|
929
|
+
describe('lifecycle-cleanup-enforcement — branches', () => {
|
|
930
|
+
it('useEffect with and without cleanup', async () => {
|
|
931
|
+
fx('src/components/Eff.tsx', [
|
|
932
|
+
'import { useEffect } from "react"',
|
|
933
|
+
'export function A() {',
|
|
934
|
+
' useEffect(() => {',
|
|
935
|
+
' const id = setInterval(() => undefined, 100)',
|
|
936
|
+
' return () => clearInterval(id)',
|
|
937
|
+
' }, [])',
|
|
938
|
+
' return null',
|
|
939
|
+
'}',
|
|
940
|
+
'export function B() {',
|
|
941
|
+
' useEffect(() => {',
|
|
942
|
+
' setInterval(() => undefined, 100)',
|
|
943
|
+
' }, [])',
|
|
944
|
+
' return null',
|
|
945
|
+
'}',
|
|
946
|
+
'export function C() {',
|
|
947
|
+
' useEffect(() => {',
|
|
948
|
+
' const sub = src.subscribe(() => undefined)',
|
|
949
|
+
' return () => sub.unsubscribe()',
|
|
950
|
+
' }, [])',
|
|
951
|
+
' return null',
|
|
952
|
+
'}',
|
|
953
|
+
].join('\n'));
|
|
954
|
+
const result = await runCheck('lifecycle-cleanup-enforcement');
|
|
955
|
+
expect(result).toBeDefined();
|
|
956
|
+
});
|
|
957
|
+
});
|
|
958
|
+
// ---------------------------------------------------------------------------
|
|
959
|
+
// openapi-response-coverage
|
|
960
|
+
// ---------------------------------------------------------------------------
|
|
961
|
+
// ---------------------------------------------------------------------------
|
|
962
|
+
// broad fixture — many checks
|
|
963
|
+
// ---------------------------------------------------------------------------
|
|
964
|
+
describe('broad fixture — many checks', () => {
|
|
965
|
+
it('exercises a wide-shape fixture across many files', async () => {
|
|
966
|
+
fx('package.json', JSON.stringify({
|
|
967
|
+
name: 'demo',
|
|
968
|
+
version: '1.0.0',
|
|
969
|
+
type: 'module',
|
|
970
|
+
main: './dist/index.js',
|
|
971
|
+
types: './dist/index.d.ts',
|
|
972
|
+
exports: { '.': { import: './dist/index.js', types: './dist/index.d.ts' } },
|
|
973
|
+
dependencies: { 'drizzle-orm': '^0.30.0', react: '^18.0.0', 'typed-inject': '^4.0.0' },
|
|
974
|
+
}, null, 2));
|
|
975
|
+
fx('tsconfig.json', JSON.stringify({
|
|
976
|
+
extends: './tsconfig.base.json',
|
|
977
|
+
compilerOptions: { target: 'es2022', module: 'esnext', strict: true },
|
|
978
|
+
include: ['src/**/*'],
|
|
979
|
+
}, null, 2));
|
|
980
|
+
fx('tsconfig.base.json', JSON.stringify({ compilerOptions: { strict: true } }, null, 2));
|
|
981
|
+
fx('src/index.ts', 'export * from "./svc.js"');
|
|
982
|
+
fx('src/svc.ts', [
|
|
983
|
+
'export interface UserRequest { id: string }',
|
|
984
|
+
'export interface UserResponse { ok: boolean }',
|
|
985
|
+
'export type UserDTO = { id: string }',
|
|
986
|
+
'export interface ApiUserList { users: UserDTO[] }',
|
|
987
|
+
'export async function getUsers(req: UserRequest): Promise<UserResponse> {',
|
|
988
|
+
' if (!req.id) return { ok: false }',
|
|
989
|
+
' try { return { ok: true } } catch (e) { return { ok: false } }',
|
|
990
|
+
'}',
|
|
991
|
+
].join('\n'));
|
|
992
|
+
fx('src/observability/m.ts', [
|
|
993
|
+
'export function record(name: string, value: number) {',
|
|
994
|
+
' // no logger here',
|
|
995
|
+
' return { name, value }',
|
|
996
|
+
'}',
|
|
997
|
+
].join('\n'));
|
|
998
|
+
fx('src/observability/with.ts', [
|
|
999
|
+
'import logger from "./logger.js"',
|
|
1000
|
+
'export function recordLogged(name: string, value: number) {',
|
|
1001
|
+
' logger.info({ event: "metric.recorded", name, value })',
|
|
1002
|
+
' return { name, value }',
|
|
1003
|
+
'}',
|
|
1004
|
+
].join('\n'));
|
|
1005
|
+
fx('src/observability/logger.ts', [
|
|
1006
|
+
'export const logger = {',
|
|
1007
|
+
' info: (..._args: unknown[]) => undefined,',
|
|
1008
|
+
' warn: (..._args: unknown[]) => undefined,',
|
|
1009
|
+
' error: (..._args: unknown[]) => undefined,',
|
|
1010
|
+
' child: () => logger,',
|
|
1011
|
+
'}',
|
|
1012
|
+
'export default logger',
|
|
1013
|
+
].join('\n'));
|
|
1014
|
+
// Run a handful of checks to exercise broader branches.
|
|
1015
|
+
const slugs = [
|
|
1016
|
+
'detached-promises',
|
|
1017
|
+
'no-unbounded-concurrency',
|
|
1018
|
+
'no-raw-fetch',
|
|
1019
|
+
'array-validation',
|
|
1020
|
+
'numeric-validation',
|
|
1021
|
+
'null-safety',
|
|
1022
|
+
'incomplete-regex-escaping',
|
|
1023
|
+
'missing-input-validation',
|
|
1024
|
+
'fastify-route-validation',
|
|
1025
|
+
'api-contract-validation',
|
|
1026
|
+
'api-response-validation',
|
|
1027
|
+
'silent-early-returns',
|
|
1028
|
+
'throws-documentation',
|
|
1029
|
+
'error-handling-quality',
|
|
1030
|
+
'logger-event-name-format',
|
|
1031
|
+
'pii-exposure-in-logs',
|
|
1032
|
+
'sql-injection',
|
|
1033
|
+
'unsafe-secret-comparison',
|
|
1034
|
+
'input-sanitization',
|
|
1035
|
+
'no-any-types',
|
|
1036
|
+
'duplicate-utility-functions',
|
|
1037
|
+
'stubbed-implementation-detection',
|
|
1038
|
+
'missing-type-exports',
|
|
1039
|
+
'toctou-race-condition',
|
|
1040
|
+
'lifecycle-cleanup-enforcement',
|
|
1041
|
+
'dispose-pattern-completeness',
|
|
1042
|
+
'stream-buffer-size-limits',
|
|
1043
|
+
'result-pattern-consistency',
|
|
1044
|
+
'database-schema-validation',
|
|
1045
|
+
'database-index-coverage',
|
|
1046
|
+
'in-memory-repository-detection',
|
|
1047
|
+
];
|
|
1048
|
+
for (const slug of slugs) {
|
|
1049
|
+
const result = await runCheck(slug);
|
|
1050
|
+
expect(result).toBeDefined();
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
// ---------------------------------------------------------------------------
|
|
1055
|
+
// path-matching utility — exercise both string and regex branches
|
|
1056
|
+
// ---------------------------------------------------------------------------
|
|
1057
|
+
describe('path-matching utility — branches', () => {
|
|
1058
|
+
it('exercises string includes and regex test patterns', async () => {
|
|
1059
|
+
const { createPathMatcher } = await import('@opensip-cli/fitness');
|
|
1060
|
+
const stringOnly = createPathMatcher(['/__tests__/']);
|
|
1061
|
+
expect(stringOnly('/src/__tests__/foo.ts')).toBe(true);
|
|
1062
|
+
expect(stringOnly('/src/main.ts')).toBe(false);
|
|
1063
|
+
const regexOnly = createPathMatcher([/\.test\.ts$/]);
|
|
1064
|
+
expect(regexOnly('foo.test.ts')).toBe(true);
|
|
1065
|
+
expect(regexOnly('foo.ts')).toBe(false);
|
|
1066
|
+
const mixed = createPathMatcher(['/dist/', /node_modules/]);
|
|
1067
|
+
expect(mixed('/proj/dist/x.js')).toBe(true);
|
|
1068
|
+
expect(mixed('/proj/node_modules/lib/index.js')).toBe(true);
|
|
1069
|
+
expect(mixed('/proj/src/x.ts')).toBe(false);
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
//# sourceMappingURL=branch-fixtures.test.js.map
|