prr-kit 1.1.2 → 1.2.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 +1 -1
- package/README.md +260 -235
- package/docs/assets/banner.svg +33 -165
- package/docs/assets/how-it-works.svg +87 -0
- package/package.json +60 -60
- package/src/core/agents/prr-master.agent.yaml +18 -7
- package/src/core/tasks/clear.md +140 -0
- package/src/core/tasks/help.md +15 -13
- package/src/core/workflows/clear/workflow.md +6 -0
- package/src/core/workflows/help/workflow.md +6 -0
- package/src/core/workflows/party-mode/steps/step-01-load-reviewers.md +35 -24
- package/src/core/workflows/party-mode/steps/step-02-discussion.md +45 -25
- package/src/core/workflows/party-mode/workflow.md +2 -2
- package/src/prr/agents/architecture-reviewer.agent.yaml +65 -45
- package/src/prr/agents/business-reviewer.agent.yaml +66 -0
- package/src/prr/agents/general-reviewer.agent.yaml +64 -48
- package/src/prr/agents/performance-reviewer.agent.yaml +65 -45
- package/src/prr/agents/security-reviewer.agent.yaml +67 -43
- package/src/prr/config-template.yaml +97 -0
- package/src/prr/data/stacks/actix.md +55 -0
- package/src/prr/data/stacks/alpine.md +47 -0
- package/src/prr/data/stacks/android.md +53 -0
- package/src/prr/data/stacks/angular.md +96 -0
- package/src/prr/data/stacks/ansible.md +55 -0
- package/src/prr/data/stacks/apollo.md +54 -0
- package/src/prr/data/stacks/astro.md +48 -0
- package/src/prr/data/stacks/aws-cdk.md +55 -0
- package/src/prr/data/stacks/axum.md +56 -0
- package/src/prr/data/stacks/babylonjs.md +55 -0
- package/src/prr/data/stacks/bash.md +53 -0
- package/src/prr/data/stacks/bevy.md +53 -0
- package/src/prr/data/stacks/bootstrap.md +52 -0
- package/src/prr/data/stacks/bun.md +55 -0
- package/src/prr/data/stacks/cpp.md +57 -0
- package/src/prr/data/stacks/csharp.md +95 -0
- package/src/prr/data/stacks/css.md +55 -0
- package/src/prr/data/stacks/cypress.md +53 -0
- package/src/prr/data/stacks/d3.md +53 -0
- package/src/prr/data/stacks/deno.md +49 -0
- package/src/prr/data/stacks/django.md +92 -0
- package/src/prr/data/stacks/docker.md +79 -0
- package/src/prr/data/stacks/drizzle.md +54 -0
- package/src/prr/data/stacks/dynamodb.md +55 -0
- package/src/prr/data/stacks/electron.md +44 -0
- package/src/prr/data/stacks/elixir.md +53 -0
- package/src/prr/data/stacks/expo.md +53 -0
- package/src/prr/data/stacks/expressjs.md +82 -0
- package/src/prr/data/stacks/fastapi.md +88 -0
- package/src/prr/data/stacks/fastify.md +60 -0
- package/src/prr/data/stacks/fiber.md +55 -0
- package/src/prr/data/stacks/firebase.md +43 -0
- package/src/prr/data/stacks/flask.md +46 -0
- package/src/prr/data/stacks/flutter.md +75 -0
- package/src/prr/data/stacks/gin.md +57 -0
- package/src/prr/data/stacks/github-actions.md +71 -0
- package/src/prr/data/stacks/go.md +88 -0
- package/src/prr/data/stacks/godot.md +56 -0
- package/src/prr/data/stacks/graphql.md +76 -0
- package/src/prr/data/stacks/grpc.md +56 -0
- package/src/prr/data/stacks/haskell.md +48 -0
- package/src/prr/data/stacks/helm.md +54 -0
- package/src/prr/data/stacks/hono.md +54 -0
- package/src/prr/data/stacks/htmx.md +38 -0
- package/src/prr/data/stacks/java.md +87 -0
- package/src/prr/data/stacks/jest-vitest.md +87 -0
- package/src/prr/data/stacks/jquery.md +50 -0
- package/src/prr/data/stacks/junit.md +53 -0
- package/src/prr/data/stacks/kotlin.md +89 -0
- package/src/prr/data/stacks/kubernetes.md +148 -0
- package/src/prr/data/stacks/langchain.md +56 -0
- package/src/prr/data/stacks/laravel.md +56 -0
- package/src/prr/data/stacks/libgdx.md +46 -0
- package/src/prr/data/stacks/lit.md +49 -0
- package/src/prr/data/stacks/love2d.md +51 -0
- package/src/prr/data/stacks/lua.md +51 -0
- package/src/prr/data/stacks/mobx.md +54 -0
- package/src/prr/data/stacks/mongodb.md +85 -0
- package/src/prr/data/stacks/monogame.md +51 -0
- package/src/prr/data/stacks/mysql.md +57 -0
- package/src/prr/data/stacks/nestjs.md +95 -0
- package/src/prr/data/stacks/nextjs.md +88 -0
- package/src/prr/data/stacks/nginx.md +55 -0
- package/src/prr/data/stacks/node.md +56 -0
- package/src/prr/data/stacks/nuxtjs.md +91 -0
- package/src/prr/data/stacks/openai-api.md +54 -0
- package/src/prr/data/stacks/opengl.md +54 -0
- package/src/prr/data/stacks/phaser.md +54 -0
- package/src/prr/data/stacks/phoenix.md +55 -0
- package/src/prr/data/stacks/php.md +56 -0
- package/src/prr/data/stacks/playwright.md +86 -0
- package/src/prr/data/stacks/postgresql.md +60 -0
- package/src/prr/data/stacks/prisma.md +81 -0
- package/src/prr/data/stacks/pygame.md +52 -0
- package/src/prr/data/stacks/pytest.md +53 -0
- package/src/prr/data/stacks/python.md +94 -0
- package/src/prr/data/stacks/pytorch.md +54 -0
- package/src/prr/data/stacks/qwik.md +50 -0
- package/src/prr/data/stacks/rails.md +48 -0
- package/src/prr/data/stacks/react-native.md +77 -0
- package/src/prr/data/stacks/react.md +104 -0
- package/src/prr/data/stacks/redis.md +76 -0
- package/src/prr/data/stacks/redux.md +107 -0
- package/src/prr/data/stacks/remix.md +51 -0
- package/src/prr/data/stacks/rust.md +88 -0
- package/src/prr/data/stacks/sass.md +51 -0
- package/src/prr/data/stacks/scala.md +50 -0
- package/src/prr/data/stacks/scikit-learn.md +53 -0
- package/src/prr/data/stacks/sequelize.md +54 -0
- package/src/prr/data/stacks/socket-io.md +54 -0
- package/src/prr/data/stacks/solidity.md +53 -0
- package/src/prr/data/stacks/solidjs.md +45 -0
- package/src/prr/data/stacks/spring-boot.md +92 -0
- package/src/prr/data/stacks/sql.md +85 -0
- package/src/prr/data/stacks/sqlite.md +55 -0
- package/src/prr/data/stacks/styled-components.md +51 -0
- package/src/prr/data/stacks/supabase.md +57 -0
- package/src/prr/data/stacks/svelte.md +77 -0
- package/src/prr/data/stacks/sveltekit.md +54 -0
- package/src/prr/data/stacks/swift.md +61 -0
- package/src/prr/data/stacks/tailwindcss.md +10 -0
- package/src/prr/data/stacks/tanstack-query.md +48 -0
- package/src/prr/data/stacks/tauri.md +52 -0
- package/src/prr/data/stacks/terraform.md +53 -0
- package/src/prr/data/stacks/three.md +53 -0
- package/src/prr/data/stacks/trpc.md +49 -0
- package/src/prr/data/stacks/typeorm.md +40 -0
- package/src/prr/data/stacks/typescript.md +83 -0
- package/src/prr/data/stacks/unity.md +61 -0
- package/src/prr/data/stacks/unreal.md +58 -0
- package/src/prr/data/stacks/vite.md +48 -0
- package/src/prr/data/stacks/vue3.md +95 -0
- package/src/prr/data/stacks/vulkan.md +53 -0
- package/src/prr/data/stacks/wasm.md +49 -0
- package/src/prr/data/stacks/webpack.md +48 -0
- package/src/prr/data/stacks/zig.md +51 -0
- package/src/prr/data/stacks/zustand.md +56 -0
- package/src/prr/workflows/1-discover/select-pr/steps/step-05-confirm.md +1 -0
- package/src/prr/workflows/1-discover/select-pr/workflow.md +1 -1
- package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-01-analyze-files.md +334 -0
- package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-02-collect-sources.md +451 -0
- package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-03-build-knowledge-base.md +337 -0
- package/src/prr/workflows/2-analyze/collect-pr-context/workflow.md +123 -0
- package/src/prr/workflows/2-analyze/describe-pr/steps/step-02-classify.md +12 -6
- package/src/prr/workflows/2-analyze/describe-pr/steps/step-03-walkthrough.md +59 -1
- package/src/prr/workflows/3-review/architecture-review/checklist.md +4 -0
- package/src/prr/workflows/3-review/architecture-review/instructions.xml +32 -4
- package/src/prr/workflows/3-review/architecture-review/workflow.yaml +17 -18
- package/src/prr/workflows/3-review/business-review/checklist.md +27 -0
- package/src/prr/workflows/3-review/business-review/instructions.xml +153 -0
- package/src/prr/workflows/3-review/business-review/workflow.yaml +17 -0
- package/src/prr/workflows/3-review/general-review/checklist.md +5 -1
- package/src/prr/workflows/3-review/general-review/instructions.xml +39 -8
- package/src/prr/workflows/3-review/general-review/workflow.yaml +17 -18
- package/src/prr/workflows/3-review/performance-review/checklist.md +3 -1
- package/src/prr/workflows/3-review/performance-review/instructions.xml +10 -3
- package/src/prr/workflows/3-review/performance-review/workflow.yaml +17 -18
- package/src/prr/workflows/3-review/security-review/checklist.md +2 -1
- package/src/prr/workflows/3-review/security-review/instructions.xml +8 -3
- package/src/prr/workflows/3-review/security-review/workflow.yaml +18 -19
- package/src/prr/workflows/4-improve/improve-code/workflow.yaml +17 -18
- package/src/prr/workflows/6-report/generate-report/steps/step-01-collect.md +9 -2
- package/src/prr/workflows/6-report/generate-report/steps/step-02-organize.md +28 -7
- package/src/prr/workflows/6-report/generate-report/steps/step-03-write.md +6 -4
- package/src/prr/workflows/6-report/generate-report/templates/review-report.template.md +124 -78
- package/src/prr/workflows/6-report/post-comments/steps/step-01-format.md +104 -13
- package/src/prr/workflows/6-report/post-comments/steps/step-02-post.md +92 -21
- package/src/prr/workflows/6-report/post-comments/workflow.md +6 -0
- package/src/prr/workflows/quick/workflow.md +138 -32
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-01-scan-configs.md +0 -106
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-02-extract-rules.md +0 -131
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-03-ask-context.md +0 -194
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-04-save-context.md +0 -161
- package/src/prr/workflows/0-setup/collect-project-context/workflow.md +0 -58
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# C++ — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `*.cpp` · `*.cc` · `*.cxx` · `*.hpp` · `*.h` with `#include <` · `namespace` · `std::` · `CMakeLists.txt` · `Makefile`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** `strcpy`, `sprintf`, `gets`, `strcat` with user-controlled input → buffer overflow. Use `strncpy`, `snprintf`, `fgets`, `strncat` with explicit size limits, or C++ `std::string`.
|
|
11
|
+
- **[CRITICAL]** `system(userInput)` → command injection. Avoid `system()`; use `exec*()` family with sanitized argv array.
|
|
12
|
+
- **[HIGH]** Integer overflow in buffer size calculation (e.g., `malloc(count * sizeof(T))`) → heap overflow if multiplication wraps. Use checked arithmetic or `std::vector`.
|
|
13
|
+
- **[HIGH]** `printf(userInput)` — untrusted string used as format specifier → format string attack. Always use `printf("%s", userInput)`.
|
|
14
|
+
- **[MEDIUM]** Reading from uninitialized memory → undefined behavior, potential info leak. Initialize all variables at declaration.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Performance
|
|
19
|
+
|
|
20
|
+
- **[HIGH]** Large object passed by value to function → unnecessary copy. Use `const T&` or `T&&` (move semantics).
|
|
21
|
+
- **[HIGH]** `new` / `delete` in tight loop → heap fragmentation and allocator overhead. Use stack allocation, `std::vector` with `reserve()`, or a pool allocator.
|
|
22
|
+
- **[HIGH]** Virtual function calls in performance-critical inner loop → vtable dispatch per call. Consider templates (CRTP) or `final` to enable devirtualization.
|
|
23
|
+
- **[MEDIUM]** `std::map` / `std::set` where `std::unordered_map` / `std::unordered_set` suffices → O(log n) vs O(1) average lookup.
|
|
24
|
+
- **[MEDIUM]** Missing `std::move` when returning named local variable (before NRVO applies) → unnecessary copy.
|
|
25
|
+
- **[LOW]** `std::endl` in output loop → flushes buffer every iteration. Use `'\n'` unless flush is explicitly needed.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
- **[CRITICAL]** Raw owning pointer without RAII wrapper → manual `delete` easily missed, double-free, or leak. Use `std::unique_ptr` for exclusive ownership, `std::shared_ptr` for shared.
|
|
32
|
+
- **[HIGH]** Memory leak: `new` allocated object not `delete`d on all code paths (including exceptions) → use smart pointers or stack allocation.
|
|
33
|
+
- **[HIGH]** Use-after-free: accessing pointer after `delete` → undefined behavior. Set pointer to `nullptr` after delete (or eliminate raw `delete` entirely).
|
|
34
|
+
- **[HIGH]** Double-free: `delete` called twice on same pointer → heap corruption. Smart pointers prevent this automatically.
|
|
35
|
+
- **[MEDIUM]** Global / static mutable state → thread safety issues without synchronization. Use `std::mutex` or `thread_local`.
|
|
36
|
+
- **[LOW]** Deep inheritance hierarchy → prefer composition over inheritance for non-polymorphic designs.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Code Quality
|
|
41
|
+
|
|
42
|
+
- **[HIGH]** `using namespace std;` in a header file → pollutes namespace of every translation unit that includes it. Limit to `.cpp` files or use explicit `std::` prefix.
|
|
43
|
+
- **[MEDIUM]** Member function that doesn't modify state not marked `const` → prevents use with `const` objects, hides intent.
|
|
44
|
+
- **[MEDIUM]** Implicit conversion between signed and unsigned integers without explicit cast → subtle overflow or wrap-around bugs. Enable `-Wsign-conversion`.
|
|
45
|
+
- **[MEDIUM]** Missing `override` on virtual method override → if base signature changes, override silently becomes a new function.
|
|
46
|
+
- **[LOW]** Long function > 50 lines with complex control flow → decompose into smaller functions.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Common Bugs & Pitfalls
|
|
51
|
+
|
|
52
|
+
- **[CRITICAL]** Undefined behavior: signed integer overflow, null pointer dereference, out-of-bounds array access, data race → UB cannot be relied upon to "just work" even if it appears to.
|
|
53
|
+
- **[HIGH]** Iterator invalidation: modifying `std::vector` / `std::map` while iterating (erase, push_back) → UB. Collect indices/iterators first or use erase-remove idiom.
|
|
54
|
+
- **[HIGH]** `std::vector::operator[]` without bounds check in production code → UB on out-of-bounds. Use `.at()` during development/testing; add assertion in production.
|
|
55
|
+
- **[MEDIUM]** Object slicing: assigning derived-class object to base-class by value → vtable and derived members are lost silently.
|
|
56
|
+
- **[MEDIUM]** `#pragma once` vs include guards mixed in same codebase → inconsistency. Pick one convention.
|
|
57
|
+
- **[LOW]** Comparing floating-point values with `==` → imprecise. Use epsilon comparison: `std::abs(a - b) < epsilon`.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# C# / .NET — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `*.cs` files · `*.csproj` · `*.sln` · `using System` · `namespace` · `dotnet` CLI · `appsettings.json` · ASP.NET Core · Entity Framework
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** SQL built with string interpolation: `$"SELECT * WHERE id = {id}"` → SQL injection. Use parameterized queries (`SqlParameter`) or EF Core.
|
|
11
|
+
- **[CRITICAL]** `Process.Start(new ProcessStartInfo { UseShellExecute = true, FileName = input })` with user input → command injection. Use argument arrays, not shell strings.
|
|
12
|
+
- **[CRITICAL]** `BinaryFormatter.Deserialize()` on untrusted data → arbitrary code execution (CVE-class). Use `System.Text.Json` or `XmlSerializer` with safe settings.
|
|
13
|
+
- **[HIGH]** Missing `[ValidateAntiForgeryToken]` on ASP.NET Core POST/PUT/DELETE → CSRF.
|
|
14
|
+
- **[HIGH]** Hardcoded connection strings / API keys in `appsettings.json` committed to VCS → use `dotnet user-secrets`, Azure Key Vault, or environment variables.
|
|
15
|
+
- **[HIGH]** `Path.Combine(baseDir, userInput)` without `Path.GetFullPath()` + prefix check → path traversal.
|
|
16
|
+
- **[HIGH]** `JsonConvert.DeserializeObject<T>(input)` with `TypeNameHandling.All` → remote code execution via `$type`.
|
|
17
|
+
- **[HIGH]** Sensitive data logged via `_logger.LogInformation()` with string interpolation → log injection.
|
|
18
|
+
- **[HIGH]** `RNGCryptoServiceProvider` deprecated → use `RandomNumberGenerator.Fill()` (static, .NET 6+).
|
|
19
|
+
- **[MEDIUM]** `Math.Random` used for security purposes → use `RandomNumberGenerator` from `System.Security.Cryptography`.
|
|
20
|
+
- **[MEDIUM]** CORS configured too permissively (`AllowAnyOrigin + AllowCredentials`) → CORS misconfiguration.
|
|
21
|
+
- **[MEDIUM]** `HttpContext.Request.Headers["X-Forwarded-For"]` trusted without proxy validation → IP spoofing.
|
|
22
|
+
- **[MEDIUM]** Passwords stored as `string` → immutable, lingers in memory. Use `SecureString` or clear char arrays.
|
|
23
|
+
- **[LOW]** Exception details returned in API response in production → configure `app.UseExceptionHandler` properly.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Performance
|
|
28
|
+
|
|
29
|
+
- **[HIGH]** `async void` method (not event handler) → exceptions unobservable, swallowed silently. Use `async Task`.
|
|
30
|
+
- **[HIGH]** `.Result` or `.Wait()` on Task in ASP.NET context → deadlock on synchronization context. Always `await`.
|
|
31
|
+
- **[HIGH]** `string +=` in loop → O(n²). Use `StringBuilder` or `string.Join`.
|
|
32
|
+
- **[HIGH]** `await` inside loop instead of batching with `Task.WhenAll()` → sequential async = unnecessary latency.
|
|
33
|
+
- **[HIGH]** Entity Framework `Include()` loading entire related collection when only count or single field needed → select projection.
|
|
34
|
+
- **[HIGH]** Synchronous EF Core calls (`.ToList()` without `Async`) in ASP.NET Core → blocking thread pool thread.
|
|
35
|
+
- **[HIGH]** Missing `AsNoTracking()` on read-only EF Core queries → change tracker overhead.
|
|
36
|
+
- **[MEDIUM]** `.ToList()` called on `IQueryable` before further LINQ operations → materializes entire result prematurely.
|
|
37
|
+
- **[MEDIUM]** Missing `ConfigureAwait(false)` in library code → captures unnecessary sync context.
|
|
38
|
+
- **[MEDIUM]** `IEnumerable<T>` iterated multiple times → if `IQueryable`, triggers multiple DB calls. Materialize once.
|
|
39
|
+
- **[MEDIUM]** Large object allocation in hot path without pooling → GC pressure. Use `ArrayPool<T>` or `MemoryPool<T>`.
|
|
40
|
+
- **[MEDIUM]** Not using `Span<T>` / `Memory<T>` for buffer operations → unnecessary heap allocations.
|
|
41
|
+
- **[MEDIUM]** LINQ `GroupBy` on large in-memory collections → O(n) memory → push to DB if possible.
|
|
42
|
+
- **[LOW]** Not using `HttpClient` with `IHttpClientFactory` → socket exhaustion with `new HttpClient()`.
|
|
43
|
+
- **[LOW]** `Task.Run` wrapping CPU-bound work then awaiting → thread pool overhead for short tasks.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Architecture
|
|
48
|
+
|
|
49
|
+
- **[HIGH]** `static` mutable fields in ASP.NET Core services → shared across requests, race conditions without sync.
|
|
50
|
+
- **[HIGH]** `IDisposable` object created without `using` or `await using` → resource leak (DB connections, streams).
|
|
51
|
+
- **[HIGH]** Service registered with wrong DI lifetime: `Scoped` injected into `Singleton` → captive dependency.
|
|
52
|
+
- **[HIGH]** `DbContext` used as singleton → EF Core `DbContext` is not thread-safe, designed for scoped lifetime.
|
|
53
|
+
- **[HIGH]** Business logic in ASP.NET Core controllers → move to service/domain layer.
|
|
54
|
+
- **[HIGH]** Not using Repository/Unit of Work pattern with EF Core → testability and coupling issues.
|
|
55
|
+
- **[MEDIUM]** Catching `Exception` too broadly → masks unrelated bugs. Catch specific exceptions.
|
|
56
|
+
- **[MEDIUM]** `HttpClient` instantiated per request → socket exhaustion. Use `IHttpClientFactory`.
|
|
57
|
+
- **[MEDIUM]** Not using `CancellationToken` propagation in async chain → operations can't be cancelled.
|
|
58
|
+
- **[MEDIUM]** God class with >500 lines → decompose by responsibility.
|
|
59
|
+
- **[MEDIUM]** Domain model leaking persistence concerns (EF navigation properties in API contracts).
|
|
60
|
+
- **[LOW]** Not using `record` types (C# 9+) for DTOs and value objects → verbose boilerplate equality.
|
|
61
|
+
- **[LOW]** Not using `sealed` on classes not designed for inheritance → unintended extension.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Code Quality
|
|
66
|
+
|
|
67
|
+
- **[HIGH]** Nullable reference types disabled (`<Nullable>disable</Nullable>`) → NullReferenceException not caught at compile time. Enable and fix warnings.
|
|
68
|
+
- **[HIGH]** `throw ex` instead of `throw` in catch block → resets stack trace, hides origin.
|
|
69
|
+
- **[HIGH]** `int.Parse(input)` without `TryParse` → `FormatException` on invalid input crashes request.
|
|
70
|
+
- **[HIGH]** Not using `pattern matching` for type checks → verbose `is`/`as` + null check.
|
|
71
|
+
- **[MEDIUM]** `var` used where type not obvious from RHS → readability suffers.
|
|
72
|
+
- **[MEDIUM]** Missing cancellation token propagation in async call chain.
|
|
73
|
+
- **[MEDIUM]** Magic strings for configuration keys instead of typed options with `IOptions<T>`.
|
|
74
|
+
- **[MEDIUM]** `?.` null conditional chaining producing `null` result silently used without null check.
|
|
75
|
+
- **[MEDIUM]** Not using C# 10+ features (`record struct`, global using, file-scoped namespace) in modern projects.
|
|
76
|
+
- **[MEDIUM]** Event subscription not unsubscribed → memory leak via event handler reference.
|
|
77
|
+
- **[LOW]** Public method without XML doc comment on public API.
|
|
78
|
+
- **[LOW]** Not using `nameof()` for property names in validation/logging → typo-prone string literals.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Common Bugs & Pitfalls
|
|
83
|
+
|
|
84
|
+
- **[HIGH]** `DateTime.Now` for storage/cross-system → timezone-dependent. Use `DateTime.UtcNow` or `DateTimeOffset.UtcNow`.
|
|
85
|
+
- **[HIGH]** `LINQ` deferred execution not understood → query executed multiple times or after `DbContext` disposed.
|
|
86
|
+
- **[HIGH]** EF Core `SaveChangesAsync()` not called after mutations → changes not persisted silently.
|
|
87
|
+
- **[HIGH]** `lock(this)` or `lock(typeof(Foo))` → public lock objects can be acquired externally → deadlock.
|
|
88
|
+
- **[HIGH]** `Task.WhenAll()` not awaited → exceptions swallowed.
|
|
89
|
+
- **[MEDIUM]** Struct implementing `IDisposable` boxed via interface → `Dispose` called on copy.
|
|
90
|
+
- **[MEDIUM]** Enum default value (0) not explicitly named → uninitialized enum has valid meaningless value.
|
|
91
|
+
- **[MEDIUM]** `string.Compare` for equality → use `string.Equals` with `StringComparison`.
|
|
92
|
+
- **[MEDIUM]** EF Core lazy loading enabled without understanding → N+1 queries via navigation property access.
|
|
93
|
+
- **[MEDIUM]** `ConcurrentDictionary.GetOrAdd` with factory that has side effects → factory may run multiple times.
|
|
94
|
+
- **[LOW]** `StringBuilder.Append()` returning `this` for chaining not used → verbose multi-line appends.
|
|
95
|
+
- **[LOW]** `Encoding.UTF8.GetString` on bytes from wrong encoding → mojibake.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# CSS / SCSS / SASS — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `*.css`, `*.scss`, `*.sass`, `*.less`, `*.styl` files
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[HIGH]** `url()` with user-controlled value → CSS injection loading external resources. Never interpolate user data into CSS.
|
|
11
|
+
- **[MEDIUM]** `expression()` (IE legacy) in CSS → executes JavaScript. Never use.
|
|
12
|
+
- **[MEDIUM]** Missing `Content-Security-Policy: style-src 'self'` allowing inline styles that could be injected.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Performance
|
|
17
|
+
|
|
18
|
+
- **[HIGH]** Universal selector `*` with expensive properties (`box-shadow`, `transform`) → applied to every element, layout thrash.
|
|
19
|
+
- **[HIGH]** `@import` in CSS (not SCSS) → blocking serial HTTP requests. Use `<link>` or bundler imports.
|
|
20
|
+
- **[HIGH]** Triggering layout thrash — reading layout properties (`offsetWidth`, `getBoundingClientRect`) then writing in same frame (JS + CSS combo). Use `transform` for animations.
|
|
21
|
+
- **[MEDIUM]** Overly specific selectors (`div > ul > li > a.link`) → slow browser matching, hard to override.
|
|
22
|
+
- **[MEDIUM]** `position: fixed` elements on scroll-heavy pages without `will-change: transform` → repaints entire page.
|
|
23
|
+
- **[MEDIUM]** Animating `width`/`height`/`top`/`left` → triggers layout recalculation. Use `transform: translate()` / `scale()` instead.
|
|
24
|
+
- **[LOW]** Large unused CSS from third-party libraries not purged → bundle bloat.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Architecture
|
|
29
|
+
|
|
30
|
+
- **[HIGH]** Deeply nested SCSS selectors (>4 levels) → high specificity, hard to override, fragile.
|
|
31
|
+
- **[MEDIUM]** Color/spacing values duplicated as magic numbers instead of CSS custom properties / SCSS variables.
|
|
32
|
+
- **[MEDIUM]** `!important` overuse → specificity war, maintainability nightmare.
|
|
33
|
+
- **[MEDIUM]** Class names not following a convention (BEM, utility-first) → inconsistent, hard to predict.
|
|
34
|
+
- **[LOW]** Vendor prefixes added manually instead of using Autoprefixer → out-of-date, missing prefixes.
|
|
35
|
+
- **[LOW]** SCSS `@extend` used with placeholder selectors — can cause unexpected selector bloat.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Accessibility
|
|
40
|
+
|
|
41
|
+
- **[HIGH]** `outline: none` / `outline: 0` on focusable elements without alternative focus indicator → keyboard navigation broken (WCAG 2.4.7).
|
|
42
|
+
- **[HIGH]** Text contrast below WCAG AA (4.5:1 for normal text, 3:1 for large text).
|
|
43
|
+
- **[MEDIUM]** `display: none` used on content that should be accessible to screen readers → use visually-hidden pattern instead.
|
|
44
|
+
- **[MEDIUM]** `pointer-events: none` on interactive elements without disabling them in HTML → keyboard still reaches them.
|
|
45
|
+
- **[LOW]** `user-select: none` on body/large containers → breaks copy-paste for users.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Common Bugs & Pitfalls
|
|
50
|
+
|
|
51
|
+
- **[HIGH]** `z-index` wars — arbitrary large values (`z-index: 9999`) without documented stacking context.
|
|
52
|
+
- **[MEDIUM]** Margin collapsing not accounted for — `margin-top` on child collapses with parent without overflow/padding/border.
|
|
53
|
+
- **[MEDIUM]** `height: 100%` on child without parent having explicit height → renders as 0.
|
|
54
|
+
- **[MEDIUM]** Flexbox/Grid item `flex: 1` without `min-width: 0` → overflow issues with long text content.
|
|
55
|
+
- **[LOW]** `calc()` with missing spaces around operators (`calc(100%-20px)`) → invalid in some browsers.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Cypress — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `cypress`, `cy.visit(`, `cy.get(`, `cy.intercept(`, `cypress.config.*`, `describe(` in `cypress/`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[HIGH]** Hardcoded test credentials in Cypress test files committed to the repository → credentials exposed in version control and CI logs. Use `Cypress.env()` with environment variables injected at CI time; store secrets in your CI secret manager.
|
|
10
|
+
- **[HIGH]** `cy.request()` calling authenticated API endpoints without proper auth token setup → tests may inadvertently call real external services or bypass auth unexpectedly. Always set the auth token via `cy.session()` or `Authorization` header in `cy.request()` explicitly.
|
|
11
|
+
- **[MEDIUM]** Secrets placed in `cypress.env.json` and committed to the repository → sensitive values in version control. Add `cypress.env.json` to `.gitignore` and inject values via CI environment variables only.
|
|
12
|
+
- **[MEDIUM]** Test data including PII from seeded users not cleaned up after test runs → leftover PII accumulates in the staging database. Add an `afterEach` or `after` hook that deletes or resets test data via a `cy.task()` DB call.
|
|
13
|
+
- **[LOW]** `chromeWebSecurity: false` set in `cypress.config` to allow cross-origin requests → disables browser same-origin protections, letting cross-origin attacks go undetected. Remove `chromeWebSecurity: false` and use `cy.origin()` for legitimate cross-origin testing.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Performance
|
|
18
|
+
- **[HIGH]** `cy.wait(5000)` hard-coded time waits instead of waiting on network requests → tests are slow and flaky; the wait may not be enough on slow CI. Replace with `cy.intercept()` aliases and `cy.wait('@aliasName')` to wait for the actual request.
|
|
19
|
+
- **[HIGH]** `cy.session()` not used for authentication state → login UI flow executed on every test, adding seconds per test. Cache login state with `cy.session('user', loginFn)` so auth is established once per session.
|
|
20
|
+
- **[HIGH]** `cy.visit()` called for every test when tests could share a single page load → full navigation and app initialization overhead per test. Visit once in `before()` and reset state between tests without full reloads where possible.
|
|
21
|
+
- **[MEDIUM]** `cy.get('.selector')` traversing the entire DOM when a scoped query is possible → selector resolution slows on large DOMs. Chain `.within()` to scope: `cy.get('#parent').within(() => { cy.get('button') })`.
|
|
22
|
+
- **[MEDIUM]** `cy.task()` not used for database seeding → seeding done via UI or `cy.request()`, which depends on the app being fully functional. Use `cy.task('db:seed', data)` to insert directly into the DB via Node.js.
|
|
23
|
+
- **[LOW]** Screenshots and videos enabled in CI without artifact retention policy → disk fills up and artifact uploads slow pipelines. Set `video: false` in CI and enable `screenshotOnRunFailure: true` only.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Architecture
|
|
28
|
+
- **[HIGH]** Selectors based on CSS classes or visible text → tests break whenever the UI is restyled or copy changes. Use `data-cy` attributes: `cy.get('[data-cy="submit-button"]')`.
|
|
29
|
+
- **[HIGH]** Repeated auth, navigation, or interaction logic not extracted to custom commands → duplication across tests; one UI change breaks dozens. Extract sequences to `Cypress.Commands.add('login', ...)`.
|
|
30
|
+
- **[MEDIUM]** Tests not organized by feature or page → test files become hundreds of lines, slow to scan. Organize into subdirectories: `cypress/e2e/auth/login.cy.ts`, `cypress/e2e/dashboard/overview.cy.ts`.
|
|
31
|
+
- **[MEDIUM]** Cypress commands chained after native Promises without returning the Cypress chain → commands run out of order. Always return the Cypress chain from inside `.then()` callbacks; avoid mixing native Promises with Cypress commands.
|
|
32
|
+
- **[MEDIUM]** No page object pattern for complex multi-step user flows → test code directly manipulates selectors; refactoring requires editing every test. Create helper functions or custom commands that encapsulate multi-step flows.
|
|
33
|
+
- **[LOW]** Cypress configuration not split into base config plus environment-specific overrides → CI and local configs are identical; developers must manually change settings. Use `defineConfig` with environment-specific override files.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Code Quality
|
|
38
|
+
- **[HIGH]** `cy.get(sel).then(el => cy.get(sel2))` anti-pattern instead of `.within()` → creates unnecessary nested chains that are hard to read. Use `cy.get('#container').within(() => { cy.get('button').click() })`.
|
|
39
|
+
- **[HIGH]** Assertions missing from tests → test passes even when the feature is broken because no assertion checks the expected state. Every test must end with at least one `.should()` assertion verifying the intended UI state.
|
|
40
|
+
- **[MEDIUM]** `.should('be.visible')` not asserted before interacting with an element → Cypress may try to click a hidden or detached element, causing flaky failures. Use `cy.get(sel).should('be.visible').click()`.
|
|
41
|
+
- **[MEDIUM]** Test data hardcoded inline instead of using fixtures → same data duplicated across tests; changing it requires editing multiple files. Extract to `cypress/fixtures/` JSON files and load with `cy.fixture()`.
|
|
42
|
+
- **[MEDIUM]** No TypeScript types for custom commands in `cypress/support/index.d.ts` → custom commands typed as `any`; autocomplete absent. Extend the `Chainable` interface with type declarations for every custom command.
|
|
43
|
+
- **[LOW]** No Cypress lint rules (eslint-plugin-cypress) configured → Cypress-specific anti-patterns not caught automatically. Add `eslint-plugin-cypress` with the recommended rule set to the ESLint config.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Common Bugs & Pitfalls
|
|
48
|
+
- **[HIGH]** `cy.get()` matching multiple elements when only one is expected → Cypress acts on the first match silently; tests pass against the wrong element. Assert `.should('have.length', 1)` or use a more specific selector.
|
|
49
|
+
- **[HIGH]** Async code (Promises, async/await) in `before()` or `beforeEach()` without returning the Cypress chain → setup completes after tests begin; state is missing when tests run. Return the Cypress chain from setup hooks or use `cy.wrap(promise)`.
|
|
50
|
+
- **[MEDIUM]** `cy.intercept()` alias defined after `cy.visit()` → network request fires before intercept is registered; alias never resolves. Always define `cy.intercept()` aliases before `cy.visit()` or the action that triggers the request.
|
|
51
|
+
- **[MEDIUM]** `cy.contains()` matching hidden text nodes (e.g., inside a tooltip or offscreen element) → test passes but the visible text the user sees is different. Add `.should('be.visible')` after `cy.contains()`.
|
|
52
|
+
- **[MEDIUM]** "detached from DOM" error when an element re-renders between `cy.get()` and the subsequent action → element reference becomes stale after React or Vue re-render. Re-query the element inside `.then()` after re-render-triggering actions.
|
|
53
|
+
- **[LOW]** `cy.clock()` and `cy.tick()` not restored after tests that manipulate timers → fake clock bleeds into subsequent tests causing unexpected timing behavior. Call `cy.clock().then(c => c.restore())` in `afterEach`.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# D3.js — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `from 'd3'`, `import * as d3`, `d3.select`, `d3.scaleLinear`, `d3.axisBottom`, `.enter().append`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[CRITICAL]** `.html(userContent)` called on a D3 selection with unencoded user data → XSS; D3 sets `innerHTML` directly. Use `.text(userContent)` for text content, or sanitize HTML with DOMPurify before passing to `.html()`.
|
|
10
|
+
- **[HIGH]** `d3.json(userUrl)` or `d3.csv(userUrl)` called with a URL derived from user input without validation → SSRF or loading of malicious data. Validate and allowlist URLs before passing to D3 fetch helpers.
|
|
11
|
+
- **[HIGH]** Dynamic SVG `<foreignObject>` elements populated with user-controlled HTML → XSS vector that bypasses SVG's normal escaping. Sanitize all HTML content placed inside `<foreignObject>` elements.
|
|
12
|
+
- **[MEDIUM]** Axis tick labels or tooltip content bound directly to raw user data values → unencoded strings rendered as SVG `text` content. D3 `.text()` is safe, but `.html()` or `innerHTML` on tooltip containers is not; sanitize before inserting.
|
|
13
|
+
- **[LOW]** D3 charts embedding data from external sources in SVG `<title>` or `<desc>` elements without encoding → minor content injection risk. Encode all external data used in SVG metadata.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Performance
|
|
18
|
+
- **[HIGH]** Not using `selection.join()` (D3 v5+) for enter/update/exit pattern → manual three-phase code is verbose, error-prone, and slower to execute. Replace `.selectAll().data().enter().append()` chains with `.join()`.
|
|
19
|
+
- **[HIGH]** Rebuilding the entire chart (re-running enter/append on all elements) on every data update instead of updating existing elements → O(n) DOM operations on each change. Use the update selection to modify existing elements; only enter new ones and exit removed ones.
|
|
20
|
+
- **[HIGH]** No transition throttling or debouncing on resize-driven re-renders → chart redraws on every pixel of window resize. Debounce resize handlers with at least 100ms delay.
|
|
21
|
+
- **[MEDIUM]** Rendering thousands of SVG `<circle>` or `<path>` elements for large datasets → SVG DOM overhead causes frame drops. Switch to `<canvas>` rendering (D3 + Canvas API) or use `d3-tile` with WebGL for datasets above ~5,000 points.
|
|
22
|
+
- **[MEDIUM]** D3 scale functions (`d3.scaleLinear`, `d3.scaleBand`) recreated on every render without memoization in React → recalculation overhead and new references causing child re-renders. Memoize scales with `useMemo` keyed on data and dimensions.
|
|
23
|
+
- **[LOW]** `d3.csv` or `d3.json` called inside a render or effect without caching result → network request repeated on every render cycle. Load data once, store in state or ref, pass as prop.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Architecture
|
|
28
|
+
- **[HIGH]** D3 DOM manipulation (`.append()`, `.attr()`, `.style()`) running alongside React rendering on the same elements → virtual DOM and actual DOM desync causing lost updates and React warnings. Let React own the DOM; use D3 only for math (scales, axes, path generators) and render SVG elements via JSX.
|
|
29
|
+
- **[MEDIUM]** Chart dimensions hardcoded as magic numbers instead of measured from the container → chart overflows or underutilizes space on different screen sizes. Use `ResizeObserver` on the container element and pass measured `width`/`height` to the chart.
|
|
30
|
+
- **[MEDIUM]** Data transformation (filtering, aggregation, normalization) mixed with rendering logic in the same function → hard to test and reuse. Separate data pipeline (pure functions) from rendering (D3/SVG operations).
|
|
31
|
+
- **[MEDIUM]** Axes, legends, and tooltips tightly coupled to chart internals → cannot reuse or test independently. Extract as separate components or factory functions receiving scales as arguments.
|
|
32
|
+
- **[LOW]** Using deprecated D3 v4 or v5 API patterns (`.on('zoom')` without `d3.zoom()`, old nest API) in a v7 codebase → subtle behavioral differences. Audit imports and replace deprecated patterns with v7 equivalents.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Code Quality
|
|
37
|
+
- **[HIGH]** `d3.select<ElementType, Datum>()` called without TypeScript generics → selection typed as `Selection<BaseType, unknown, ...>`, losing all type safety on datum and element properties. Always provide element and datum type parameters.
|
|
38
|
+
- **[MEDIUM]** Chart margins defined as magic numbers inline → hardcoded values repeated across multiple chart files. Extract to a named config object `const margin = { top: 20, right: 30, bottom: 40, left: 50 }`.
|
|
39
|
+
- **[MEDIUM]** Transitions across charts using inconsistent durations and easing → jarring UX. Define shared transition presets and apply via `d3.transition('name')` named transitions.
|
|
40
|
+
- **[MEDIUM]** Tooltip element created via `d3.select('body').append('div')` without cleanup → orphaned tooltip `<div>` elements accumulate in the DOM. Remove tooltip in component cleanup/teardown.
|
|
41
|
+
- **[LOW]** Not using D3's built-in color schemes (`d3.schemeTableau10`, `d3.interpolateViridis`) → custom color arrays duplicated and inconsistent across charts. Use D3 ordinal and sequential color scales with built-in schemes.
|
|
42
|
+
- **[LOW]** SVG not given explicit `viewBox` and `preserveAspectRatio` attributes → SVG does not scale correctly in flexible containers. Always set `viewBox="0 0 ${width} ${height}"` on the root SVG element.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Common Bugs & Pitfalls
|
|
47
|
+
- **[HIGH]** Data join key function omitted from `.data(dataset, d => d.id)` → D3 matches elements by index, causing wrong enter/exit assignment when data order changes. Always provide a key function that uniquely identifies each datum.
|
|
48
|
+
- **[HIGH]** `d3.scaleLinear().domain([d3.min(data), d3.max(data)])` when `data` contains non-numeric or `undefined` values → domain becomes `[NaN, NaN]`, all positions render at 0. Validate and filter data before computing domain; use `d3.extent` for safety.
|
|
49
|
+
- **[MEDIUM]** SVG y-axis inverted (y increases downward) not accounted for in scale range → chart renders upside down. Set scale range as `[height, 0]` for y-axis scales to flip the coordinate system.
|
|
50
|
+
- **[MEDIUM]** Tooltip not hidden on `mouseleave` from chart element → tooltip stays visible indefinitely after cursor exits. Always pair `mouseover`/`mouseenter` handlers with `mouseleave` handlers that hide the tooltip.
|
|
51
|
+
- **[MEDIUM]** `d3.zoom` transform applied to the wrong parent element → pan/zoom causes jumpy or offset behavior. Apply the transform to the inner `<g>` element, not the `<svg>`, and account for margin offset.
|
|
52
|
+
- **[MEDIUM]** `d3.brushX` selection not cleared when data updates → stale brush extent filters new data unexpectedly. Reset brush programmatically with `brush.move(brushGroup, null)` on data change.
|
|
53
|
+
- **[LOW]** `path.attr('d', lineGenerator)` called before data is bound to the selection → generator receives `undefined`, rendering an empty or broken path. Ensure data is joined before calling path generators.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Deno — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `deno.json` / `deno.jsonc` · `deno.lock` · `Deno.` namespace usage · `import_map.json` · `jsr:` / `npm:` / `https://` import specifiers · `deno run` scripts
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** Running with `--allow-all` (or `-A`) flag → defeats Deno's permission model entirely. Grant only the minimum required permissions (`--allow-net=api.example.com`, `--allow-read=./data`).
|
|
11
|
+
- **[HIGH]** `--allow-run` without specifying allowed commands → grants permission to execute any process. Restrict with `--allow-run=git,node` allowlist.
|
|
12
|
+
- **[HIGH]** Dynamic `import()` from a user-controlled URL or string → arbitrary code execution. Never construct import URLs from external input.
|
|
13
|
+
- **[MEDIUM]** `--allow-net` without domain restriction → can connect to any host. Restrict to required domains.
|
|
14
|
+
- **[MEDIUM]** `--allow-env` without variable allowlist → exposes all environment variables including secrets to the script. Use `--allow-env=SPECIFIC_VAR`.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Performance
|
|
19
|
+
|
|
20
|
+
- **[HIGH]** `Deno.readFileSync` / `Deno.writeFileSync` on large files in an async context → blocks the event loop. Use async `await Deno.readFile()` / `Deno.writeFile()`.
|
|
21
|
+
- **[MEDIUM]** `for await ... of` on `Deno.readDir` without early exit → iterates entire directory. Break early when target is found.
|
|
22
|
+
- **[LOW]** Not using Deno's native `Deno.serve()` (Deno 1.35+) → older `serve()` from `std/http` has more overhead. Prefer the native API for new code.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
|
|
28
|
+
- **[HIGH]** Node.js `require()` used without `npm:` specifier → `require` is not available in Deno by default. Use `import` with `npm:package-name` or `jsr:@scope/package`.
|
|
29
|
+
- **[HIGH]** Dependencies imported from raw `https://` URLs without version pinning → non-reproducible builds; upstream URL changes silently break code. Use `jsr:` or `npm:` specifiers with versions.
|
|
30
|
+
- **[MEDIUM]** `deno.lock` file not committed to version control → dependency versions not reproducible across environments. Commit the lockfile.
|
|
31
|
+
- **[MEDIUM]** Permissions requested at CLI level too broadly for a library → libraries should not prescribe permissions; they should document what permissions callers must grant.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Code Quality
|
|
36
|
+
|
|
37
|
+
- **[HIGH]** `// @ts-nocheck` or `any` type used extensively → Deno enforces strict TypeScript by default; suppressing type checking defeats the safety guarantee.
|
|
38
|
+
- **[MEDIUM]** `Deno.env.get("VAR")` result used without null check → returns `undefined` for missing variables. Always validate or use a default: `Deno.env.get("PORT") ?? "8000"`.
|
|
39
|
+
- **[LOW]** `import * as foo from "module"` when only one or two exports are needed → increases bundle size in compiled output. Use named imports.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Common Bugs & Pitfalls
|
|
44
|
+
|
|
45
|
+
- **[HIGH]** Network request attempted without `--allow-net` → `Deno.errors.PermissionDenied` thrown at runtime, not at startup. Test with the exact set of permissions you intend to deploy with.
|
|
46
|
+
- **[HIGH]** Top-level `await` in a module imported by many others → delays the entire import chain. Use `await` inside functions or limit top-level `await` to entry points.
|
|
47
|
+
- **[MEDIUM]** `import 'https://deno.land/...'` from the Deno third-party module registry → `deno.land/x` is no longer receiving new modules; prefer `jsr:` for new dependencies.
|
|
48
|
+
- **[MEDIUM]** `Deno.cwd()` used to construct file paths → returns the working directory at runtime, which may differ from the script's location. Use `import.meta.url` + `new URL('./file', import.meta.url).pathname` for script-relative paths.
|
|
49
|
+
- **[LOW]** `Deno.exit()` called in a library function → terminates the entire process; callers cannot handle the error. Throw an error instead.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Django — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `from django`, `models.Model`, `views.py`, `urls.py`, `settings.py`, `manage.py`, `INSTALLED_APPS`, DRF `serializers.py`, `djangorestframework`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** Raw SQL via `raw()` or `cursor.execute()` with string formatting → SQL injection. Use `%s` parameterized or ORM.
|
|
11
|
+
- **[CRITICAL]** `mark_safe()` on user-controlled content → XSS. Only use on strings you fully control.
|
|
12
|
+
- **[CRITICAL]** `DEBUG = True` in production → detailed error pages with source code exposed.
|
|
13
|
+
- **[CRITICAL]** `SECRET_KEY` hardcoded in `settings.py` → version control exposure. Use `os.environ.get()` or `django-environ`.
|
|
14
|
+
- **[HIGH]** Missing `@login_required` or DRF `permission_classes` → unauthenticated access.
|
|
15
|
+
- **[HIGH]** `@csrf_exempt` without justification on state-changing endpoints.
|
|
16
|
+
- **[HIGH]** `ALLOWED_HOSTS = ['*']` in production → HTTP Host header injection.
|
|
17
|
+
- **[HIGH]** Object-level permission not checked → `get_object_or_404` without ownership verification → IDOR.
|
|
18
|
+
- **[HIGH]** Missing `is_staff`/`is_superuser` check on admin-equivalent views.
|
|
19
|
+
- **[HIGH]** File upload without content-type validation → execute malicious file via misconfigured server.
|
|
20
|
+
- **[HIGH]** Open redirect via unvalidated `next` parameter in login views.
|
|
21
|
+
- **[MEDIUM]** Missing `HttpOnly`/`Secure`/`SameSite` flags on session cookie (`SESSION_COOKIE_*` settings).
|
|
22
|
+
- **[MEDIUM]** `MEDIA_ROOT` served by Django in production → should be served by Nginx/CDN with correct content-type.
|
|
23
|
+
- **[MEDIUM]** DRF `TokenAuthentication` over HTTP → tokens transmitted in plaintext. Enforce HTTPS.
|
|
24
|
+
- **[LOW]** Django admin accessible at default `/admin/` path in production → known attack target; change with custom URL.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Performance
|
|
29
|
+
|
|
30
|
+
- **[CRITICAL]** N+1 ORM queries — accessing related objects in loop without `select_related()` or `prefetch_related()`.
|
|
31
|
+
- **[HIGH]** `QuerySet.all()` without filtering → loads entire table.
|
|
32
|
+
- **[HIGH]** Missing database indexes on frequently filtered/ordered fields (`db_index=True` or `Meta.indexes`).
|
|
33
|
+
- **[HIGH]** `len(queryset)` instead of `queryset.count()` → loads all records into memory.
|
|
34
|
+
- **[HIGH]** Synchronous external API calls in view → blocks Django worker. Use Celery for async work.
|
|
35
|
+
- **[HIGH]** DRF serializer with `many=True` on unfiltered QuerySet → serializing entire table.
|
|
36
|
+
- **[HIGH]** Missing Celery/task queue for email sending, image processing in request-response cycle.
|
|
37
|
+
- **[MEDIUM]** Missing pagination on list views (ListView or DRF `pagination_class`).
|
|
38
|
+
- **[MEDIUM]** Missing `only()` / `defer()` on queries that don't need all fields → SELECT *.
|
|
39
|
+
- **[MEDIUM]** `values()` / `values_list()` not used for read-only aggregation → full ORM overhead.
|
|
40
|
+
- **[MEDIUM]** QuerySet not cached via `django.core.cache` for expensive repeated reads.
|
|
41
|
+
- **[MEDIUM]** Database queries in template rendering (lazy ORM evaluation).
|
|
42
|
+
- **[LOW]** `annotate()` not used for counting related objects → N+1 Python-side counting.
|
|
43
|
+
- **[LOW]** Not using `bulk_create()` / `bulk_update()` for batch DB operations.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Architecture
|
|
48
|
+
|
|
49
|
+
- **[HIGH]** Business logic in views or templates → move to model methods, managers, or service layer.
|
|
50
|
+
- **[HIGH]** Fat models >500 lines → split into service classes or managers.
|
|
51
|
+
- **[HIGH]** Django signals used for critical business logic → hard to trace, test. Prefer explicit service calls.
|
|
52
|
+
- **[HIGH]** Not using `AbstractBaseUser` for custom auth → `AUTH_USER_MODEL` migration headaches later.
|
|
53
|
+
- **[HIGH]** `settings.py` not split by environment (base/dev/prod) → debug settings leaked to production.
|
|
54
|
+
- **[MEDIUM]** Missing `related_name` on FK/M2M → reverse accessor is `<model>_set`, confusing.
|
|
55
|
+
- **[MEDIUM]** Direct `settings` import in reusable apps → use `AppConfig`.
|
|
56
|
+
- **[MEDIUM]** Missing migration for model change → `makemigrations` not run, production schema drift.
|
|
57
|
+
- **[MEDIUM]** DRF `ModelSerializer` used with `fields = '__all__'` → over-exposing fields.
|
|
58
|
+
- **[MEDIUM]** DRF view doing too much (validation + business logic + serialization) → extract to service.
|
|
59
|
+
- **[LOW]** Not using Django's `AbstractModel` for common fields (created_at, updated_at).
|
|
60
|
+
- **[LOW]** App-level URLs not included in project `urls.py` via `include()`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Code Quality
|
|
65
|
+
|
|
66
|
+
- **[HIGH]** Missing `__str__` on Model → `<Model object (1)>` in admin and logs.
|
|
67
|
+
- **[HIGH]** Missing `Meta.ordering` on models used in list views → non-deterministic order.
|
|
68
|
+
- **[HIGH]** DRF serializer `validate_<field>` not raising `serializers.ValidationError` → validation silently ignored.
|
|
69
|
+
- **[MEDIUM]** `get_or_create` without checking `created` flag → unaware if object was newly created.
|
|
70
|
+
- **[MEDIUM]** `filter().get()` instead of `get()` → extra unnecessary query.
|
|
71
|
+
- **[MEDIUM]** Hardcoded URLs in templates/views instead of `{% url %}` or `reverse()`.
|
|
72
|
+
- **[MEDIUM]** Not using `get_object_or_404` in views → raw `Model.objects.get()` raises 500 on miss.
|
|
73
|
+
- **[MEDIUM]** `null=True` on `CharField`/`TextField` → two empty values (empty string + NULL). Use `blank=True` only.
|
|
74
|
+
- **[MEDIUM]** DRF not using `@action` decorator for custom actions → awkward URL patterns.
|
|
75
|
+
- **[LOW]** Missing `verbose_name`/`verbose_name_plural` on models → ugly admin.
|
|
76
|
+
- **[LOW]** Missing `app_name` in `urls.py` → namespace conflicts.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Common Bugs & Pitfalls
|
|
81
|
+
|
|
82
|
+
- **[HIGH]** QuerySet as default argument (`def fn(qs=Model.objects.all())`) → evaluated once at definition, stale data.
|
|
83
|
+
- **[HIGH]** Transaction not used around multiple related DB writes → partial failure leaves inconsistent state.
|
|
84
|
+
- **[HIGH]** `update()` on QuerySet bypasses `save()` and signals → `pre_save`/`post_save` not triggered.
|
|
85
|
+
- **[HIGH]** `delete()` on QuerySet bypasses model `delete()` and `post_delete` signals.
|
|
86
|
+
- **[HIGH]** `request.user` accessed in model or service layer → breaking separation of concerns and testability.
|
|
87
|
+
- **[MEDIUM]** Timezone-naive `datetime.now()` instead of `timezone.now()` → bugs in non-UTC deployments.
|
|
88
|
+
- **[MEDIUM]** `get_or_create` in concurrent requests → race condition, unique constraint violations.
|
|
89
|
+
- **[MEDIUM]** Signal `sender` not specified → signal fires for all models of that event.
|
|
90
|
+
- **[MEDIUM]** Migration squashing not done → extremely slow migration history.
|
|
91
|
+
- **[LOW]** `on_delete=models.CASCADE` default assumed without considering orphan behavior.
|
|
92
|
+
- **[LOW]** `choices` on CharField not enforced at DB level → only validated in forms/serializers.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Docker — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `Dockerfile*`, `docker-compose*.yml`, `.dockerignore`, `FROM `, `RUN `, `COPY `, `EXPOSE`, `docker` in CI, `compose.yml`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** Container running as `root` (default) → container escape = root on host. Add `USER nonroot` after creating non-root user.
|
|
11
|
+
- **[CRITICAL]** Secrets passed as `ENV` or `ARG` in Dockerfile → visible in image layers and `docker inspect`. Use Docker BuildKit secrets (`--secret`) or runtime env injection.
|
|
12
|
+
- **[CRITICAL]** `.env` file copied into image via `COPY . .` without `.dockerignore` → secrets in image layer.
|
|
13
|
+
- **[HIGH]** Base image tagged as `latest` → non-deterministic builds, unexpected CVEs from upstream updates. Use `node:20.11.1-alpine3.19`.
|
|
14
|
+
- **[HIGH]** Outdated base image with known CVEs → scan with `docker scout cves` or Trivy.
|
|
15
|
+
- **[HIGH]** `docker-compose.yml` with `privileged: true` without justification → container escapes host namespaces.
|
|
16
|
+
- **[HIGH]** `volumes: /:/host` or similar host root mount → full filesystem access from container.
|
|
17
|
+
- **[HIGH]** Ports bound to `0.0.0.0` for internal services → exposed to all interfaces. Use `127.0.0.1:port:port`.
|
|
18
|
+
- **[HIGH]** Hardcoded credentials in `docker-compose.yml` committed to VCS → credential exposure.
|
|
19
|
+
- **[MEDIUM]** `--cap-add SYS_ADMIN` or `--security-opt seccomp=unconfined` → elevated privileges.
|
|
20
|
+
- **[MEDIUM]** No read-only filesystem (`--read-only`) for containers that don't need write access.
|
|
21
|
+
- **[LOW]** Docker socket (`/var/run/docker.sock`) mounted in container → full Docker daemon access = host escape.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Performance
|
|
26
|
+
|
|
27
|
+
- **[HIGH]** Layer order wrong — source code copied before `package.json` + install → cache invalidated every code change. Copy lockfile + install first, then source.
|
|
28
|
+
- **[HIGH]** No multi-stage build for compiled languages → dev deps and build tools in production image.
|
|
29
|
+
- **[HIGH]** Large files/directories not in `.dockerignore` → slow build context transfer on every build.
|
|
30
|
+
- **[MEDIUM]** `RUN apt-get install` without `--no-install-recommends` → bloated image with unnecessary packages.
|
|
31
|
+
- **[MEDIUM]** Multiple separate `RUN` commands → extra layers. Chain with `&&` and `\`.
|
|
32
|
+
- **[MEDIUM]** Not using `--mount=type=cache` for package manager cache in BuildKit → re-downloading packages.
|
|
33
|
+
- **[MEDIUM]** Base image not using Alpine/distroless → unnecessarily large image.
|
|
34
|
+
- **[MEDIUM]** `COPY . .` before `npm install` → code changes invalidate dependency cache.
|
|
35
|
+
- **[LOW]** Not squashing layers in final image → history reveals sensitive intermediate steps.
|
|
36
|
+
- **[LOW]** `apt-get` without `apt-get clean && rm -rf /var/lib/apt/lists/*` → package cache in layer.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Architecture
|
|
41
|
+
|
|
42
|
+
- **[HIGH]** Application state written to container filesystem → lost on restart. Use named volumes for persistent data.
|
|
43
|
+
- **[HIGH]** Missing `HEALTHCHECK` → orchestrator can't detect unhealthy container, continues sending traffic.
|
|
44
|
+
- **[HIGH]** Single container running multiple services → violates SRP, harder to scale, complicated restarts.
|
|
45
|
+
- **[HIGH]** Secret rotation requires image rebuild → use runtime secret injection (Vault, K8s Secrets, AWS SSM).
|
|
46
|
+
- **[MEDIUM]** `docker-compose.yml` used in production without orchestration → no auto-scaling, rolling updates.
|
|
47
|
+
- **[MEDIUM]** Config hardcoded in Dockerfile → image not portable across environments. Use env vars + config files.
|
|
48
|
+
- **[MEDIUM]** `depends_on` used without health check condition → service starts before dependency is ready.
|
|
49
|
+
- **[MEDIUM]** Not using `restart: unless-stopped` / `restart: on-failure` → container doesn't recover from crashes.
|
|
50
|
+
- **[LOW]** Missing `ENTRYPOINT` — only `CMD` → command accidentally overridden at runtime.
|
|
51
|
+
- **[LOW]** Not pinning Docker Compose version in CI → behavior changes with compose updates.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Code Quality
|
|
56
|
+
|
|
57
|
+
- **[HIGH]** No `.dockerignore` file → unpredictable, large build context (`.git`, `node_modules`, `.env`).
|
|
58
|
+
- **[HIGH]** `ADD url ./` instead of `RUN curl + COPY` → `ADD` fetches during build, no verification.
|
|
59
|
+
- **[MEDIUM]** `ADD` used when `COPY` sufficient — `ADD` has implicit tar extraction and remote URL fetching; `COPY` is explicit.
|
|
60
|
+
- **[MEDIUM]** `ENV` variables not documented → unclear what configuration is available.
|
|
61
|
+
- **[MEDIUM]** `EXPOSE` port not matching actual application port → confusing, may break port mapping.
|
|
62
|
+
- **[MEDIUM]** Non-deterministic `apt-get` package versions → builds differ over time. Pin package versions.
|
|
63
|
+
- **[LOW]** Missing `LABEL` annotations (maintainer, version, description) → untracked images in registry.
|
|
64
|
+
- **[LOW]** Long `RUN` command without line continuation `\` → unreadable Dockerfile.
|
|
65
|
+
- **[LOW]** Not using `WORKDIR` → inconsistent working directory, fragile relative paths.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Common Bugs & Pitfalls
|
|
70
|
+
|
|
71
|
+
- **[HIGH]** Process not handling `SIGTERM` → container doesn't stop gracefully, killed after timeout. Use `exec` form `["node", "app.js"]` not shell form.
|
|
72
|
+
- **[HIGH]** PID 1 not reaping zombie processes → memory leak. Use `--init` flag or `tini` as init process.
|
|
73
|
+
- **[HIGH]** `CMD ["npm", "start"]` without shell → environment variables from `ENV` not available in some contexts. Test both.
|
|
74
|
+
- **[MEDIUM]** Container timezone different from host → cron jobs run at wrong time. Set `TZ` env var.
|
|
75
|
+
- **[MEDIUM]** `COPY --chown` not used → files owned by root inside container, non-root user can't write.
|
|
76
|
+
- **[MEDIUM]** DNS resolution failing in container → missing `--dns` or network mode mismatch.
|
|
77
|
+
- **[MEDIUM]** Build arg (`ARG`) vs environment variable (`ENV`) confusion — `ARG` only available during build, `ENV` persists.
|
|
78
|
+
- **[LOW]** Port already in use on host → `docker run` fails with `bind: address already in use`.
|
|
79
|
+
- **[LOW]** Volume mount overwriting container files → `node_modules` from host overlays container's installed deps.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Drizzle ORM — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `drizzle-orm`, `from 'drizzle-orm'`, `drizzle()`, `pgTable`, `mysqlTable`, `sqliteTable`, `migrate()`, `drizzle-kit`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[CRITICAL]** Raw SQL via `sql` tagged template literal with user input not parameterized (e.g., `sql\`WHERE name = '${userInput}'\``) → SQL injection. Use Drizzle's query builder or pass user values as parameterized bindings: `sql\`WHERE name = ${userInput}\`` (Drizzle auto-parameterizes interpolated values in the `sql` tag).
|
|
10
|
+
- **[HIGH]** Drizzle schema file imported and exposed via an API route response → internal table structure, column names, and types leaked to clients. Never serialize schema objects into HTTP responses; expose only shaped DTOs.
|
|
11
|
+
- **[HIGH]** No row-level access control in query layer → any authenticated user can query any row by guessing IDs. Add a `WHERE userId = currentUserId` (or equivalent tenant filter) to every query that returns user-owned data.
|
|
12
|
+
- **[MEDIUM]** Database credentials stored in `drizzle.config.ts` and committed to the repository → credentials exposed in version control history. Read credentials from environment variables (`process.env.DATABASE_URL`) in the config file.
|
|
13
|
+
- **[MEDIUM]** Migrations run with superuser DB credentials in production CI/CD pipeline → accidental schema destruction possible. Use a migration-only role with `CREATE`, `ALTER`, `DROP` on the app schema; separate from the runtime role.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Performance
|
|
18
|
+
- **[HIGH]** N+1 queries from nested `.findMany()` calls without using `.with()` for relations → one query fired per parent row to load children. Switch to Drizzle's relational query API with `.with({ relation: true })` to load related data in a single query.
|
|
19
|
+
- **[HIGH]** No `.limit()` on list queries → full table scan returned to application memory, OOM risk on large tables. Every `findMany` or `select` on an unbounded table must include `.limit(n)`.
|
|
20
|
+
- **[HIGH]** `db.select()` without column projection (no `{ field: table.field }` shape) → all columns fetched; breaks index-only scans and sends unnecessary data over the wire. Pass a columns object to `db.select({ id: table.id, name: table.name })` for every query.
|
|
21
|
+
- **[MEDIUM]** Multi-table mutations (insert + update across tables) not wrapped in a transaction → partial failure leaves data inconsistent. Use `db.transaction(async (tx) => { ... })` for all operations that must be atomic.
|
|
22
|
+
- **[MEDIUM]** Indexes not defined in the Drizzle schema alongside the table → indexes exist only in migration SQL but not tracked in the schema, causing drift. Define all indexes using `.index()` or `.uniqueIndex()` in the same schema file as the table.
|
|
23
|
+
- **[MEDIUM]** `drizzle-kit push` used in staging or production to apply schema changes → `push` may drop and recreate columns, causing data loss. Use `drizzle-kit generate` to produce migration SQL files, review them, then apply with `drizzle-kit migrate`.
|
|
24
|
+
- **[LOW]** Connection not pooled (no PgBouncer or application-level pool like `pg-pool`) → new TCP connection opened per request; connection exhaustion under load. Configure a connection pool and pass the pooled client to `drizzle()`.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Architecture
|
|
29
|
+
- **[HIGH]** Database queries written directly in API route handlers or controllers → no separation of data access from transport layer, making queries untestable in isolation. Extract all DB queries into a repository or data-access layer; import the repository into route handlers.
|
|
30
|
+
- **[MEDIUM]** Drizzle's relational query API not used when related data is needed → developers write complex manual JOINs that are harder to maintain and more error-prone. Use `.query.table.findMany({ with: { relation: {} } })` for relation traversal.
|
|
31
|
+
- **[MEDIUM]** Schema spread across many files without a barrel `schema.ts` export → Drizzle relations and `db` instance require all schema tables; missing tables cause runtime errors. Collect all table definitions in a single `schema.ts` (or `schema/index.ts`) barrel export passed to `drizzle()`.
|
|
32
|
+
- **[MEDIUM]** No seed script for development database → developers set up data manually, causing environment divergence. Provide a `seed.ts` script using Drizzle inserts that can be run with `npx tsx seed.ts`.
|
|
33
|
+
- **[LOW]** Table names in schema not matching actual database table names (no `{ name: 'actual_table' }` option) → Drizzle generates incorrect SQL, causing "relation does not exist" errors. Set the explicit table name string in `pgTable('actual_table_name', ...)` when it differs from the variable name.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Code Quality
|
|
38
|
+
- **[HIGH]** Column types in Drizzle schema not aligned with application TypeScript types (e.g., `text()` for a column that only holds enum values) → invalid values accepted at DB level. Use `text('col', { enum: ['a', 'b', 'c'] })` or add a Zod/Valibot parse step at the boundary.
|
|
39
|
+
- **[HIGH]** `.returning()` omitted after `.insert()` when the generated ID or defaults are needed → `result` is an empty array, causing `undefined` reference on the next line. Chain `.returning()` to every INSERT that the application reads back.
|
|
40
|
+
- **[MEDIUM]** `.notNull()` missing on columns that are logically required → `null` values accumulate and cause unexpected `null` checks throughout the codebase. Add `.notNull()` to every column the application treats as mandatory.
|
|
41
|
+
- **[MEDIUM]** `.$type<T>()` not used for columns that hold branded or opaque types (e.g., `UserId`, `OrderId`) → plain `string` or `number` types allow mixing IDs across entities. Use `text('user_id').$type<UserId>()` to brand column types and catch cross-entity ID bugs at compile time.
|
|
42
|
+
- **[MEDIUM]** Drizzle TypeScript types not inferred with `typeof table.$inferSelect` / `.$inferInsert` → manual type duplication drifts from schema over time. Use Drizzle's inferred types everywhere instead of hand-written interfaces.
|
|
43
|
+
- **[LOW]** Table naming inconsistency between Drizzle variable name and the DB table name passed as first arg → confusion when reading error messages or raw SQL logs. Keep Drizzle variable name and table name string identical (both snake_case).
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Common Bugs & Pitfalls
|
|
48
|
+
- **[HIGH]** `.insert().values()` result used without `.returning()` → the return value is a result metadata object (rowCount), not the inserted row; accessing `.id` returns `undefined`. Always add `.returning({ id: table.id })` (or the needed fields) after `.insert().values()`.
|
|
49
|
+
- **[HIGH]** Relations defined in the Drizzle schema with `relations()` but `.with()` not used in the actual query → N+1 still occurs because `relations()` only informs the query API; it does not automatically join. Use `db.query.table.findMany({ with: { relation: {} } })` to activate the relation.
|
|
50
|
+
- **[MEDIUM]** `drizzle-kit push` used against a production database → push computes a diff and may drop columns or tables to match the schema, causing irreversible data loss. Enforce a CI policy that only `drizzle-kit migrate` (with reviewed migration files) runs in production.
|
|
51
|
+
- **[MEDIUM]** Database connection created inside a request handler (e.g., `const db = drizzle(new Pool(...))` per request) → new pool created for every request, exhausting DB connections rapidly. Create the `db` instance once at module initialization and reuse it.
|
|
52
|
+
- **[MEDIUM]** `eq(table.col, undefined)` passed to a WHERE clause when an optional filter is absent → Drizzle generates `WHERE col = NULL` (never matches). Guard optional filters: `...(value !== undefined ? [eq(table.col, value)] : [])` using `and()`.
|
|
53
|
+
- **[LOW]** Drizzle type inference breaking on complex union or conditional columns → TypeScript errors cascade through the codebase. Pin Drizzle and `drizzle-kit` to the same exact semver version and upgrade them together.
|
|
54
|
+
|