@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.
Files changed (204) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +256 -140
  3. package/catalog.yaml +985 -19
  4. package/package.json +7 -1
  5. package/rules/go/go.performance.no-regex-construction-in-loop.rule.yaml +33 -0
  6. package/rules/go/go.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
  7. package/rules/go/go.performance.no-unbounded-concurrency.rule.yaml +33 -0
  8. package/rules/go/go.security.echo-sensitive-binding-without-validation.rule.yaml +46 -0
  9. package/rules/go/go.security.echo-unsafe-multipart-upload.rule.yaml +45 -0
  10. package/rules/go/go.security.fiber-sensitive-binding-without-validation.rule.yaml +45 -0
  11. package/rules/go/go.security.fiber-unsafe-multipart-upload.rule.yaml +45 -0
  12. package/rules/go/go.security.gin-sensitive-binding-without-validation.rule.yaml +45 -0
  13. package/rules/go/go.security.gin-trust-all-proxies.rule.yaml +45 -0
  14. package/rules/go/go.security.gin-wildcard-cors-with-credentials.rule.yaml +47 -0
  15. package/rules/go/go.security.net-http-missing-timeouts.rule.yaml +45 -0
  16. package/rules/go/go.security.sensitive-data-egress.rule.yaml +46 -0
  17. package/rules/go/go.security.tar-path-traversal.rule.yaml +45 -0
  18. package/rules/go/go.security.template-unescaped-request-value.rule.yaml +45 -0
  19. package/rules/go/go.testing.real-network-in-unit-test.rule.yaml +33 -0
  20. package/rules/go/go.testing.t-skip-without-ticket-reference.rule.yaml +33 -0
  21. package/rules/go/go.testing.time-sleep-in-unit-test.rule.yaml +33 -0
  22. package/rules/java/java.performance.no-regex-construction-in-loop.rule.yaml +33 -0
  23. package/rules/java/java.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
  24. package/rules/java/java.performance.no-unbounded-concurrency.rule.yaml +33 -0
  25. package/rules/java/java.security.android-screenshot-exposure.rule.yaml +35 -0
  26. package/rules/java/java.security.android-world-readable-mode.rule.yaml +35 -0
  27. package/rules/java/java.security.jpa-concatenated-query.rule.yaml +47 -0
  28. package/rules/java/java.security.reflected-output-from-request.rule.yaml +35 -0
  29. package/rules/java/java.security.servlet-insecure-cookie.rule.yaml +35 -0
  30. package/rules/java/java.security.spring-actuator-health-details-always.rule.yaml +40 -0
  31. package/rules/java/java.security.spring-actuator-sensitive-exposure.rule.yaml +40 -0
  32. package/rules/java/java.security.spring-csrf-globally-disabled.rule.yaml +49 -0
  33. package/rules/java/java.security.spring-debug-exposure.rule.yaml +35 -0
  34. package/rules/java/java.security.spring-permit-all-default.rule.yaml +47 -0
  35. package/rules/java/java.security.spring-webmvc-unrestricted-data-binding.rule.yaml +47 -0
  36. package/rules/java/java.security.template-unescaped-user-output.rule.yaml +49 -0
  37. package/rules/java/java.testing.disabled-without-ticket-reference.rule.yaml +33 -0
  38. package/rules/java/java.testing.http-client-in-unit-test.rule.yaml +33 -0
  39. package/rules/java/java.testing.thread-sleep-in-unit-test.rule.yaml +33 -0
  40. package/rules/php/php.performance.no-regex-construction-in-loop.rule.yaml +33 -0
  41. package/rules/php/php.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
  42. package/rules/php/php.performance.no-unbounded-concurrency.rule.yaml +33 -0
  43. package/rules/php/php.security.insecure-cors-wildcard-with-credentials.rule.yaml +41 -0
  44. package/rules/php/php.security.insecure-mail-or-file-transport.rule.yaml +41 -0
  45. package/rules/php/php.security.insecure-session-or-cookie-config.rule.yaml +42 -0
  46. package/rules/php/php.security.laravel-sensitive-csrf-exclusion.rule.yaml +42 -0
  47. package/rules/php/php.security.laravel-unsafe-blade-output.rule.yaml +42 -0
  48. package/rules/php/php.security.laravel-unsafe-mass-assignment.rule.yaml +45 -0
  49. package/rules/php/php.security.sensitive-data-egress.rule.yaml +42 -0
  50. package/rules/php/php.security.symfony-csrf-disabled.rule.yaml +42 -0
  51. package/rules/php/php.security.symfony-debug-exposure.rule.yaml +44 -0
  52. package/rules/php/php.security.unsafe-file-upload-handling.rule.yaml +41 -0
  53. package/rules/php/php.security.wordpress-missing-nonce-or-capability.rule.yaml +42 -0
  54. package/rules/php/php.security.wordpress-unprepared-sql.rule.yaml +42 -0
  55. package/rules/php/php.testing.curl-in-unit-test.rule.yaml +33 -0
  56. package/rules/php/php.testing.mark-test-skipped-without-ticket-reference.rule.yaml +33 -0
  57. package/rules/php/php.testing.sleep-in-unit-test.rule.yaml +33 -0
  58. package/rules/python/py.performance.no-regex-construction-in-loop.rule.yaml +33 -0
  59. package/rules/python/py.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
  60. package/rules/python/py.performance.no-unbounded-concurrency.rule.yaml +33 -0
  61. package/rules/python/py.security.django-csrf-exempt-state-changing.rule.yaml +46 -0
  62. package/rules/python/py.security.django-missing-csrf-middleware.rule.yaml +47 -0
  63. package/rules/python/py.security.django-unsafe-production-settings.rule.yaml +47 -0
  64. package/rules/python/py.security.drf-allow-any-default.rule.yaml +46 -0
  65. package/rules/python/py.security.drf-allow-any-unsafe-method.rule.yaml +46 -0
  66. package/rules/python/py.security.fastapi-insecure-cors.rule.yaml +43 -0
  67. package/rules/python/py.security.flask-missing-upload-body-limit.rule.yaml +44 -0
  68. package/rules/python/py.security.flask-unsafe-html-output.rule.yaml +44 -0
  69. package/rules/python/py.security.flask-unsafe-upload-filename.rule.yaml +44 -0
  70. package/rules/python/py.testing.pytest-skip-without-ticket-reference.rule.yaml +33 -0
  71. package/rules/python/py.testing.real-network-in-unit-test.rule.yaml +33 -0
  72. package/rules/python/py.testing.time-sleep-in-unit-test.rule.yaml +33 -0
  73. package/rules/ruby/ruby.performance.no-regex-construction-in-loop.rule.yaml +33 -0
  74. package/rules/ruby/ruby.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
  75. package/rules/ruby/ruby.performance.no-unbounded-concurrency.rule.yaml +33 -0
  76. package/rules/ruby/ruby.security.rails-csrf-disabled.rule.yaml +45 -0
  77. package/rules/ruby/ruby.security.rails-detailed-exceptions-enabled.rule.yaml +44 -0
  78. package/rules/ruby/ruby.security.rails-open-redirect.rule.yaml +45 -0
  79. package/rules/ruby/ruby.security.rails-unsafe-html-output.rule.yaml +46 -0
  80. package/rules/ruby/ruby.security.rails-unsafe-render.rule.yaml +45 -0
  81. package/rules/ruby/ruby.security.rails-unsafe-session-or-cookie-store.rule.yaml +45 -0
  82. package/rules/ruby/ruby.security.rails-unsafe-strong-parameters.rule.yaml +46 -0
  83. package/rules/ruby/ruby.security.sensitive-data-egress.rule.yaml +45 -0
  84. package/rules/ruby/ruby.security.sidekiq-web-unauthenticated-mount.rule.yaml +45 -0
  85. package/rules/ruby/ruby.testing.focused-example.rule.yaml +33 -0
  86. package/rules/ruby/ruby.testing.pending-without-ticket-reference.rule.yaml +33 -0
  87. package/rules/ruby/ruby.testing.real-network-in-unit-test.rule.yaml +33 -0
  88. package/rules/ruby/ruby.testing.skip-without-ticket-reference.rule.yaml +33 -0
  89. package/rules/ruby/ruby.testing.sleep-in-unit-test.rule.yaml +33 -0
  90. package/rules/rust/rust.performance.no-regex-construction-in-loop.rule.yaml +33 -0
  91. package/rules/rust/rust.performance.no-sync-fs-in-request-path.rule.yaml +33 -0
  92. package/rules/rust/rust.performance.no-unbounded-concurrency.rule.yaml +33 -0
  93. package/rules/rust/rust.security.actix-wildcard-cors-with-credentials.rule.yaml +47 -0
  94. package/rules/rust/rust.security.axum-body-limit-disabled.rule.yaml +45 -0
  95. package/rules/rust/rust.security.axum-insecure-cors-with-credentials.rule.yaml +47 -0
  96. package/rules/rust/rust.security.rocket-panic-prone-request-handler.rule.yaml +45 -0
  97. package/rules/rust/rust.security.rocket-unsafe-template-output.rule.yaml +47 -0
  98. package/rules/rust/rust.security.sqlx-diesel-raw-interpolated-query.rule.yaml +47 -0
  99. package/rules/rust/rust.security.template-unescaped-request-value.rule.yaml +47 -0
  100. package/rules/rust/rust.security.warp-blocking-or-panic-in-async-handler.rule.yaml +45 -0
  101. package/rules/rust/rust.testing.ignore-without-ticket-reference.rule.yaml +33 -0
  102. package/rules/rust/rust.testing.real-network-in-unit-test.rule.yaml +33 -0
  103. package/rules/rust/rust.testing.thread-sleep-in-unit-test.rule.yaml +33 -0
  104. package/rules/shared/security.archive-path-traversal.rule.yaml +41 -0
  105. package/rules/shared/security.external-file-upload.rule.yaml +40 -0
  106. package/rules/shared/security.permissive-file-permissions.rule.yaml +40 -0
  107. package/rules/shared/security.sensitive-data-egress.rule.yaml +36 -0
  108. package/rules/typescript/ts.correctness.assignment-in-condition.rule.yaml +36 -0
  109. package/rules/typescript/ts.correctness.assignment-to-import-binding.rule.yaml +36 -0
  110. package/rules/typescript/ts.correctness.async-promise-executor.rule.yaml +36 -0
  111. package/rules/typescript/ts.correctness.duplicate-function-parameter.rule.yaml +36 -0
  112. package/rules/typescript/ts.correctness.duplicate-import-source.rule.yaml +36 -0
  113. package/rules/typescript/ts.correctness.duplicate-object-key.rule.yaml +36 -0
  114. package/rules/typescript/ts.correctness.duplicate-switch-case.rule.yaml +36 -0
  115. package/rules/typescript/ts.correctness.empty-block-statement.rule.yaml +35 -0
  116. package/rules/typescript/ts.correctness.identical-comparison-operands.rule.yaml +36 -0
  117. package/rules/typescript/ts.correctness.reassign-catch-binding.rule.yaml +35 -0
  118. package/rules/typescript/ts.correctness.regexp-pattern-unusual-control-character.rule.yaml +35 -0
  119. package/rules/typescript/ts.correctness.self-assignment.rule.yaml +36 -0
  120. package/rules/typescript/ts.next.server-action-missing-local-auth.rule.yaml +35 -0
  121. package/rules/typescript/ts.performance.no-array-spread-in-hot-loop.rule.yaml +32 -0
  122. package/rules/typescript/ts.performance.no-cache-miss-from-unstable-key.rule.yaml +32 -0
  123. package/rules/typescript/ts.performance.no-expensive-sort-in-render-path.rule.yaml +32 -0
  124. package/rules/typescript/ts.performance.no-json-parse-stringify-clone.rule.yaml +32 -0
  125. package/rules/typescript/ts.performance.no-large-object-spread-in-loop.rule.yaml +32 -0
  126. package/rules/typescript/ts.performance.no-n-plus-one-await-in-map.rule.yaml +32 -0
  127. package/rules/typescript/ts.performance.no-redundant-network-fetch.rule.yaml +32 -0
  128. package/rules/typescript/ts.performance.no-regex-construction-in-loop.rule.yaml +32 -0
  129. package/rules/typescript/ts.performance.no-sync-fs-in-request-path.rule.yaml +32 -0
  130. package/rules/typescript/ts.performance.no-unbounded-concurrency.rule.yaml +32 -0
  131. package/rules/typescript/ts.quality.no-ambiguous-abbreviations.rule.yaml +27 -0
  132. package/rules/typescript/ts.quality.no-barrel-file-cycle.rule.yaml +27 -0
  133. package/rules/typescript/ts.quality.no-boolean-parameter-trap.rule.yaml +27 -0
  134. package/rules/typescript/ts.quality.no-dead-export.rule.yaml +27 -0
  135. package/rules/typescript/ts.quality.no-hidden-side-effect-import.rule.yaml +27 -0
  136. package/rules/typescript/ts.quality.no-inconsistent-error-shape.rule.yaml +27 -0
  137. package/rules/typescript/ts.quality.no-mixed-abstraction-level.rule.yaml +27 -0
  138. package/rules/typescript/ts.quality.no-primitive-obsession-in-domain-model.rule.yaml +27 -0
  139. package/rules/typescript/ts.quality.no-temporal-coupling.rule.yaml +27 -0
  140. package/rules/typescript/ts.quality.no-wide-public-surface.rule.yaml +27 -0
  141. package/rules/typescript/ts.react.no-accessibility-label-missing.rule.yaml +36 -0
  142. package/rules/typescript/ts.react.no-activedescendant-on-non-focusable-host.rule.yaml +36 -0
  143. package/rules/typescript/ts.react.no-click-without-keyboard-handler.rule.yaml +36 -0
  144. package/rules/typescript/ts.react.no-deprecated-create-factory.rule.yaml +34 -0
  145. package/rules/typescript/ts.react.no-deprecated-react-dom-root-api.rule.yaml +34 -0
  146. package/rules/typescript/ts.react.no-derived-state-from-props.rule.yaml +34 -0
  147. package/rules/typescript/ts.react.no-effect-fetch-without-cancellation.rule.yaml +35 -0
  148. package/rules/typescript/ts.react.no-find-dom-node.rule.yaml +34 -0
  149. package/rules/typescript/ts.react.no-img-missing-alt-text.rule.yaml +36 -0
  150. package/rules/typescript/ts.react.no-index-as-key-in-dynamic-list.rule.yaml +34 -0
  151. package/rules/typescript/ts.react.no-interactive-role-on-static-semantics.rule.yaml +36 -0
  152. package/rules/typescript/ts.react.no-invalid-anchor-href.rule.yaml +36 -0
  153. package/rules/typescript/ts.react.no-keyboard-interaction-without-widget-role.rule.yaml +36 -0
  154. package/rules/typescript/ts.react.no-legacy-lifecycle.rule.yaml +34 -0
  155. package/rules/typescript/ts.react.no-missing-error-boundary.rule.yaml +36 -0
  156. package/rules/typescript/ts.react.no-positive-tabindex.rule.yaml +36 -0
  157. package/rules/typescript/ts.react.no-static-element-with-synthetic-handlers.rule.yaml +36 -0
  158. package/rules/typescript/ts.react.no-string-ref.rule.yaml +34 -0
  159. package/rules/typescript/ts.react.no-uncontrolled-to-controlled-input.rule.yaml +34 -0
  160. package/rules/typescript/ts.react.no-widget-role-without-tabindex.rule.yaml +36 -0
  161. package/rules/typescript/ts.security.ajv-insecure-configuration.rule.yaml +34 -0
  162. package/rules/typescript/ts.security.angular-dom-sanitizer-bypass-untrusted-input.rule.yaml +35 -0
  163. package/rules/typescript/ts.security.apollo-server-csrf-disabled.rule.yaml +36 -0
  164. package/rules/typescript/ts.security.apollo-server-graphql-dev-tooling-exposure.rule.yaml +36 -0
  165. package/rules/typescript/ts.security.apollo-server-introspection-exposure.rule.yaml +35 -0
  166. package/rules/typescript/ts.security.apollo-server-missing-query-limits.rule.yaml +35 -0
  167. package/rules/typescript/ts.security.astro-vite-public-secret-define.rule.yaml +39 -0
  168. package/rules/typescript/ts.security.debug-statement-in-source.rule.yaml +36 -0
  169. package/rules/typescript/ts.security.electron-dangerous-webpreferences.rule.yaml +35 -0
  170. package/rules/typescript/ts.security.electron-insecure-local-state.rule.yaml +35 -0
  171. package/rules/typescript/ts.security.electron-missing-ipc-origin-check.rule.yaml +35 -0
  172. package/rules/typescript/ts.security.electron-shell-open-external-unvalidated.rule.yaml +35 -0
  173. package/rules/typescript/ts.security.express-error-handler-information-disclosure.rule.yaml +35 -0
  174. package/rules/typescript/ts.security.express-static-dotfiles-allow.rule.yaml +35 -0
  175. package/rules/typescript/ts.security.express-unbounded-body-parser.rule.yaml +34 -0
  176. package/rules/typescript/ts.security.express-user-controlled-static-mount.rule.yaml +35 -0
  177. package/rules/typescript/ts.security.fastify-excessive-body-limit.rule.yaml +34 -0
  178. package/rules/typescript/ts.security.fastify-public-bind-without-trust-proxy.rule.yaml +38 -0
  179. package/rules/typescript/ts.security.graphql-upload-without-csrf-guard.rule.yaml +36 -0
  180. package/rules/typescript/ts.security.iframe-missing-sandbox-attribute.rule.yaml +35 -0
  181. package/rules/typescript/ts.security.insecure-content-security-policy-literal.rule.yaml +35 -0
  182. package/rules/typescript/ts.security.insecure-helmet-hardening-options.rule.yaml +36 -0
  183. package/rules/typescript/ts.security.jwt-insecure-signing-algorithm.rule.yaml +35 -0
  184. package/rules/typescript/ts.security.legacy-buffer-constructor.rule.yaml +35 -0
  185. package/rules/typescript/ts.security.log-injection.rule.yaml +36 -0
  186. package/rules/typescript/ts.security.nestjs-helmet-after-route-mount.rule.yaml +34 -0
  187. package/rules/typescript/ts.security.nestjs-missing-global-validation-pipe.rule.yaml +35 -0
  188. package/rules/typescript/ts.security.nestjs-skip-throttle-sensitive-route.rule.yaml +35 -0
  189. package/rules/typescript/ts.security.nestjs-validation-pipe-without-whitelist.rule.yaml +36 -0
  190. package/rules/typescript/ts.security.nuxt-public-runtime-secret.rule.yaml +38 -0
  191. package/rules/typescript/ts.security.open-redirect.rule.yaml +2 -0
  192. package/rules/typescript/ts.security.request-driven-array-index-access.rule.yaml +33 -0
  193. package/rules/typescript/ts.security.sensitive-data-egress.rule.yaml +1 -0
  194. package/rules/typescript/ts.security.ssrf.rule.yaml +1 -0
  195. package/rules/typescript/ts.security.unsafe-dompurify-version.rule.yaml +36 -0
  196. package/rules/typescript/ts.security.unsafe-marked-version.rule.yaml +36 -0
  197. package/rules/typescript/ts.security.xml-parse-string-with-untrusted-input.rule.yaml +35 -0
  198. package/rules/typescript/ts.testing.no-flaky-timer-test.rule.yaml +38 -0
  199. package/rules/typescript/ts.testing.no-focused-test.rule.yaml +34 -0
  200. package/rules/typescript/ts.testing.no-missing-edge-case-tests.rule.yaml +35 -0
  201. package/rules/typescript/ts.testing.no-network-call-in-unit-test.rule.yaml +38 -0
  202. package/rules/typescript/ts.testing.no-skipped-test-without-ticket.rule.yaml +34 -0
  203. package/rules/typescript/ts.testing.no-snapshot-without-intent.rule.yaml +34 -0
  204. 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.