@critiq/rules 0.0.1
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/README.md +169 -0
- package/catalog.yaml +599 -0
- package/package.json +21 -0
- package/rules/shared/security.insecure-http-transport.rule.yaml +42 -0
- package/rules/shared/security.no-command-execution-with-request-input.rule.yaml +42 -0
- package/rules/shared/security.no-hardcoded-credentials.rule.yaml +42 -0
- package/rules/shared/security.no-request-path-file-read.rule.yaml +42 -0
- package/rules/shared/security.no-sensitive-data-in-logs-and-telemetry.rule.yaml +44 -0
- package/rules/shared/security.no-sql-interpolation.rule.yaml +42 -0
- package/rules/shared/security.tls-verification-disabled.rule.yaml +42 -0
- package/rules/shared/security.unsafe-deserialization.rule.yaml +41 -0
- package/rules/shared/security.weak-hash-algorithm.rule.yaml +41 -0
- package/rules/typescript/ts.config.no-process-env-outside-config.rule.yaml +37 -0
- package/rules/typescript/ts.correctness.blocking-call-in-async-flow.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.constant-condition.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.implicit-undefined-return.rule.yaml +34 -0
- package/rules/typescript/ts.correctness.incorrect-boolean-logic.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.missing-await-on-async-call.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.missing-default-dispatch.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.missing-timeout-on-external-call.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.nested-property-access-without-check.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.off-by-one-loop-boundary.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.optional-value-without-fallback.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.possible-null-dereference.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.shared-state-race.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.unchecked-map-key-access.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.unhandled-async-error.rule.yaml +35 -0
- package/rules/typescript/ts.correctness.unreachable-statement.rule.yaml +40 -0
- package/rules/typescript/ts.logging.no-console-error.rule.yaml +34 -0
- package/rules/typescript/ts.logging.no-console-log.rule.yaml +34 -0
- package/rules/typescript/ts.next.no-server-client-boundary-leaks.rule.yaml +36 -0
- package/rules/typescript/ts.performance.inefficient-data-structure-usage.rule.yaml +35 -0
- package/rules/typescript/ts.performance.large-payload-without-streaming.rule.yaml +35 -0
- package/rules/typescript/ts.performance.missing-batch-operations.rule.yaml +35 -0
- package/rules/typescript/ts.performance.nested-loops-hot-path.rule.yaml +35 -0
- package/rules/typescript/ts.performance.repeated-expensive-computation.rule.yaml +35 -0
- package/rules/typescript/ts.performance.repeated-io-in-loop.rule.yaml +35 -0
- package/rules/typescript/ts.performance.retained-large-object.rule.yaml +35 -0
- package/rules/typescript/ts.performance.sequential-async-calls.rule.yaml +35 -0
- package/rules/typescript/ts.performance.unbounded-growth-memory-leak.rule.yaml +35 -0
- package/rules/typescript/ts.performance.unnecessary-rerenders-from-state-misuse.rule.yaml +35 -0
- package/rules/typescript/ts.quality.deep-nesting.rule.yaml +35 -0
- package/rules/typescript/ts.quality.duplicate-code-block.rule.yaml +35 -0
- package/rules/typescript/ts.quality.function-too-large-or-complex.rule.yaml +35 -0
- package/rules/typescript/ts.quality.hardcoded-configuration-values.rule.yaml +35 -0
- package/rules/typescript/ts.quality.logic-change-without-test-updates.rule.yaml +36 -0
- package/rules/typescript/ts.quality.magic-numbers-or-strings.rule.yaml +35 -0
- package/rules/typescript/ts.quality.missing-error-context.rule.yaml +35 -0
- package/rules/typescript/ts.quality.missing-tests-for-critical-logic.rule.yaml +35 -0
- package/rules/typescript/ts.quality.swallowed-error.rule.yaml +35 -0
- package/rules/typescript/ts.quality.tight-module-coupling.rule.yaml +35 -0
- package/rules/typescript/ts.random.no-math-random-in-core.rule.yaml +37 -0
- package/rules/typescript/ts.react.no-cascaded-effect-fetches.rule.yaml +37 -0
- package/rules/typescript/ts.runtime.no-debugger-statement.rule.yaml +29 -0
- package/rules/typescript/ts.security.bind-to-all-interfaces.rule.yaml +36 -0
- package/rules/typescript/ts.security.browser-token-storage.rule.yaml +37 -0
- package/rules/typescript/ts.security.dangerous-insert-html.rule.yaml +36 -0
- package/rules/typescript/ts.security.dangerously-set-inner-html.rule.yaml +38 -0
- package/rules/typescript/ts.security.datadog-browser-track-user-interactions.rule.yaml +37 -0
- package/rules/typescript/ts.security.debug-mode-enabled.rule.yaml +38 -0
- package/rules/typescript/ts.security.dynamodb-query-injection.rule.yaml +36 -0
- package/rules/typescript/ts.security.exposed-directory-listing.rule.yaml +37 -0
- package/rules/typescript/ts.security.express-cookie-missing-http-only.rule.yaml +36 -0
- package/rules/typescript/ts.security.express-default-cookie-config.rule.yaml +39 -0
- package/rules/typescript/ts.security.express-default-session-config.rule.yaml +37 -0
- package/rules/typescript/ts.security.express-insecure-cookie.rule.yaml +36 -0
- package/rules/typescript/ts.security.express-missing-helmet.rule.yaml +37 -0
- package/rules/typescript/ts.security.express-nosql-injection.rule.yaml +36 -0
- package/rules/typescript/ts.security.express-permissive-cookie-config.rule.yaml +38 -0
- package/rules/typescript/ts.security.express-reduce-fingerprint.rule.yaml +37 -0
- package/rules/typescript/ts.security.express-static-assets-after-session.rule.yaml +37 -0
- package/rules/typescript/ts.security.external-file-upload.rule.yaml +36 -0
- package/rules/typescript/ts.security.file-generation.rule.yaml +36 -0
- package/rules/typescript/ts.security.format-string-using-user-input.rule.yaml +36 -0
- package/rules/typescript/ts.security.frontend-only-authorization.rule.yaml +35 -0
- package/rules/typescript/ts.security.handlebars-no-escape.rule.yaml +38 -0
- package/rules/typescript/ts.security.hardcoded-auth-secret.rule.yaml +37 -0
- package/rules/typescript/ts.security.import-using-user-input.rule.yaml +36 -0
- package/rules/typescript/ts.security.information-leakage.rule.yaml +38 -0
- package/rules/typescript/ts.security.insecure-allow-origin.rule.yaml +36 -0
- package/rules/typescript/ts.security.insecure-auth-cookie-flags.rule.yaml +37 -0
- package/rules/typescript/ts.security.insecure-password-hash-configuration.rule.yaml +37 -0
- package/rules/typescript/ts.security.insecure-websocket-transport.rule.yaml +37 -0
- package/rules/typescript/ts.security.insufficiently-random-values.rule.yaml +36 -0
- package/rules/typescript/ts.security.jwt-not-revoked.rule.yaml +37 -0
- package/rules/typescript/ts.security.jwt-sensitive-claims.rule.yaml +37 -0
- package/rules/typescript/ts.security.manual-html-sanitization.rule.yaml +37 -0
- package/rules/typescript/ts.security.missing-authorization-before-sensitive-action.rule.yaml +35 -0
- package/rules/typescript/ts.security.missing-integrity-check.rule.yaml +36 -0
- package/rules/typescript/ts.security.missing-message-origin-check.rule.yaml +36 -0
- package/rules/typescript/ts.security.missing-ownership-validation.rule.yaml +35 -0
- package/rules/typescript/ts.security.missing-request-timeout-or-retry.rule.yaml +35 -0
- package/rules/typescript/ts.security.no-dynamic-execution.rule.yaml +34 -0
- package/rules/typescript/ts.security.no-innerhtml-assignment.rule.yaml +36 -0
- package/rules/typescript/ts.security.non-literal-fs-filename.rule.yaml +36 -0
- package/rules/typescript/ts.security.observable-timing-discrepancy.rule.yaml +37 -0
- package/rules/typescript/ts.security.open-redirect.rule.yaml +37 -0
- package/rules/typescript/ts.security.permissive-allow-origin.rule.yaml +36 -0
- package/rules/typescript/ts.security.permissive-file-permissions.rule.yaml +37 -0
- package/rules/typescript/ts.security.postmessage-wildcard-origin.rule.yaml +36 -0
- package/rules/typescript/ts.security.predictable-token-generation.rule.yaml +36 -0
- package/rules/typescript/ts.security.raw-html-using-user-input.rule.yaml +37 -0
- package/rules/typescript/ts.security.sensitive-data-egress.rule.yaml +36 -0
- package/rules/typescript/ts.security.sensitive-data-in-exception.rule.yaml +37 -0
- package/rules/typescript/ts.security.sensitive-data-written-to-file.rule.yaml +37 -0
- package/rules/typescript/ts.security.ssrf.rule.yaml +34 -0
- package/rules/typescript/ts.security.token-or-session-not-validated.rule.yaml +35 -0
- package/rules/typescript/ts.security.ui-redress.rule.yaml +37 -0
- package/rules/typescript/ts.security.unsanitized-http-response.rule.yaml +36 -0
- package/rules/typescript/ts.security.unvalidated-external-input.rule.yaml +35 -0
- package/rules/typescript/ts.security.user-controlled-sendfile.rule.yaml +36 -0
- package/rules/typescript/ts.security.user-controlled-view-render.rule.yaml +36 -0
- package/rules/typescript/ts.security.weak-cipher-or-mode.rule.yaml +35 -0
- package/rules/typescript/ts.security.weak-key-strength.rule.yaml +36 -0
- package/rules/typescript/ts.security.weak-tls-version.rule.yaml +36 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +5 -0
- package/src/index.js.map +1 -0
- package/src/lib/rules-package.d.ts +3 -0
- package/src/lib/rules-package.js +16 -0
- package/src/lib/rules-package.js.map +1 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.correctness.optional-value-without-fallback
|
|
5
|
+
title: Optional value used without fallback
|
|
6
|
+
summary: Optional values should be normalized before arithmetic, concatenation, or other direct use.
|
|
7
|
+
rationale: Using maybe-undefined values in ordinary expressions bakes uncertainty into the result and can fail or stringify unexpectedly at runtime.
|
|
8
|
+
tags:
|
|
9
|
+
- correctness
|
|
10
|
+
- "null"
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-cor-004
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: block
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: data-flow.optional-value-without-fallback
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.null
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.8
|
|
28
|
+
tags:
|
|
29
|
+
- correctness
|
|
30
|
+
- "null"
|
|
31
|
+
message:
|
|
32
|
+
title: Normalize optional values before using them
|
|
33
|
+
summary: "`${captures.issue.text}` uses a maybe-missing value without a fallback."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Add `??`, `||`, or an explicit guard so the expression always receives a defined value.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.correctness.possible-null-dereference
|
|
5
|
+
title: Possible null or undefined dereference
|
|
6
|
+
summary: Nullable values should be guarded before property access or invocation.
|
|
7
|
+
rationale: Dereferencing a value that can still be null or undefined causes avoidable runtime failures on common unhappy paths.
|
|
8
|
+
tags:
|
|
9
|
+
- correctness
|
|
10
|
+
- "null"
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-cor-001
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: block
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: data-flow.possible-null-dereference
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.null
|
|
26
|
+
severity: high
|
|
27
|
+
confidence: 0.85
|
|
28
|
+
tags:
|
|
29
|
+
- correctness
|
|
30
|
+
- "null"
|
|
31
|
+
message:
|
|
32
|
+
title: Guard nullable values before dereferencing
|
|
33
|
+
summary: "`${captures.issue.text}` dereferences a value that may still be null or undefined."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Add an explicit guard, use optional chaining deliberately, or provide a fallback before dereferencing the value.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.correctness.shared-state-race
|
|
5
|
+
title: Race condition on shared state
|
|
6
|
+
summary: Async functions that mutate shared state after an await boundary should be reviewed for races.
|
|
7
|
+
rationale: Mutating outer-scope or instance state after suspension can interleave with concurrent callers and corrupt shared state.
|
|
8
|
+
tags:
|
|
9
|
+
- correctness
|
|
10
|
+
- concurrency
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-cor-015
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: concurrency.shared-state-race
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.concurrency
|
|
26
|
+
severity: high
|
|
27
|
+
confidence: 0.65
|
|
28
|
+
tags:
|
|
29
|
+
- correctness
|
|
30
|
+
- concurrency
|
|
31
|
+
message:
|
|
32
|
+
title: Review shared-state mutation across await boundaries
|
|
33
|
+
summary: "`${captures.issue.text}` mutates shared state after async suspension."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Isolate the state per request, serialize access, or move the mutation before the await boundary.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.correctness.unchecked-map-key-access
|
|
5
|
+
title: Unchecked map or dictionary key access
|
|
6
|
+
summary: Lookups should verify key presence before reading from maps or keyed objects.
|
|
7
|
+
rationale: Missing-key reads often flow undefined deeper into the function and fail far away from the original lookup.
|
|
8
|
+
tags:
|
|
9
|
+
- correctness
|
|
10
|
+
- data-access
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-cor-003
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: block
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: data-flow.unchecked-map-key-access
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.data-access
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.8
|
|
28
|
+
tags:
|
|
29
|
+
- correctness
|
|
30
|
+
- data-access
|
|
31
|
+
message:
|
|
32
|
+
title: Guard keyed lookups before reading values
|
|
33
|
+
summary: "`${captures.issue.text}` reads from a keyed collection without checking whether the key is present."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Check `has`, `in`, or `Object.hasOwn` before reading, or provide a safe default when the key is missing.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.correctness.unhandled-async-error
|
|
5
|
+
title: Unhandled promise rejection or async error
|
|
6
|
+
summary: Promise chains started in a function should terminate with explicit rejection handling.
|
|
7
|
+
rationale: Floating `.then(...)` chains without a terminal catch can reject outside the intended error-handling path and make failures hard to trace.
|
|
8
|
+
tags:
|
|
9
|
+
- correctness
|
|
10
|
+
- async
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-cor-012
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: async.unhandled-async-error
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: correctness.async
|
|
26
|
+
severity: high
|
|
27
|
+
confidence: 0.85
|
|
28
|
+
tags:
|
|
29
|
+
- correctness
|
|
30
|
+
- async
|
|
31
|
+
message:
|
|
32
|
+
title: Handle async rejections explicitly
|
|
33
|
+
summary: Review `${captures.issue.text}` and terminate the promise chain with rejection handling.
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Await the operation in a try/catch block or attach a terminal `.catch(...)` handler to the promise chain.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.correctness.unreachable-statement
|
|
5
|
+
title: Unreachable code after return or throw
|
|
6
|
+
summary: Statements after terminal exits should be removed or moved before the exit.
|
|
7
|
+
rationale: Dead statements obscure intent and often indicate a refactor that left stale logic behind.
|
|
8
|
+
tags:
|
|
9
|
+
- correctness
|
|
10
|
+
- control-flow
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-cor-010
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: block
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: control-flow.unreachable-statement
|
|
22
|
+
bind: issue
|
|
23
|
+
where:
|
|
24
|
+
- path: reason
|
|
25
|
+
in:
|
|
26
|
+
- after-return
|
|
27
|
+
- after-throw
|
|
28
|
+
emit:
|
|
29
|
+
finding:
|
|
30
|
+
category: correctness.control-flow
|
|
31
|
+
severity: low
|
|
32
|
+
confidence: 0.95
|
|
33
|
+
tags:
|
|
34
|
+
- correctness
|
|
35
|
+
- control-flow
|
|
36
|
+
message:
|
|
37
|
+
title: Remove unreachable statement
|
|
38
|
+
summary: "`${captures.issue.text}` cannot execute because control flow already terminated."
|
|
39
|
+
remediation:
|
|
40
|
+
summary: Delete the dead statement or move it before the return or throw.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.logging.no-console-error
|
|
5
|
+
title: Avoid console.error
|
|
6
|
+
summary: Route error logs through the project logger.
|
|
7
|
+
rationale: Console error calls bypass the shared logging pipeline.
|
|
8
|
+
tags:
|
|
9
|
+
- logging
|
|
10
|
+
- rules-catalog
|
|
11
|
+
scope:
|
|
12
|
+
languages:
|
|
13
|
+
- typescript
|
|
14
|
+
match:
|
|
15
|
+
node:
|
|
16
|
+
kind: CallExpression
|
|
17
|
+
bind: call
|
|
18
|
+
where:
|
|
19
|
+
- path: callee.object.text
|
|
20
|
+
equals: console
|
|
21
|
+
- path: callee.property.text
|
|
22
|
+
equals: error
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: maintainability
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: high
|
|
28
|
+
tags:
|
|
29
|
+
- logging
|
|
30
|
+
message:
|
|
31
|
+
title: Avoid `${captures.call.text}`
|
|
32
|
+
summary: Route `${captures.call.text}` through the project logger.
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Replace `${captures.call.text}` with the logger.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.logging.no-console-log
|
|
5
|
+
title: Avoid console.log
|
|
6
|
+
summary: Use the project logger instead of console.log.
|
|
7
|
+
rationale: Console logging bypasses the shared logger pipeline.
|
|
8
|
+
tags:
|
|
9
|
+
- logging
|
|
10
|
+
- rules-catalog
|
|
11
|
+
scope:
|
|
12
|
+
languages:
|
|
13
|
+
- typescript
|
|
14
|
+
match:
|
|
15
|
+
node:
|
|
16
|
+
kind: CallExpression
|
|
17
|
+
bind: call
|
|
18
|
+
where:
|
|
19
|
+
- path: callee.object.text
|
|
20
|
+
equals: console
|
|
21
|
+
- path: callee.property.text
|
|
22
|
+
equals: log
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: maintainability
|
|
26
|
+
severity: low
|
|
27
|
+
confidence: high
|
|
28
|
+
tags:
|
|
29
|
+
- logging
|
|
30
|
+
message:
|
|
31
|
+
title: Avoid `${captures.call.text}`
|
|
32
|
+
summary: Use the project logger instead of `${captures.call.text}`.
|
|
33
|
+
remediation:
|
|
34
|
+
summary: Replace `${captures.call.text}` with the logger.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.next.no-server-client-boundary-leaks
|
|
5
|
+
title: Avoid server/client boundary leaks in Next.js
|
|
6
|
+
summary: Server components should not use browser-only APIs or client-only hooks without an explicit client boundary.
|
|
7
|
+
rationale: Browser globals and client-only hooks break server rendering boundaries and make component intent unclear.
|
|
8
|
+
tags:
|
|
9
|
+
- next
|
|
10
|
+
- react
|
|
11
|
+
- rules-catalog
|
|
12
|
+
stability: experimental
|
|
13
|
+
appliesTo: file
|
|
14
|
+
scope:
|
|
15
|
+
languages:
|
|
16
|
+
- typescript
|
|
17
|
+
- javascript
|
|
18
|
+
match:
|
|
19
|
+
fact:
|
|
20
|
+
kind: framework.next-server-client-boundary-leak
|
|
21
|
+
bind: issue
|
|
22
|
+
emit:
|
|
23
|
+
finding:
|
|
24
|
+
category: correctness.framework
|
|
25
|
+
severity: high
|
|
26
|
+
confidence: 0.8
|
|
27
|
+
tags:
|
|
28
|
+
- next
|
|
29
|
+
- react
|
|
30
|
+
- correctness
|
|
31
|
+
message:
|
|
32
|
+
title: Add a client boundary before using browser-only APIs
|
|
33
|
+
summary: "`${captures.issue.text}` uses a browser-only API or client-only hook in a server file."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Add `'use client'` or move the code into a client component or hook.
|
|
36
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.inefficient-data-structure-usage
|
|
5
|
+
title: Inefficient data structure usage
|
|
6
|
+
summary: Linear membership checks or key projections should be reviewed for more suitable lookup structures.
|
|
7
|
+
rationale: Repeated array scans or `Object.keys(...).includes(...)` patterns are often avoidable with `Set`, `Map`, or direct key checks.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- algorithmic
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-034
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: block
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.inefficient-data-structure-usage
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.algorithmic
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.8
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- algorithmic
|
|
31
|
+
message:
|
|
32
|
+
title: Prefer a more efficient lookup structure
|
|
33
|
+
summary: "`${captures.issue.text}` uses a linear lookup where a dedicated structure would be cheaper."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Replace repeated array scans with `Set` or `Map`, or use direct key existence checks instead of projecting keys first.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.large-payload-without-streaming
|
|
5
|
+
title: Large payload processing without streaming
|
|
6
|
+
summary: Whole-payload reads of likely large content should be reviewed for streaming alternatives.
|
|
7
|
+
rationale: Pulling large payloads into memory increases latency and memory pressure compared with streaming or chunked processing.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- memory-io
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-037
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: block
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.large-payload-without-streaming
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.memory-io
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.8
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- memory-io
|
|
31
|
+
message:
|
|
32
|
+
title: Stream large payloads when practical
|
|
33
|
+
summary: "`${captures.issue.text}` reads likely large payload data into memory at once."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Prefer `createReadStream`, iterator-based parsing, or chunked processing for large files and binary payloads.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.missing-batch-operations
|
|
5
|
+
title: Missing batching of operations
|
|
6
|
+
summary: Repeated one-by-one operations inside loops should prefer available batch-style helpers.
|
|
7
|
+
rationale: Batch APIs usually reduce round trips, contention, and request overhead compared with item-at-a-time loops.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- io
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-036
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.missing-batch-operations
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.io
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.75
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- io
|
|
31
|
+
message:
|
|
32
|
+
title: Prefer batch helpers over one-by-one loops
|
|
33
|
+
summary: "`${captures.issue.text}` runs one item at a time even though a batch-style helper appears available."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Replace the per-item loop with the available batch or bulk helper, or add one if the dependency already supports it.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.nested-loops-hot-path
|
|
5
|
+
title: Nested loops in hot path (O(n²) risk)
|
|
6
|
+
summary: Nested loops in the same function should be reviewed for quadratic work on larger inputs.
|
|
7
|
+
rationale: Nested iteration often becomes the dominant cost on hot paths and usually benefits from indexing or precomputation.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- algorithmic
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-031
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.nested-loops-hot-path
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.algorithmic
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.75
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- algorithmic
|
|
31
|
+
message:
|
|
32
|
+
title: Review nested loops on hot paths
|
|
33
|
+
summary: "`${captures.issue.text}` nests loops and may introduce quadratic work."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Precompute lookups, index one side with a set or map, or otherwise flatten the repeated scan.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.repeated-expensive-computation
|
|
5
|
+
title: Repeated expensive computation
|
|
6
|
+
summary: Repeating the same expensive computation in one block should usually be cached.
|
|
7
|
+
rationale: Recomputing the same serialization, key projection, or formatter construction wastes CPU and obscures intent.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- compute
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-032
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: block
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.repeated-expensive-computation
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.compute
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.8
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- compute
|
|
31
|
+
message:
|
|
32
|
+
title: Cache repeated expensive work
|
|
33
|
+
summary: "`${captures.issue.text}` repeats expensive computation in the same block."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Compute the value once, store it in a local binding, and reuse the cached result.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.repeated-io-in-loop
|
|
5
|
+
title: Repeated IO call inside loop
|
|
6
|
+
summary: Database or network calls inside loops can multiply latency and load.
|
|
7
|
+
rationale: Per-item IO in loops often turns one operation into N round trips and is a common performance bottleneck.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- io
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-033
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.repeated-io-in-loop
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.io
|
|
26
|
+
severity: high
|
|
27
|
+
confidence: 0.9
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- io
|
|
31
|
+
message:
|
|
32
|
+
title: Avoid repeated IO calls inside loops
|
|
33
|
+
summary: "`${captures.issue.text}` performs IO inside a loop and can multiply latency."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Move the IO outside the loop, batch the work, or fetch the required data in one call before iterating.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.retained-large-object
|
|
5
|
+
title: Unnecessarily retained large object
|
|
6
|
+
summary: Large payloads assigned into shared state should be reviewed for shorter lifetimes.
|
|
7
|
+
rationale: Retaining large buffers or payload objects longer than needed increases memory pressure and GC churn.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- memory
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-039
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.retained-large-object
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.memory
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.65
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- memory
|
|
31
|
+
message:
|
|
32
|
+
title: Avoid retaining large payloads in shared state
|
|
33
|
+
summary: Review `${captures.issue.text}` and release large payload objects as soon as possible.
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Keep the payload local, persist only the fields you need, or clear the shared reference after use.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.sequential-async-calls
|
|
5
|
+
title: Sequential async calls that could run in parallel
|
|
6
|
+
summary: Independent awaited calls in the same block should not serialize unnecessarily.
|
|
7
|
+
rationale: Awaiting unrelated async operations one by one increases end-to-end latency without adding correctness.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- async
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-035
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: block
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.sequential-async-calls
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.async
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.8
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- async
|
|
31
|
+
message:
|
|
32
|
+
title: Parallelize independent async calls
|
|
33
|
+
summary: "`${captures.issue.text}` is awaited after an independent async call and may serialize work unnecessarily."
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Start both operations first and await them together, for example with `Promise.all(...)`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.unbounded-growth-memory-leak
|
|
5
|
+
title: Potential memory leak from unbounded growth
|
|
6
|
+
summary: Shared collections that only grow should be reviewed for eviction or lifecycle boundaries.
|
|
7
|
+
rationale: Appending to long-lived state without a cap can leak memory across requests or over process lifetime.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- memory
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-038
|
|
13
|
+
stability: stable
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.unbounded-growth-memory-leak
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.memory
|
|
26
|
+
severity: high
|
|
27
|
+
confidence: 0.75
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- memory
|
|
31
|
+
message:
|
|
32
|
+
title: Bound long-lived collection growth
|
|
33
|
+
summary: Review `${captures.issue.text}` for unbounded growth of shared state.
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Add eviction, scoping, or cleanup so the collection cannot grow forever across calls.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
apiVersion: critiq.dev/v1alpha1
|
|
2
|
+
kind: Rule
|
|
3
|
+
metadata:
|
|
4
|
+
id: ts.performance.unnecessary-rerenders-from-state-misuse
|
|
5
|
+
title: Unnecessary re-renders from state misuse
|
|
6
|
+
summary: React state setters invoked directly during render should be reviewed for rerender loops.
|
|
7
|
+
rationale: Calling a state setter during render can trigger avoidable rerenders and unstable component behavior.
|
|
8
|
+
tags:
|
|
9
|
+
- performance
|
|
10
|
+
- ui
|
|
11
|
+
- rules-catalog
|
|
12
|
+
- crq-per-040
|
|
13
|
+
stability: experimental
|
|
14
|
+
appliesTo: function
|
|
15
|
+
scope:
|
|
16
|
+
languages:
|
|
17
|
+
- typescript
|
|
18
|
+
- javascript
|
|
19
|
+
match:
|
|
20
|
+
fact:
|
|
21
|
+
kind: performance.unnecessary-rerenders-from-state-misuse
|
|
22
|
+
bind: issue
|
|
23
|
+
emit:
|
|
24
|
+
finding:
|
|
25
|
+
category: performance.ui
|
|
26
|
+
severity: medium
|
|
27
|
+
confidence: 0.7
|
|
28
|
+
tags:
|
|
29
|
+
- performance
|
|
30
|
+
- ui
|
|
31
|
+
message:
|
|
32
|
+
title: Avoid render-path state updates
|
|
33
|
+
summary: Review `${captures.issue.text}` and move state updates out of the render path.
|
|
34
|
+
remediation:
|
|
35
|
+
summary: Trigger the state update from an event, effect, or transition instead of directly during render.
|