@critiq/rules 0.0.1 → 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 +201 -0
- package/README.md +256 -140
- package/catalog.yaml +985 -19
- package/package.json +7 -1
- package/rules/go/go.performance.no-regex-construction-in-loop.rule.yaml +33 -0
- package/rules/go/go.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
- package/rules/go/go.performance.no-unbounded-concurrency.rule.yaml +33 -0
- package/rules/go/go.security.echo-sensitive-binding-without-validation.rule.yaml +46 -0
- package/rules/go/go.security.echo-unsafe-multipart-upload.rule.yaml +45 -0
- package/rules/go/go.security.fiber-sensitive-binding-without-validation.rule.yaml +45 -0
- package/rules/go/go.security.fiber-unsafe-multipart-upload.rule.yaml +45 -0
- package/rules/go/go.security.gin-sensitive-binding-without-validation.rule.yaml +45 -0
- package/rules/go/go.security.gin-trust-all-proxies.rule.yaml +45 -0
- package/rules/go/go.security.gin-wildcard-cors-with-credentials.rule.yaml +47 -0
- package/rules/go/go.security.net-http-missing-timeouts.rule.yaml +45 -0
- package/rules/go/go.security.sensitive-data-egress.rule.yaml +46 -0
- package/rules/go/go.security.tar-path-traversal.rule.yaml +45 -0
- package/rules/go/go.security.template-unescaped-request-value.rule.yaml +45 -0
- package/rules/go/go.testing.real-network-in-unit-test.rule.yaml +33 -0
- package/rules/go/go.testing.t-skip-without-ticket-reference.rule.yaml +33 -0
- package/rules/go/go.testing.time-sleep-in-unit-test.rule.yaml +33 -0
- package/rules/java/java.performance.no-regex-construction-in-loop.rule.yaml +33 -0
- package/rules/java/java.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
- package/rules/java/java.performance.no-unbounded-concurrency.rule.yaml +33 -0
- package/rules/java/java.security.android-screenshot-exposure.rule.yaml +35 -0
- package/rules/java/java.security.android-world-readable-mode.rule.yaml +35 -0
- package/rules/java/java.security.jpa-concatenated-query.rule.yaml +47 -0
- package/rules/java/java.security.reflected-output-from-request.rule.yaml +35 -0
- package/rules/java/java.security.servlet-insecure-cookie.rule.yaml +35 -0
- package/rules/java/java.security.spring-actuator-health-details-always.rule.yaml +40 -0
- package/rules/java/java.security.spring-actuator-sensitive-exposure.rule.yaml +40 -0
- package/rules/java/java.security.spring-csrf-globally-disabled.rule.yaml +49 -0
- package/rules/java/java.security.spring-debug-exposure.rule.yaml +35 -0
- package/rules/java/java.security.spring-permit-all-default.rule.yaml +47 -0
- package/rules/java/java.security.spring-webmvc-unrestricted-data-binding.rule.yaml +47 -0
- package/rules/java/java.security.template-unescaped-user-output.rule.yaml +49 -0
- package/rules/java/java.testing.disabled-without-ticket-reference.rule.yaml +33 -0
- package/rules/java/java.testing.http-client-in-unit-test.rule.yaml +33 -0
- package/rules/java/java.testing.thread-sleep-in-unit-test.rule.yaml +33 -0
- package/rules/php/php.performance.no-regex-construction-in-loop.rule.yaml +33 -0
- package/rules/php/php.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
- package/rules/php/php.performance.no-unbounded-concurrency.rule.yaml +33 -0
- package/rules/php/php.security.insecure-cors-wildcard-with-credentials.rule.yaml +41 -0
- package/rules/php/php.security.insecure-mail-or-file-transport.rule.yaml +41 -0
- package/rules/php/php.security.insecure-session-or-cookie-config.rule.yaml +42 -0
- package/rules/php/php.security.laravel-sensitive-csrf-exclusion.rule.yaml +42 -0
- package/rules/php/php.security.laravel-unsafe-blade-output.rule.yaml +42 -0
- package/rules/php/php.security.laravel-unsafe-mass-assignment.rule.yaml +45 -0
- package/rules/php/php.security.sensitive-data-egress.rule.yaml +42 -0
- package/rules/php/php.security.symfony-csrf-disabled.rule.yaml +42 -0
- package/rules/php/php.security.symfony-debug-exposure.rule.yaml +44 -0
- package/rules/php/php.security.unsafe-file-upload-handling.rule.yaml +41 -0
- package/rules/php/php.security.wordpress-missing-nonce-or-capability.rule.yaml +42 -0
- package/rules/php/php.security.wordpress-unprepared-sql.rule.yaml +42 -0
- package/rules/php/php.testing.curl-in-unit-test.rule.yaml +33 -0
- package/rules/php/php.testing.mark-test-skipped-without-ticket-reference.rule.yaml +33 -0
- package/rules/php/php.testing.sleep-in-unit-test.rule.yaml +33 -0
- package/rules/python/py.performance.no-regex-construction-in-loop.rule.yaml +33 -0
- package/rules/python/py.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
- package/rules/python/py.performance.no-unbounded-concurrency.rule.yaml +33 -0
- package/rules/python/py.security.django-csrf-exempt-state-changing.rule.yaml +46 -0
- package/rules/python/py.security.django-missing-csrf-middleware.rule.yaml +47 -0
- package/rules/python/py.security.django-unsafe-production-settings.rule.yaml +47 -0
- package/rules/python/py.security.drf-allow-any-default.rule.yaml +46 -0
- package/rules/python/py.security.drf-allow-any-unsafe-method.rule.yaml +46 -0
- package/rules/python/py.security.fastapi-insecure-cors.rule.yaml +43 -0
- package/rules/python/py.security.flask-missing-upload-body-limit.rule.yaml +44 -0
- package/rules/python/py.security.flask-unsafe-html-output.rule.yaml +44 -0
- package/rules/python/py.security.flask-unsafe-upload-filename.rule.yaml +44 -0
- package/rules/python/py.testing.pytest-skip-without-ticket-reference.rule.yaml +33 -0
- package/rules/python/py.testing.real-network-in-unit-test.rule.yaml +33 -0
- package/rules/python/py.testing.time-sleep-in-unit-test.rule.yaml +33 -0
- package/rules/ruby/ruby.performance.no-regex-construction-in-loop.rule.yaml +33 -0
- package/rules/ruby/ruby.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
- package/rules/ruby/ruby.performance.no-unbounded-concurrency.rule.yaml +33 -0
- package/rules/ruby/ruby.security.rails-csrf-disabled.rule.yaml +45 -0
- package/rules/ruby/ruby.security.rails-detailed-exceptions-enabled.rule.yaml +44 -0
- package/rules/ruby/ruby.security.rails-open-redirect.rule.yaml +45 -0
- package/rules/ruby/ruby.security.rails-unsafe-html-output.rule.yaml +46 -0
- package/rules/ruby/ruby.security.rails-unsafe-render.rule.yaml +45 -0
- package/rules/ruby/ruby.security.rails-unsafe-session-or-cookie-store.rule.yaml +45 -0
- package/rules/ruby/ruby.security.rails-unsafe-strong-parameters.rule.yaml +46 -0
- package/rules/ruby/ruby.security.sensitive-data-egress.rule.yaml +45 -0
- package/rules/ruby/ruby.security.sidekiq-web-unauthenticated-mount.rule.yaml +45 -0
- package/rules/ruby/ruby.testing.focused-example.rule.yaml +33 -0
- package/rules/ruby/ruby.testing.pending-without-ticket-reference.rule.yaml +33 -0
- package/rules/ruby/ruby.testing.real-network-in-unit-test.rule.yaml +33 -0
- package/rules/ruby/ruby.testing.skip-without-ticket-reference.rule.yaml +33 -0
- package/rules/ruby/ruby.testing.sleep-in-unit-test.rule.yaml +33 -0
- package/rules/rust/rust.performance.no-regex-construction-in-loop.rule.yaml +33 -0
- package/rules/rust/rust.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
- package/rules/rust/rust.performance.no-unbounded-concurrency.rule.yaml +33 -0
- package/rules/rust/rust.security.actix-wildcard-cors-with-credentials.rule.yaml +47 -0
- package/rules/rust/rust.security.axum-body-limit-disabled.rule.yaml +45 -0
- package/rules/rust/rust.security.axum-insecure-cors-with-credentials.rule.yaml +47 -0
- package/rules/rust/rust.security.rocket-panic-prone-request-handler.rule.yaml +45 -0
- package/rules/rust/rust.security.rocket-unsafe-template-output.rule.yaml +47 -0
- package/rules/rust/rust.security.sqlx-diesel-raw-interpolated-query.rule.yaml +47 -0
- package/rules/rust/rust.security.template-unescaped-request-value.rule.yaml +47 -0
- package/rules/rust/rust.security.warp-blocking-or-panic-in-async-handler.rule.yaml +45 -0
- package/rules/rust/rust.testing.ignore-without-ticket-reference.rule.yaml +33 -0
- package/rules/rust/rust.testing.real-network-in-unit-test.rule.yaml +33 -0
- package/rules/rust/rust.testing.thread-sleep-in-unit-test.rule.yaml +33 -0
- package/rules/shared/security.archive-path-traversal.rule.yaml +41 -0
- package/rules/shared/security.external-file-upload.rule.yaml +40 -0
- package/rules/shared/security.permissive-file-permissions.rule.yaml +40 -0
- package/rules/shared/security.sensitive-data-egress.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.assignment-in-condition.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.assignment-to-import-binding.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.async-promise-executor.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.duplicate-function-parameter.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.duplicate-import-source.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.duplicate-object-key.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.duplicate-switch-case.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.empty-block-statement.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.identical-comparison-operands.rule.yaml +36 -0
- package/rules/typescript/ts.correctness.reassign-catch-binding.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.regexp-pattern-unusual-control-character.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.self-assignment.rule.yaml +36 -0
- package/rules/typescript/ts.next.server-action-missing-local-auth.rule.yaml +35 -0
- package/rules/typescript/ts.performance.no-array-spread-in-hot-loop.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-cache-miss-from-unstable-key.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-expensive-sort-in-render-path.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-json-parse-stringify-clone.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-large-object-spread-in-loop.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-n-plus-one-await-in-map.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-redundant-network-fetch.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-regex-construction-in-loop.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-sync-fs-in-request-path.rule.yaml +32 -0
- package/rules/typescript/ts.performance.no-unbounded-concurrency.rule.yaml +32 -0
- package/rules/typescript/ts.quality.no-ambiguous-abbreviations.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-barrel-file-cycle.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-boolean-parameter-trap.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-dead-export.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-hidden-side-effect-import.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-inconsistent-error-shape.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-mixed-abstraction-level.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-primitive-obsession-in-domain-model.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-temporal-coupling.rule.yaml +27 -0
- package/rules/typescript/ts.quality.no-wide-public-surface.rule.yaml +27 -0
- package/rules/typescript/ts.react.no-accessibility-label-missing.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-activedescendant-on-non-focusable-host.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-click-without-keyboard-handler.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-deprecated-create-factory.rule.yaml +34 -0
- package/rules/typescript/ts.react.no-deprecated-react-dom-root-api.rule.yaml +34 -0
- package/rules/typescript/ts.react.no-derived-state-from-props.rule.yaml +34 -0
- package/rules/typescript/ts.react.no-effect-fetch-without-cancellation.rule.yaml +35 -0
- package/rules/typescript/ts.react.no-find-dom-node.rule.yaml +34 -0
- package/rules/typescript/ts.react.no-img-missing-alt-text.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-index-as-key-in-dynamic-list.rule.yaml +34 -0
- package/rules/typescript/ts.react.no-interactive-role-on-static-semantics.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-invalid-anchor-href.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-keyboard-interaction-without-widget-role.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-legacy-lifecycle.rule.yaml +34 -0
- package/rules/typescript/ts.react.no-missing-error-boundary.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-positive-tabindex.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-static-element-with-synthetic-handlers.rule.yaml +36 -0
- package/rules/typescript/ts.react.no-string-ref.rule.yaml +34 -0
- package/rules/typescript/ts.react.no-uncontrolled-to-controlled-input.rule.yaml +34 -0
- package/rules/typescript/ts.react.no-widget-role-without-tabindex.rule.yaml +36 -0
- package/rules/typescript/ts.security.ajv-insecure-configuration.rule.yaml +34 -0
- package/rules/typescript/ts.security.angular-dom-sanitizer-bypass-untrusted-input.rule.yaml +35 -0
- package/rules/typescript/ts.security.apollo-server-csrf-disabled.rule.yaml +36 -0
- package/rules/typescript/ts.security.apollo-server-graphql-dev-tooling-exposure.rule.yaml +36 -0
- package/rules/typescript/ts.security.apollo-server-introspection-exposure.rule.yaml +35 -0
- package/rules/typescript/ts.security.apollo-server-missing-query-limits.rule.yaml +35 -0
- package/rules/typescript/ts.security.astro-vite-public-secret-define.rule.yaml +39 -0
- package/rules/typescript/ts.security.debug-statement-in-source.rule.yaml +36 -0
- package/rules/typescript/ts.security.electron-dangerous-webpreferences.rule.yaml +35 -0
- package/rules/typescript/ts.security.electron-insecure-local-state.rule.yaml +35 -0
- package/rules/typescript/ts.security.electron-missing-ipc-origin-check.rule.yaml +35 -0
- package/rules/typescript/ts.security.electron-shell-open-external-unvalidated.rule.yaml +35 -0
- package/rules/typescript/ts.security.express-error-handler-information-disclosure.rule.yaml +35 -0
- package/rules/typescript/ts.security.express-static-dotfiles-allow.rule.yaml +35 -0
- package/rules/typescript/ts.security.express-unbounded-body-parser.rule.yaml +34 -0
- package/rules/typescript/ts.security.express-user-controlled-static-mount.rule.yaml +35 -0
- package/rules/typescript/ts.security.fastify-excessive-body-limit.rule.yaml +34 -0
- package/rules/typescript/ts.security.fastify-public-bind-without-trust-proxy.rule.yaml +38 -0
- package/rules/typescript/ts.security.graphql-upload-without-csrf-guard.rule.yaml +36 -0
- package/rules/typescript/ts.security.iframe-missing-sandbox-attribute.rule.yaml +35 -0
- package/rules/typescript/ts.security.insecure-content-security-policy-literal.rule.yaml +35 -0
- package/rules/typescript/ts.security.insecure-helmet-hardening-options.rule.yaml +36 -0
- package/rules/typescript/ts.security.jwt-insecure-signing-algorithm.rule.yaml +35 -0
- package/rules/typescript/ts.security.legacy-buffer-constructor.rule.yaml +35 -0
- package/rules/typescript/ts.security.log-injection.rule.yaml +36 -0
- package/rules/typescript/ts.security.nestjs-helmet-after-route-mount.rule.yaml +34 -0
- package/rules/typescript/ts.security.nestjs-missing-global-validation-pipe.rule.yaml +35 -0
- package/rules/typescript/ts.security.nestjs-skip-throttle-sensitive-route.rule.yaml +35 -0
- package/rules/typescript/ts.security.nestjs-validation-pipe-without-whitelist.rule.yaml +36 -0
- package/rules/typescript/ts.security.nuxt-public-runtime-secret.rule.yaml +38 -0
- package/rules/typescript/ts.security.open-redirect.rule.yaml +2 -0
- package/rules/typescript/ts.security.request-driven-array-index-access.rule.yaml +33 -0
- package/rules/typescript/ts.security.sensitive-data-egress.rule.yaml +1 -0
- package/rules/typescript/ts.security.ssrf.rule.yaml +1 -0
- package/rules/typescript/ts.security.unsafe-dompurify-version.rule.yaml +36 -0
- package/rules/typescript/ts.security.unsafe-marked-version.rule.yaml +36 -0
- package/rules/typescript/ts.security.xml-parse-string-with-untrusted-input.rule.yaml +35 -0
- package/rules/typescript/ts.testing.no-flaky-timer-test.rule.yaml +38 -0
- package/rules/typescript/ts.testing.no-focused-test.rule.yaml +34 -0
- package/rules/typescript/ts.testing.no-missing-edge-case-tests.rule.yaml +35 -0
- package/rules/typescript/ts.testing.no-network-call-in-unit-test.rule.yaml +38 -0
- package/rules/typescript/ts.testing.no-skipped-test-without-ticket.rule.yaml +34 -0
- package/rules/typescript/ts.testing.no-snapshot-without-intent.rule.yaml +34 -0
- package/rules/typescript/ts.testing.no-test-only-code-in-production.rule.yaml +38 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-deprecated-create-factory
|
|
5
|
+
title: Avoid React.createFactory
|
|
6
|
+
summary: "`createFactory` is a legacy helper for pre-JSX code and is removed from modern React typings and guidance."
|
|
7
|
+
rationale: Function components and JSX provide clearer element construction without the indirection and weaker typing of factory helpers.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: ui.react.deprecated-create-factory
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.ui
|
|
25
|
+
severity: low
|
|
26
|
+
confidence: 0.86
|
|
27
|
+
tags:
|
|
28
|
+
- react
|
|
29
|
+
- ui
|
|
30
|
+
message:
|
|
31
|
+
title: Replace createFactory with JSX or createElement
|
|
32
|
+
summary: "`${captures.issue.text}` relies on the deprecated `createFactory` helper."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Inline JSX, call `React.createElement` with explicit props, or convert the call site to a small function component.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-deprecated-react-dom-root-api
|
|
5
|
+
title: Migrate off legacy ReactDOM render entrypoints
|
|
6
|
+
summary: "`render`, `hydrate`, and `unmountComponentAtNode` from `react-dom` are legacy APIs replaced by the `createRoot` and `hydrateRoot` clients."
|
|
7
|
+
rationale: The concurrent renderer expects roots created through the client API; legacy entrypoints block adoption of React 18 streaming and suspense features.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: ui.react.deprecated-react-dom-root-api
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.ui
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.88
|
|
27
|
+
tags:
|
|
28
|
+
- react
|
|
29
|
+
- ui
|
|
30
|
+
message:
|
|
31
|
+
title: Switch to createRoot or hydrateRoot
|
|
32
|
+
summary: "`${captures.issue.text}` calls a deprecated ReactDOM mounting API."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Import `createRoot` or `hydrateRoot` from `react-dom/client`, create a root once, and use `root.render` for updates instead of legacy `ReactDOM.render`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-derived-state-from-props
|
|
5
|
+
title: Avoid initializing state directly from props
|
|
6
|
+
summary: Duplicating props into useState without an explicit sync strategy hides updates and confuses controlled versus uncontrolled boundaries.
|
|
7
|
+
rationale: Props-driven initial state goes stale when props change unless you synchronize intentionally or lift state up.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: ui.react.derived-state-from-props
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.ui
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.72
|
|
27
|
+
tags:
|
|
28
|
+
- react
|
|
29
|
+
- ui
|
|
30
|
+
message:
|
|
31
|
+
title: Derive values from props without fragile state copies
|
|
32
|
+
summary: "`${captures.issue.text}` initializes state from incoming props."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Prefer controlled props, derive with useMemo, or synchronize with useEffect when props legitimately own the value.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-effect-fetch-without-cancellation
|
|
5
|
+
title: Cancel inflight fetches inside React effects
|
|
6
|
+
summary: React effects that fetch remote data should attach AbortSignal wiring so stale responses cannot commit after dependencies change.
|
|
7
|
+
rationale: Race conditions from abandoned requests produce inconsistent UI state and duplicated side effects when identifiers or routes change quickly.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- react
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: performance.react-effect-fetch-without-cancellation
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: performance.ui
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.74
|
|
27
|
+
tags:
|
|
28
|
+
- performance
|
|
29
|
+
- react
|
|
30
|
+
message:
|
|
31
|
+
title: Abort stale fetch responses inside effects
|
|
32
|
+
summary: "`${captures.issue.text}` loads remote data without an AbortSignal or comparable cancellation guard."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: >-
|
|
35
|
+
Thread AbortController.signal through fetch or axios calls and abort inside the effect cleanup, or migrate to a data library that manages cancellation.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-find-dom-node
|
|
5
|
+
title: Avoid ReactDOM.findDOMNode
|
|
6
|
+
summary: "`findDOMNode` reaches through component boundaries with a deprecated escape hatch that breaks strict mode migrations."
|
|
7
|
+
rationale: Direct DOM lookups make React trees harder to refactor and fail to model multi-node or fragment-based rendering safely.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: ui.react.find-dom-node
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.ui
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.8
|
|
27
|
+
tags:
|
|
28
|
+
- react
|
|
29
|
+
- ui
|
|
30
|
+
message:
|
|
31
|
+
title: "Replace `findDOMNode` with direct refs"
|
|
32
|
+
summary: "`${captures.issue.text}` uses the deprecated `findDOMNode` API."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Attach a ref directly to the rendered element, or forward refs through component boundaries instead of reading DOM nodes imperatively.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-img-missing-alt-text
|
|
5
|
+
title: Add alt text to meaningful images
|
|
6
|
+
summary: "JSX images need a meaningful `alt` value, or an explicit empty string when the image is decorative."
|
|
7
|
+
rationale: Missing alternative text hides visual meaning from screen reader users and creates avoidable accessibility regressions.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- accessibility
|
|
11
|
+
- ui
|
|
12
|
+
- rules-catalog
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: ui.react.missing-alt-text
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.ui
|
|
26
|
+
severity: high
|
|
27
|
+
confidence: 0.87
|
|
28
|
+
tags:
|
|
29
|
+
- react
|
|
30
|
+
- accessibility
|
|
31
|
+
- ui
|
|
32
|
+
message:
|
|
33
|
+
title: Add alternative text to images
|
|
34
|
+
summary: "`${captures.issue.text}` renders without alternative text."
|
|
35
|
+
remediation:
|
|
36
|
+
summary: "Add a meaningful `alt` string, or set `alt=\"\"` only when the image is purely decorative and redundant."
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-index-as-key-in-dynamic-list
|
|
5
|
+
title: Avoid array index keys in dynamic lists
|
|
6
|
+
summary: Using the map index as a React key breaks reconciliation when lists reorder, filter, or insert items.
|
|
7
|
+
rationale: Stable keys help React preserve component state and reduce subtle UI bugs when list membership changes.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: ui.react.index-key-in-list
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.ui
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.78
|
|
27
|
+
tags:
|
|
28
|
+
- react
|
|
29
|
+
- ui
|
|
30
|
+
message:
|
|
31
|
+
title: Replace index keys with stable identifiers
|
|
32
|
+
summary: "`${captures.issue.text}` uses a list index as a React key."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Use a stable business identifier from each item, or derive a stable key when the dataset truly never mutates order.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-interactive-role-on-static-semantics
|
|
5
|
+
title: Keep interactive roles off static semantics
|
|
6
|
+
summary: "Headings, captions, and phrasing content should not pretend to be buttons or tabs without restructuring the markup."
|
|
7
|
+
rationale: Mixing document roles with widget roles confuses assistive technologies and usually signals a heading or label that should stay static while a real control sits nearby.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- accessibility
|
|
11
|
+
- ui
|
|
12
|
+
- rules-catalog
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: ui.react.semantic-static-with-interactive-role
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.ui
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.78
|
|
28
|
+
tags:
|
|
29
|
+
- react
|
|
30
|
+
- accessibility
|
|
31
|
+
- ui
|
|
32
|
+
message:
|
|
33
|
+
title: Avoid widget roles on semantic text elements
|
|
34
|
+
summary: "`${captures.issue.text}` assigns an interactive ARIA role to a semantic text element."
|
|
35
|
+
remediation:
|
|
36
|
+
summary: Move the interaction to a sibling `button` or link, keep the heading for structure only, and wire behaviour with IDs or aria-controls instead of overloading roles.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-invalid-anchor-href
|
|
5
|
+
title: Use real destinations for anchor elements
|
|
6
|
+
summary: "Links need a concrete `href` so navigation, keyboard activation, and assistive technologies behave predictably."
|
|
7
|
+
rationale: Placeholder anchors and `javascript:` URLs break expectations for links, harm accessibility, and often hide ad-hoc click handlers that should be buttons instead.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- accessibility
|
|
11
|
+
- ui
|
|
12
|
+
- rules-catalog
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: ui.react.anchor-with-invalid-href
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.ui
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.82
|
|
28
|
+
tags:
|
|
29
|
+
- react
|
|
30
|
+
- accessibility
|
|
31
|
+
- ui
|
|
32
|
+
message:
|
|
33
|
+
title: Give anchors a valid href
|
|
34
|
+
summary: "`${captures.issue.text}` is an anchor without a usable navigation target."
|
|
35
|
+
remediation:
|
|
36
|
+
summary: Point `href` at a real URL or in-page fragment, use a native button for actions, or add an explicit widget role with keyboard support when mimicking controls.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-keyboard-interaction-without-widget-role
|
|
5
|
+
title: Declare a widget role when mixing click and key handlers
|
|
6
|
+
summary: "Elements that handle both clicks and key events behave like custom controls and should advertise an appropriate ARIA role."
|
|
7
|
+
rationale: Assistive technologies cannot infer widget behaviour from events alone; an explicit role pairs keyboard support with the correct accessibility tree semantics.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- accessibility
|
|
11
|
+
- ui
|
|
12
|
+
- rules-catalog
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: ui.react.keyboard-interaction-without-widget-role
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.ui
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.8
|
|
28
|
+
tags:
|
|
29
|
+
- react
|
|
30
|
+
- accessibility
|
|
31
|
+
- ui
|
|
32
|
+
message:
|
|
33
|
+
title: Add a widget role for custom controls
|
|
34
|
+
summary: "`${captures.issue.text}` combines click and keyboard handlers without a widget role."
|
|
35
|
+
remediation:
|
|
36
|
+
summary: Set `role="button"` or another fitting widget role, ensure focusability, and mirror native control keyboard patterns.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-legacy-lifecycle
|
|
5
|
+
title: Avoid legacy React lifecycle methods
|
|
6
|
+
summary: Legacy class lifecycle hooks are brittle in strict mode and block migration toward modern React patterns.
|
|
7
|
+
rationale: Deprecated lifecycle methods are easy to misuse during async rendering and create noisy upgrade work across older React codebases.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: ui.react.legacy-lifecycle
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.ui
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.76
|
|
27
|
+
tags:
|
|
28
|
+
- react
|
|
29
|
+
- ui
|
|
30
|
+
message:
|
|
31
|
+
title: Replace legacy React lifecycle methods
|
|
32
|
+
summary: "`${captures.issue.text}` is a legacy React lifecycle method."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: "Prefer modern lifecycle alternatives, hooks, or an explicit `UNSAFE_` migration name only as a short-lived bridge."
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-missing-error-boundary
|
|
5
|
+
title: Add a route-level error boundary for Next.js App Router segments
|
|
6
|
+
summary: Next.js route segments should declare an error.tsx handler so async and client failures surface safely.
|
|
7
|
+
rationale: Without a segment error boundary, failures in pages or layouts can crash the entire subtree without recovery UI.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- next
|
|
11
|
+
- ui
|
|
12
|
+
- rules-catalog
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: project
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: ui.react.missing-error-boundary
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.framework
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.75
|
|
28
|
+
tags:
|
|
29
|
+
- react
|
|
30
|
+
- next
|
|
31
|
+
- ui
|
|
32
|
+
message:
|
|
33
|
+
title: Provide error.tsx for App Router segments
|
|
34
|
+
summary: "`${captures.issue.text}` is missing a sibling error boundary file for its route segment."
|
|
35
|
+
remediation:
|
|
36
|
+
summary: Add error.tsx next to the segment’s page or layout, or hoist shared handling to a parent segment.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-positive-tabindex
|
|
5
|
+
title: Avoid positive tabIndex values
|
|
6
|
+
summary: "Positive `tabIndex` values create a custom keyboard order that is fragile and usually less accessible than DOM order."
|
|
7
|
+
rationale: Manual focus sequencing is easy to break as layouts evolve and makes keyboard navigation less predictable for assistive-technology users.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- accessibility
|
|
11
|
+
- ui
|
|
12
|
+
- rules-catalog
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: ui.react.positive-tabindex
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.ui
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.85
|
|
28
|
+
tags:
|
|
29
|
+
- react
|
|
30
|
+
- accessibility
|
|
31
|
+
- ui
|
|
32
|
+
message:
|
|
33
|
+
title: Keep keyboard focus order natural
|
|
34
|
+
summary: "`${captures.issue.text}` uses a positive `tabIndex` value."
|
|
35
|
+
remediation:
|
|
36
|
+
summary: "Prefer source-order focus flow, use `tabIndex={0}` only when needed, and reserve negative values for programmatic focus targets."
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-static-element-with-synthetic-handlers
|
|
5
|
+
title: Avoid dangling pointer or key handlers on static elements
|
|
6
|
+
summary: "Non-interactive elements that listen for pointer or key events without a widget role usually hide custom interaction that needs explicit semantics."
|
|
7
|
+
rationale: Drag handles, menus, and listboxes should expose roles and focus models; otherwise assistive technologies treat the region as inert content.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- accessibility
|
|
11
|
+
- ui
|
|
12
|
+
- rules-catalog
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: ui.react.non-interactive-with-pointer-or-key-handler-without-role
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.ui
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.76
|
|
28
|
+
tags:
|
|
29
|
+
- react
|
|
30
|
+
- accessibility
|
|
31
|
+
- ui
|
|
32
|
+
message:
|
|
33
|
+
title: Model custom interaction explicitly
|
|
34
|
+
summary: "`${captures.issue.text}` listens for pointer or keyboard events without declaring a widget role."
|
|
35
|
+
remediation:
|
|
36
|
+
summary: Promote the element to a named widget with `role`, keyboard parity, and focus order, or attach handlers to a native interactive element instead.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-string-ref
|
|
5
|
+
title: Avoid legacy React string refs
|
|
6
|
+
summary: String refs rely on older React behavior that is harder to analyze and less reliable than callback or object refs.
|
|
7
|
+
rationale: Legacy refs obscure ownership, do not compose cleanly, and complicate future migrations away from class-heavy React patterns.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: ui.react.string-ref
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.ui
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.79
|
|
27
|
+
tags:
|
|
28
|
+
- react
|
|
29
|
+
- ui
|
|
30
|
+
message:
|
|
31
|
+
title: Replace string refs with modern refs
|
|
32
|
+
summary: "`${captures.issue.text}` uses a legacy React string ref."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: "Use `createRef`, `useRef`, or a callback ref so the reference stays explicit and type-safe."
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-uncontrolled-to-controlled-input
|
|
5
|
+
title: Avoid mixing controlled and uncontrolled input props
|
|
6
|
+
summary: Combining value with defaultValue leads to ambiguous ownership between React and the DOM.
|
|
7
|
+
rationale: Inputs should be either controlled via value or bootstrapped once via defaultValue, not both at once.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: function
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: ui.react.uncontrolled-controlled-input
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.ui
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.88
|
|
27
|
+
tags:
|
|
28
|
+
- react
|
|
29
|
+
- ui
|
|
30
|
+
message:
|
|
31
|
+
title: Pick either controlled or uncontrolled inputs
|
|
32
|
+
summary: "`${captures.issue.text}` sets both value and defaultValue."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Remove defaultValue when binding value, or drop value to stay fully uncontrolled.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.react.no-widget-role-without-tabindex
|
|
5
|
+
title: Pair interactive roles with focus behavior
|
|
6
|
+
summary: "Custom elements that declare widget roles need to enter the tab order unless they wrap a native focusable control."
|
|
7
|
+
rationale: ARIA widget roles without `tabIndex` are inert to keyboard users because the browser never sends them focus events.
|
|
8
|
+
tags:
|
|
9
|
+
- react
|
|
10
|
+
- accessibility
|
|
11
|
+
- ui
|
|
12
|
+
- rules-catalog
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: ui.react.widget-role-without-tabindex
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.ui
|
|
26
|
+
severity: high
|
|
27
|
+
confidence: 0.83
|
|
28
|
+
tags:
|
|
29
|
+
- react
|
|
30
|
+
- accessibility
|
|
31
|
+
- ui
|
|
32
|
+
message:
|
|
33
|
+
title: Make custom widgets focusable
|
|
34
|
+
summary: "`${captures.issue.text}` exposes a widget role without a non-negative `tabIndex`."
|
|
35
|
+
remediation:
|
|
36
|
+
summary: Add `tabIndex={0}` for simple widgets, or prefer native elements like `button` and `a` with real `href` values.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.security.ajv-insecure-configuration
|
|
5
|
+
title: Harden AJV compile options
|
|
6
|
+
summary: AJV should not compile schemas with allErrors true unless strict mode is enabled.
|
|
7
|
+
rationale: Missing strict-mode options historically enabled schema compilation DoS and unexpected coercion behavior.
|
|
8
|
+
tags:
|
|
9
|
+
- security
|
|
10
|
+
- validation
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: stable
|
|
13
|
+
appliesTo: block
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: security.ajv-insecure-configuration
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: security.misconfiguration
|
|
25
|
+
severity: medium
|
|
26
|
+
confidence: 0.82
|
|
27
|
+
tags:
|
|
28
|
+
- security
|
|
29
|
+
- validation
|
|
30
|
+
message:
|
|
31
|
+
title: "Tighten AJV options in ${captures.issue.text}"
|
|
32
|
+
summary: "${captures.issue.text} enables allErrors without strict, strictTypes, or strictSchema."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Enable AJV strict options appropriate to your major version and avoid compiling untrusted schemas with permissive settings.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.security.angular-dom-sanitizer-bypass-untrusted-input
|
|
5
|
+
title: Avoid trusting unsanitized Angular bypass sinks
|
|
6
|
+
summary: DomSanitizer bypass helpers should not receive route, storage, or request-derived values without validation.
|
|
7
|
+
rationale: Bypass helpers disable Angular templating protections and turn downstream sinks into XSS execution points.
|
|
8
|
+
tags:
|
|
9
|
+
- security
|
|
10
|
+
- angular
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: block
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: security.angular-dom-sanitizer-bypass-untrusted-input
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: security.output-encoding
|
|
25
|
+
severity: high
|
|
26
|
+
confidence: 0.81
|
|
27
|
+
tags:
|
|
28
|
+
- security
|
|
29
|
+
- angular
|
|
30
|
+
message:
|
|
31
|
+
title: Replace Angular sanitizer bypass with validated HTML or structured bindings
|
|
32
|
+
summary: "`${captures.issue.text}` trusts externally influenced markup."
|
|
33
|
+
remediation:
|
|
34
|
+
summary: >-
|
|
35
|
+
Keep sensitive markup on Angular-safe bindings or sanitize with a reviewed helper before calling bypassSecurityTrust helpers.
|