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,85 @@
|
|
|
1
|
+
# MongoDB — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: mongoose, mongodb npm package, MongoClient, Schema, Model, .find(), .aggregate(), .populate(), MONGO_URI, mongodb+srv://, .model(
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** Query operator injection — user object passed directly into a query (e.g. `Model.find({ username: req.body.username })` where the value is `{ : "" }`) → all documents are returned, bypassing authentication. Sanitise with `express-mongo-sanitize` or validate that every user-supplied field is a primitive type before use.
|
|
11
|
+
- **[CRITICAL]** `` clause with user-controlled input → server-side JavaScript injection (SSJI) executes arbitrary code inside MongoDB. Disable JavaScript execution entirely with `security.javascriptEnabled: false` in `mongod.conf`.
|
|
12
|
+
- **[CRITICAL]** MongoDB connection string with credentials stored in source code or committed to version control → credential exposure; rotate immediately and use environment variables (`MONGO_URI`) loaded at runtime.
|
|
13
|
+
- **[HIGH]** Returning full documents including sensitive fields (`password`, `token`, `ssn`) from API routes → over-exposure of sensitive data. Always apply `.select("-password -token")` or a projection object to strip sensitive fields before sending the response.
|
|
14
|
+
- **[HIGH]** Missing authentication on the MongoDB connection in production → any host that can reach the MongoDB port can read and write all data. Enable auth, create a dedicated application user with least-privilege roles, and use TLS.
|
|
15
|
+
- **[HIGH]** Atlas network access rule set to `0.0.0.0/0` (Allow from Anywhere) → database is accessible from the entire internet. Restrict the IP allow-list to your application server IPs or VPC CIDR only.
|
|
16
|
+
- **[HIGH]** `` pipeline stage with a user-controlled `from` field (collection name) → arbitrary collection data can be exfiltrated. Validate and whitelist the collection name against a fixed set before constructing the pipeline.
|
|
17
|
+
- **[MEDIUM]** Missing Mongoose schema validation (no `type`, `required`, or `enum` constraints) → arbitrary fields are stored and type errors go undetected until runtime. Define strict schemas with proper validators for all fields.
|
|
18
|
+
- **[MEDIUM]** `findOne({ email })` without checking whether `email` is `undefined` → returns the first document in the collection when the field is undefined, granting access to the wrong user. Validate that query fields are defined and non-null before executing the query.
|
|
19
|
+
- **[MEDIUM]** Not using `.lean()` for read-only operations → Mongoose document objects expose prototype methods that can be a vector for prototype pollution if the result is spread into user-controlled structures. Use `.lean()` for read paths that do not need Mongoose methods.
|
|
20
|
+
- **[LOW]** Verbose MongoDB error messages forwarded to API responses → collection names, field names, and index names are revealed to the client, aiding enumeration. Catch all database errors and return sanitised, generic error messages.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Performance
|
|
25
|
+
|
|
26
|
+
- **[CRITICAL]** Missing index on frequently queried fields → MongoDB performs a full collection scan O(n) on every query, causing response times that degrade linearly with data growth. Add indexes via `schema.index()` or the `@@index` decorator and verify with `.explain()`.
|
|
27
|
+
- **[CRITICAL]** `find().toArray()` (or `.find({})` without `.limit()`) on a large collection → the entire collection is loaded into application memory, causing OOM crashes. Always append `.limit(n)` and implement cursor-based or offset pagination.
|
|
28
|
+
- **[HIGH]** `` stage not placed at the beginning of an aggregation pipeline → MongoDB processes all documents through earlier stages before filtering, massively increasing work. Move `` as early as possible so indexes can be used.
|
|
29
|
+
- **[HIGH]** N+1 query pattern — `.populate()` called on each item in a loop or nested inside a `.map()` → one query fires per document. Call `.populate()` on the parent query directly or use a single `` aggregation stage.
|
|
30
|
+
- **[HIGH]** Missing compound index for multi-field queries (e.g. `{ userId: 1, createdAt: -1 }`) → MongoDB cannot use a single-field index for a combined filter-plus-sort, causing a full scan. Add compound indexes that match the query's equality fields followed by range/sort fields.
|
|
31
|
+
- **[HIGH]** Using `` with non-indexed fields → each branch of `` triggers a separate full-collection scan. Index every field used in `` clauses or restructure the query.
|
|
32
|
+
- **[MEDIUM]** Missing projection in queries → entire documents (including large nested arrays and binary fields) are transferred over the network even when only two fields are needed. Pass a projection as the second argument to `.find()` or use `.select()`.
|
|
33
|
+
- **[MEDIUM]** `` operator with a very large array (over 1000 items) → performance degrades as MongoDB evaluates each element; the array is also sent in full over the wire. Consider batching into smaller `` calls or restructuring with a ``.
|
|
34
|
+
- **[MEDIUM]** Not using bulk operations (`insertMany`, `bulkWrite`) for batch writes → N individual round-trips to MongoDB, each with network latency overhead. Replace loops of `save()` / `insertOne()` with a single `bulkWrite` call.
|
|
35
|
+
- **[MEDIUM]** Aggregation pipeline without an early `` stage → all intermediate pipeline stages process the entire dataset before results are trimmed downstream. Add `` immediately after `` (or as early as semantics allow) to reduce intermediate data volume.
|
|
36
|
+
- **[MEDIUM]** Deeply nested documents queried by inner fields → MongoDB must traverse the entire document path for each query; deep nesting also inflates document size. Denormalise hot-path query fields to the top level or use references.
|
|
37
|
+
- **[MEDIUM]** `` search used without a text index → throws an error or falls back to a full scan. Create a `{ field: "text" }` index on every field used in text-search queries.
|
|
38
|
+
- **[LOW]** Slow queries not monitored via MongoDB Atlas Performance Advisor or `.explain("executionStats")` → performance regressions go undetected until they cause user-facing timeouts. Set up Atlas alerts or run `explain()` in development for any query touching large collections.
|
|
39
|
+
- **[LOW]** Missing TTL index on expiring documents (sessions, password-reset tokens, cache entries) → stale documents accumulate indefinitely, requiring a manual cron job. Use `schema.index({ createdAt: 1 }, { expireAfterSeconds: n })` for automatic cleanup.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
- **[HIGH]** Embedding unbounded arrays inside documents (e.g. appending to a `comments` or `events` array indefinitely) → documents grow beyond MongoDB's 16 MB limit, causing write failures. Use referencing with a separate collection when arrays can grow without bound.
|
|
46
|
+
- **[HIGH]** Using MongoDB as a relational database with many `` joins across multiple collections → defeats the purpose of a document store and produces poor performance. Revisit the schema to embed frequently co-read data and only reference data that is accessed independently.
|
|
47
|
+
- **[HIGH]** Multi-document writes without a transaction (`session.withTransaction`) → a failure midway leaves the database in an inconsistent state with no rollback. Wrap all multi-document operations in a session transaction.
|
|
48
|
+
- **[HIGH]** Mongoose connection created per request instead of reused via a module-level singleton → the connection pool is exhausted under load and each new connection adds latency. Establish one connection at application startup and export the client/model for reuse.
|
|
49
|
+
- **[MEDIUM]** Schema defined without `timestamps: true` option → no `createdAt` / `updatedAt` audit trail is available without manual tracking. Add `{ timestamps: true }` to every schema that stores user-created or mutable data.
|
|
50
|
+
- **[MEDIUM]** Embedding-vs-referencing decision not documented or consistently applied → developers silently add unbounded sub-documents over time without realising the 16 MB ceiling. Document the decision per collection and add a schema comment when embedding is bounded.
|
|
51
|
+
- **[MEDIUM]** Business logic placed in Mongoose pre/post hooks → hooks fire on `model.save()` but NOT on `findOneAndUpdate()`, `updateMany()`, etc., causing logic to be silently skipped. Centralise business rules in a service layer that is always called regardless of the update method used.
|
|
52
|
+
- **[MEDIUM]** `Schema.Types.Mixed` used as a catch-all for new fields → loses Mongoose validation, type coercion, and the ability to index sub-fields. Model every known field with a proper schema type; use `Mixed` only as a genuinely last resort.
|
|
53
|
+
- **[MEDIUM]** `mongoose.set("strictQuery", false)` (Mongoose 6 default) in use → querying on an unknown field silently returns wrong results instead of an error; Mongoose 7 changed the default to `true`. Explicitly set `strictQuery: true` and update any queries that rely on unknown fields.
|
|
54
|
+
- **[LOW]** Schema indexes not created with `{ background: true }` on large collections → foreground index creation blocks all reads and writes for the duration of the build. Always use `background: true` (MongoDB 4.2 and below) or the default non-blocking build in MongoDB 4.4+.
|
|
55
|
+
- **[LOW]** No soft-delete pattern for important records (users, orders, financial transactions) → accidental or malicious hard deletes are unrecoverable. Add a `deletedAt` timestamp field and filter it in queries instead of issuing `deleteOne`.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Code Quality
|
|
60
|
+
|
|
61
|
+
- **[HIGH]** Missing `await` on Mongoose operations → the promise is returned but not resolved; the calling code proceeds with `undefined` data and no error is thrown, making this a silent bug. Every `.find()`, `.save()`, `.updateOne()`, etc. call must be `await`ed or chained with `.then()`.
|
|
62
|
+
- **[HIGH]** `updateMany` / `updateOne` called without a `` operator → the entire document is replaced with the provided object, destroying all other fields. Always wrap the update payload with `{ : { ... } }` unless a full replacement is intentional.
|
|
63
|
+
- **[HIGH]** `findOneAndUpdate` used without `{ new: true }` → returns the document state before the update by default, so the caller operates on stale data. Pass `{ new: true }` to receive the updated document.
|
|
64
|
+
- **[MEDIUM]** Comparing an ObjectId to a plain string with `===` → always `false` because they are different types; the comparison silently fails without an error. Use `.equals()` method on the ObjectId or convert both sides to strings before comparing.
|
|
65
|
+
- **[MEDIUM]** Mixing `.exec()` calls with direct query resolution (returning the query object without `.exec()`) → inconsistent coding style and subtle differences in error handling. Pick one approach and apply it consistently across the codebase.
|
|
66
|
+
- **[MEDIUM]** Duplicate key error (MongoDB error code 11000) not handled explicitly → a generic 500 Internal Server Error is returned for unique-constraint violations, giving the client no actionable information. Catch `MongoServerError` with code 11000 and return a 409 Conflict with a descriptive message.
|
|
67
|
+
- **[MEDIUM]** `Model.find()` result not checked before treating it as a non-empty set → an empty array `[]` is truthy in JavaScript, so `if (results)` passes even when no documents were found. Check `results.length` or use `findOne` and check for `null`.
|
|
68
|
+
- **[MEDIUM]** Not using `.lean()` for read-only query results → Mongoose document objects are 3–5x slower to create than plain JS objects and consume more memory due to internal tracking. Use `.lean()` for any query result that is only read and serialised, never mutated and saved.
|
|
69
|
+
- **[LOW]** Schema virtual fields not included in `toJSON` output because `toJSON: { virtuals: true }` is not set on the schema → virtuals are silently absent from API responses. Add `{ toJSON: { virtuals: true }, toObject: { virtuals: true } }` to every schema that defines virtuals.
|
|
70
|
+
- **[LOW]** Mongoose model defined without the `mongoose.models.X || mongoose.model()` guard in hot-reload environments (Next.js, Nuxt dev server) → `OverwriteModelError` is thrown on every hot reload. Use `export default mongoose.models.User || mongoose.model("User", schema)`.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Common Bugs & Pitfalls
|
|
75
|
+
|
|
76
|
+
- **[HIGH]** Missing `await` on Mongoose operations → the promise is not resolved; downstream code receives `undefined` and no exception surfaces. This is the single most common Mongoose bug. Lint with `no-floating-promises` or `@typescript-eslint/no-floating-promises`.
|
|
77
|
+
- **[HIGH]** `` vs dot-notation confusion → `{ "items.qty": { : 5 } }` matches documents where any element's `qty` exceeds 5 across the array, while `{ items: { : { qty: { : 5 }, status: "A" } } }` requires both conditions on the same element. Choose the correct form based on whether multi-field element matching is needed.
|
|
78
|
+
- **[HIGH]** Mongoose session not passed to query operations inside a transaction → the query executes outside the transaction, so changes are immediately visible and not rolled back on error. Pass `{ session }` to every query and update call within the transaction callback.
|
|
79
|
+
- **[HIGH]** `Model.save()` called on a document returned by `.lean()` → `.lean()` returns a plain JavaScript object, not a Mongoose document; `.save()` does not exist on it and throws at runtime. Either omit `.lean()` when you need to save, or use `Model.updateOne` with the plain object data.
|
|
80
|
+
- **[MEDIUM]** ObjectId string not validated before being passed to a query → an invalid string causes a Mongoose `CastError` that propagates as a 500 error instead of a 400. Validate with `mongoose.isValidObjectId(id)` before querying and return 400 if invalid.
|
|
81
|
+
- **[MEDIUM]** Stale Mongoose connection in serverless environments (Lambda, Vercel, Netlify Functions) → cold starts create a new connection; warm re-invocations may reuse a closed connection. Use a cached connection pattern: check `mongoose.connection.readyState` before calling `mongoose.connect()`.
|
|
82
|
+
- **[MEDIUM]** Confusion between `findByIdAndDelete` and `deleteOne` → `findByIdAndDelete` triggers `pre/post` remove middleware; `deleteOne` does not. If middleware (audit logs, cascade deletes) depends on the hook, use `findByIdAndDelete`.
|
|
83
|
+
- **[MEDIUM]** `` filter misuse → `: { tags: "old" }` removes the primitive value from an array of primitives, while `: { items: { id: x } }` removes matching sub-documents. Using an object filter on a primitives array or vice versa silently removes nothing.
|
|
84
|
+
- **[LOW]** `Schema.Types.ObjectId` `ref` string must match the registered model name exactly (case-sensitive) → `ref: "user"` fails if the model was registered as `"User"`; `.populate()` throws a `MissingSchemaError`. Always match the exact string passed to `mongoose.model()`.
|
|
85
|
+
- **[LOW]** Aggregation `` with `_id: null` to compute totals returns an empty array when the collection is empty, not `[{ count: 0 }]` → accessing `result[0].count` throws. Check `result.length` before accessing the first element, or use `` in a subsequent `` stage.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# MonoGame — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `MonoGame`, `Microsoft.Xna.Framework`, `Game`, `SpriteBatch`, `ContentManager`, `GameTime`, `*.mgcb`, `*.xnb`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[MEDIUM]** Content pipeline loading asset paths from user-provided input → directory traversal enabling reads outside the content directory; validate and canonicalize all paths before passing to `ContentManager.Load<T>()`.
|
|
10
|
+
- **[MEDIUM]** Save game data deserialized from disk without schema validation → malformed or tampered save files cause crashes or unexpected game state; validate all fields against expected ranges and types after loading.
|
|
11
|
+
- **[MEDIUM]** Online feature API keys or backend URLs embedded in the compiled assembly → decompilable with ILSpy or dnSpy; load secrets from environment variables or a server-side configuration endpoint at runtime.
|
|
12
|
+
- **[LOW]** Network multiplayer accepting game action packets without server-side authority → cheating via crafted packets; validate all game state changes on an authoritative server and reject invalid moves.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Performance
|
|
17
|
+
- **[HIGH]** `SpriteBatch.Begin()` and `SpriteBatch.End()` called for each individual sprite instead of grouping sprites by texture atlas → each Begin/End pair flushes the GPU batch; group all sprites sharing a texture into a single Begin/End block.
|
|
18
|
+
- **[HIGH]** New `Texture2D`, `SoundEffect`, or `RenderTarget2D` objects created inside `Update()` or `Draw()` every frame → GC pressure causing periodic frame hitches; create all resources at load time and reuse them.
|
|
19
|
+
- **[HIGH]** `ContentManager.Load<T>("assetName")` called every frame → MonoGame's ContentManager does cache, but the overhead of repeated lookups and any missed caching causes unnecessary work; cache the returned asset reference in a field after the first load.
|
|
20
|
+
- **[MEDIUM]** `SpriteSortMode.Immediate` used when `SpriteSortMode.Deferred` is sufficient → Immediate mode flushes the batch on every `SpriteBatch.Draw()` call; use `Deferred` (the default) unless custom shader switching between sprites is required.
|
|
21
|
+
- **[MEDIUM]** `Game.IsFixedTimeStep` left as `true` with a mismatch between target elapsed time and monitor refresh rate → inconsistent timing; set `TargetElapsedTime` to match the display refresh rate or disable fixed timestep and use `gameTime.ElapsedGameTime` for variable timestep.
|
|
22
|
+
- **[LOW]** Audio `SoundEffectInstance` objects not stopped and disposed after use → audio resources accumulate; explicitly call `Stop()` and `Dispose()` on instances that are no longer needed.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
- **[HIGH]** All game logic, rendering, input handling, and state management concentrated in `Game1.cs` → monolithic; decompose into `GameComponent` / `DrawableGameComponent` subclasses and dedicated manager classes (InputManager, SceneManager, AudioManager).
|
|
28
|
+
- **[MEDIUM]** `GameComponent` and `DrawableGameComponent` not used for modular subsystems → ad-hoc manager classes manually called each frame; derive from `GameComponent` (update-only) or `DrawableGameComponent` (update + draw) and register with `Components.Add()`.
|
|
29
|
+
- **[MEDIUM]** Game screens (menu, gameplay, pause, credits) managed with ad-hoc boolean flags instead of a state machine or screen manager → spaghetti transition logic; implement a `GameScreen` base class with a `ScreenManager` that maintains a stack of active screens.
|
|
30
|
+
- **[MEDIUM]** Content loading mixed with game initialization in the same method → assets load on every state transition; separate `LoadContent()` for one-time asset loading from screen-specific setup in `Initialize()`.
|
|
31
|
+
- **[LOW]** No separation between game world update logic and MonoGame's `Update(GameTime)` lifecycle → pure game logic is MonoGame-coupled and untestable; extract domain logic into framework-agnostic classes called from `Update()`.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Code Quality
|
|
36
|
+
- **[HIGH]** `Texture2D`, `SpriteBatch`, `SoundEffect`, `RenderTarget2D`, and `Effect` instances not disposed when no longer needed → unmanaged GPU and audio resources leak; implement `IDisposable` on any class holding MonoGame resources and call `Dispose()` on shutdown or scene transition.
|
|
37
|
+
- **[MEDIUM]** Movement, animation speed, or physics not scaled by `gameTime.ElapsedGameTime.TotalSeconds` → frame-rate-dependent behavior; always multiply per-frame changes by elapsed seconds for consistent speed across frame rates.
|
|
38
|
+
- **[MEDIUM]** Screen resolution and safe area hardcoded as numeric literals instead of queried from `GraphicsDevice.Viewport` → breaks on different screen sizes; read dimensions from `GraphicsDevice.Viewport.Width` / `Height` and support `GraphicsDeviceManager` changes.
|
|
39
|
+
- **[MEDIUM]** `SpriteBatch.Draw()` used without specifying a `layerDepth` when `SpriteSortMode.BackToFront` or `FrontToBack` is used → undefined draw order; always provide explicit layer depths when using depth-sorted draw modes.
|
|
40
|
+
- **[LOW]** `ContentManager.Unload()` not called when transitioning between major scenes → assets from the old scene remain in memory; call `Content.Unload()` after a scene transition and reload only the assets needed for the new scene.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Common Bugs & Pitfalls
|
|
45
|
+
- **[HIGH]** `SpriteBatch.Draw()` called outside of a `SpriteBatch.Begin()` / `SpriteBatch.End()` block → throws `InvalidOperationException: SpriteBatch.Begin must be called before SpriteBatch.Draw`; always structure draw calls within matched Begin/End pairs.
|
|
46
|
+
- **[HIGH]** `base.LoadContent()` not called first within an overridden `LoadContent()` → base class content loading skipped, causing NullReferenceException on built-in resources; always call `base.LoadContent()` as the first statement.
|
|
47
|
+
- **[HIGH]** `base.Update(gameTime)` or `base.Draw(gameTime)` not called in overrides → registered `GameComponent` objects not updated or drawn by the framework; always call the base method unless explicitly managing components manually.
|
|
48
|
+
- **[MEDIUM]** `Update()` running faster than `Draw()` when `IsFixedTimeStep = true` and frame rate drops → multiple `Update()` calls per `Draw()`, causing input events to be processed multiple times; account for this in input handling by consuming input state once per draw cycle.
|
|
49
|
+
- **[MEDIUM]** Sprite rotation origin defaulting to `Vector2.Zero` (top-left) instead of the sprite's center → rotation pivots from the wrong point; pass `new Vector2(texture.Width / 2f, texture.Height / 2f)` as the origin for centered rotation.
|
|
50
|
+
- **[MEDIUM]** Content built for one platform (Windows `xnb`) deployed to another (Android, iOS) → content pipeline output is platform-specific; build content for each target platform separately using the MGCB Editor's platform settings.
|
|
51
|
+
- **[LOW]** `Random` instantiated with `new Random()` inside a method called every frame → same seed can be generated multiple times per millisecond producing identical sequences; create a single `Random` instance and reuse it throughout the game lifetime.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# MySQL — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `mysql`, `mysql2`, `from 'mysql2'`, `PyMySQL`, `DATABASE_URL` with `mysql://`, `*.sql` with MySQL syntax, `InnoDB`, `SHOW TABLES`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[CRITICAL]** SQL injection via string concatenation instead of prepared statements → attacker can read, modify, or drop any data. Use `?` placeholders with parameterized queries in all drivers (`mysql2`, `PyMySQL`).
|
|
10
|
+
- **[CRITICAL]** Root MySQL user used for application connections → full server access on credential compromise. Create a dedicated application user with `GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.*` only.
|
|
11
|
+
- **[HIGH]** `FILE` privilege granted to application DB user → allows reading arbitrary server files via `LOAD DATA INFILE`. Revoke `FILE` privilege; it is never needed by application code.
|
|
12
|
+
- **[HIGH]** `LOAD DATA INFILE` used with user-controlled file paths → arbitrary file read from server filesystem. Validate and allowlist all paths; prefer inserting data via application logic.
|
|
13
|
+
- **[HIGH]** Relying solely on `mysql_real_escape_string` for injection prevention → charset-based bypasses exist with certain encodings. Use prepared statements exclusively; escaping is not a sufficient substitute.
|
|
14
|
+
- **[MEDIUM]** `general_log` enabled in production → all queries including those containing passwords or PII written to log file. Disable `general_log` in production; use `slow_query_log` for performance diagnostics only.
|
|
15
|
+
- **[MEDIUM]** Remote root login not disabled → exposed MySQL port allows brute-force root access. Run `DELETE FROM mysql.user WHERE User='root' AND Host != 'localhost'` and `FLUSH PRIVILEGES`.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Performance
|
|
20
|
+
- **[HIGH]** Missing indexes on columns used in `WHERE`, `JOIN ON`, or `ORDER BY` clauses → full table scan on every query, degrading linearly with row count. Run `EXPLAIN` on all slow queries and add indexes on unindexed filter columns.
|
|
21
|
+
- **[HIGH]** `SELECT *` used instead of explicit column list → over-fetches data, prevents covering indexes, and breaks when schema changes. List only required columns in every SELECT.
|
|
22
|
+
- **[HIGH]** `OFFSET` pagination on large tables (e.g., `LIMIT 20 OFFSET 10000`) → MySQL scans and discards offset rows on every page request. Use keyset pagination: `WHERE id > last_seen_id LIMIT 20`.
|
|
23
|
+
- **[MEDIUM]** `MyISAM` storage engine still used → no transaction support, table-level locking blocks concurrent writes. Migrate all tables to `InnoDB` (`ALTER TABLE t ENGINE=InnoDB`).
|
|
24
|
+
- **[MEDIUM]** Not running `EXPLAIN` or `EXPLAIN FORMAT=JSON` on queries before deploying → performance regressions not caught until production load. Add query analysis to the PR review checklist for any new or modified queries.
|
|
25
|
+
- **[MEDIUM]** `ORDER BY RAND()` used for random row selection → forces full table sort; extremely slow on large tables. Use `WHERE id >= FLOOR(RAND() * (SELECT MAX(id) FROM t)) LIMIT 1` or pre-compute random sets.
|
|
26
|
+
- **[LOW]** Large `TEXT` or `BLOB` columns in frequently queried tables → increases row size, reduces rows per page, slows full-table operations. Store large content in object storage; keep only a reference URL in the DB.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Architecture
|
|
31
|
+
- **[HIGH]** No foreign key constraints defined → orphaned child records accumulate silently; referential integrity broken. Define `FOREIGN KEY` constraints with explicit `ON DELETE` and `ON UPDATE` behavior on all relationships.
|
|
32
|
+
- **[HIGH]** Structured data serialized as JSON in `TEXT` columns without using MySQL's `JSON` type → no schema validation, no index support, no partial updates. Use MySQL `JSON` column type with generated columns for indexable fields.
|
|
33
|
+
- **[MEDIUM]** Multi-statement operations (e.g., insert + update + deduct balance) not wrapped in a transaction → partial failure leaves data in inconsistent state. Wrap related mutations in `START TRANSACTION` / `COMMIT` with `ROLLBACK` on error.
|
|
34
|
+
- **[MEDIUM]** Database charset not set to `utf8mb4` at server, database, and table level → 4-byte characters (emoji, certain CJK) cause truncation or `Incorrect string value` errors. Set `CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci` at all three levels.
|
|
35
|
+
- **[MEDIUM]** Read replicas not used for reporting or analytics queries → heavy read queries compete with write traffic on primary. Route `SELECT` queries for reports to a read replica using separate connection.
|
|
36
|
+
- **[LOW]** No defined archival or partitioning strategy for high-volume tables → tables grow unbounded, slowing all queries. Implement `PARTITION BY RANGE` on date columns or periodic archival to cold storage.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Code Quality
|
|
41
|
+
- **[HIGH]** `DATETIME` used instead of `TIMESTAMP` for timezone-aware data (or vice versa without understanding the difference) → `TIMESTAMP` auto-converts to UTC but has 2038 overflow; `DATETIME` stores literally. Explicitly choose: `TIMESTAMP` for UTC-stored times, `DATETIME` for calendar values.
|
|
42
|
+
- **[HIGH]** Passwords stored as plaintext or using MD5/SHA1 → trivially reversible on breach. Use bcrypt, scrypt, or Argon2 via the application layer; never hash passwords in SQL.
|
|
43
|
+
- **[MEDIUM]** `INT(11)` display width used as if it were a storage-size constraint → display width is deprecated in MySQL 8 and has no effect on storage. Use `INT`, `BIGINT`, or `TINYINT` without display width specifiers.
|
|
44
|
+
- **[MEDIUM]** Missing `ON DELETE` / `ON UPDATE` clause on foreign key definitions → default behavior is `RESTRICT`, which may not match domain intent. Explicitly declare the desired referential action on every foreign key.
|
|
45
|
+
- **[MEDIUM]** Schema changes deployed without using a migration tool (Flyway, Liquibase, or similar) → environments diverge; no rollback path. Commit all schema changes as versioned migration files.
|
|
46
|
+
- **[LOW]** `ENUM` columns changed by adding values in the middle of the list → alters the underlying integer representation, corrupting existing data. Only append new values to the end of an ENUM list, or use a lookup table instead.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Common Bugs & Pitfalls
|
|
51
|
+
- **[HIGH]** `utf8` charset used instead of `utf8mb4` → MySQL's `utf8` is a 3-byte encoding; 4-byte characters (emoji, some CJK) are silently truncated or cause insertion errors. Migrate all columns, tables, and the connection charset to `utf8mb4`.
|
|
52
|
+
- **[HIGH]** Column defined with `DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP` unexpectedly updates on unrelated writes → every UPDATE on the row changes the timestamp even if that column is not intended to track modification. Remove `ON UPDATE` from columns that should not auto-update.
|
|
53
|
+
- **[MEDIUM]** `GROUP BY` without strict mode (`ONLY_FULL_GROUP_BY` disabled) → non-aggregated columns silently return an arbitrary row's value. Enable `ONLY_FULL_GROUP_BY` in `sql_mode` and fix all queries that rely on the lenient behavior.
|
|
54
|
+
- **[MEDIUM]** Case-insensitive collation (`utf8mb4_general_ci`) causing unexpected equality matches (e.g., `'a' = 'A'`, or `'cafe' = 'café'`) → uniqueness constraints may not work as expected. Choose collation deliberately: `utf8mb4_bin` for case-sensitive, `utf8mb4_unicode_ci` for case-insensitive with correct Unicode rules.
|
|
55
|
+
- **[MEDIUM]** Reserved words used as column or table names without backtick quoting (e.g., `order`, `key`, `rank`) → syntax errors or incorrect query parsing. Quote reserved words with backticks or rename the identifiers.
|
|
56
|
+
- **[LOW]** `AUTO_INCREMENT` counter not reset after bulk delete → ID gaps mislead users into thinking records were deleted. Gaps are normal; document this and never expose raw IDs as business-meaningful order indicators.
|
|
57
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# NestJS — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `@Module(`, `@Injectable(`, `@Controller(`, `NestFactory`, `nest-cli.json`, `@nestjs/` in deps, `@nestjs/microservices`, `@nestjs/event-emitter`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** Route handler missing `@UseGuards(AuthGuard)` on protected endpoints → unauthenticated access.
|
|
11
|
+
- **[CRITICAL]** Missing `ValidationPipe` globally → raw unvalidated user input reaches business logic. Use `app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }))`.
|
|
12
|
+
- **[CRITICAL]** Raw SQL query with string interpolation of user input → SQL injection. Always use parameterized queries or ORM.
|
|
13
|
+
- **[CRITICAL]** Mass assignment via `@Body()` mapped directly to entity without DTO → over-posting vulnerability.
|
|
14
|
+
- **[HIGH]** Returning TypeORM/Prisma entity directly from controller → exposes all fields including passwords. Map to response DTO.
|
|
15
|
+
- **[HIGH]** Missing `@Roles()` decorator with `RolesGuard` on admin endpoints → privilege escalation.
|
|
16
|
+
- **[HIGH]** No rate limiting on auth endpoints (login, register, password reset) → brute force. Use `@nestjs/throttler`.
|
|
17
|
+
- **[HIGH]** Secrets/config hardcoded in source instead of `ConfigService` → exposed in version control.
|
|
18
|
+
- **[HIGH]** JWT secret too short or using default value → token forging.
|
|
19
|
+
- **[HIGH]** `@Param('id')` used in ownership check without verifying the resource belongs to authenticated user → IDOR.
|
|
20
|
+
- **[HIGH]** File upload without MIME type validation and storage path sanitization → arbitrary file upload.
|
|
21
|
+
- **[MEDIUM]** Missing CORS configuration or `origin: '*'` in production.
|
|
22
|
+
- **[MEDIUM]** Error messages leaking stack traces → use exception filters to sanitize.
|
|
23
|
+
- **[MEDIUM]** `@nestjs/jwt` refresh token not invalidated on logout → stolen token reuse.
|
|
24
|
+
- **[LOW]** Swagger UI accessible in production without auth protection → API schema exposed.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Performance
|
|
29
|
+
|
|
30
|
+
- **[HIGH]** N+1 query in repository — loading relations in loop instead of TypeORM `relations` / Prisma `include`.
|
|
31
|
+
- **[HIGH]** Synchronous/blocking operations in async handler → blocks event loop. Use worker threads or queue.
|
|
32
|
+
- **[HIGH]** Missing database indexes on `WHERE`, `ORDER BY`, `JOIN` columns.
|
|
33
|
+
- **[HIGH]** Missing pagination on list endpoints → unbounded response and memory.
|
|
34
|
+
- **[HIGH]** Global interceptors running expensive operations on every request without caching.
|
|
35
|
+
- **[HIGH]** `@nestjs/bull` / `@nestjs/bullmq` jobs not using concurrency limits → worker saturation.
|
|
36
|
+
- **[MEDIUM]** Missing `CacheModule` / `@Cacheable()` for expensive repeated reads.
|
|
37
|
+
- **[MEDIUM]** Loading full dataset into memory when only count/aggregate needed → use `count()` query.
|
|
38
|
+
- **[MEDIUM]** TypeORM lazy relations causing hidden N+1 via property access.
|
|
39
|
+
- **[MEDIUM]** Missing connection pool configuration → default too small under load.
|
|
40
|
+
- **[MEDIUM]** `findOne` vs `findOneOrFail` → choose based on whether absence is expected or error.
|
|
41
|
+
- **[LOW]** Unused interceptors/guards running on every request.
|
|
42
|
+
- **[LOW]** `@EventEmitter2` with synchronous listeners blocking request pipeline.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Architecture
|
|
47
|
+
|
|
48
|
+
- **[HIGH]** Business logic in controller class → controllers handle HTTP only; move to service.
|
|
49
|
+
- **[HIGH]** Circular dependency between modules without `forwardRef` → instantiation error.
|
|
50
|
+
- **[HIGH]** Missing module declaration for provider → `Cannot determine dependencies` error at runtime.
|
|
51
|
+
- **[HIGH]** Service calling another service via HTTP (internal) instead of direct injection → unnecessary network hop.
|
|
52
|
+
- **[HIGH]** Domain logic in repository layer → repositories handle data access only, not business rules.
|
|
53
|
+
- **[HIGH]** Not using CQRS (`@nestjs/cqrs`) for complex domains → command/query mixed causing tangled services.
|
|
54
|
+
- **[HIGH]** Microservice event handlers not idempotent → duplicate processing on retry.
|
|
55
|
+
- **[MEDIUM]** God service >500 lines → split by domain responsibility (SRP).
|
|
56
|
+
- **[MEDIUM]** Repository logic (query building) in service class → separate concerns.
|
|
57
|
+
- **[MEDIUM]** Missing exception filter → unhandled exceptions expose stack traces in production.
|
|
58
|
+
- **[MEDIUM]** `any` type on DTO fields → defeats TypeScript and class-validator.
|
|
59
|
+
- **[MEDIUM]** Not using NestJS lifecycle hooks (`OnApplicationBootstrap`, `OnModuleDestroy`) for startup/shutdown.
|
|
60
|
+
- **[MEDIUM]** Event-driven patterns using `@nestjs/event-emitter` for critical flows that need transaction guarantee → use database-backed queues.
|
|
61
|
+
- **[LOW]** Module not feature-scoped (everything in AppModule).
|
|
62
|
+
- **[LOW]** Not using `@nestjs/config` with `ConfigModule.forRoot({ validate })` → no startup validation of env vars.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Code Quality
|
|
67
|
+
|
|
68
|
+
- **[HIGH]** Missing `async`/`await` on repository/service calls → unhandled promise, silent data loss.
|
|
69
|
+
- **[HIGH]** Missing class-validator decorators on DTO → no input validation even with ValidationPipe.
|
|
70
|
+
- **[HIGH]** DTO class not using `class-transformer` decorators (`@Type`, `@Transform`) with `transform: true` → nested objects not transformed.
|
|
71
|
+
- **[HIGH]** Not using `plainToInstance` + `validate` for manual DTO transformation outside request pipeline.
|
|
72
|
+
- **[MEDIUM]** Property injection (`@Inject()` on property) instead of constructor injection → harder to test.
|
|
73
|
+
- **[MEDIUM]** Missing `@ApiProperty()` / `@ApiResponse()` Swagger decorators → undocumented API.
|
|
74
|
+
- **[MEDIUM]** Inconsistent error response format across controllers.
|
|
75
|
+
- **[MEDIUM]** `@Param()` without `ParseIntPipe` or `ParseUUIDPipe` → string passed to numeric query.
|
|
76
|
+
- **[MEDIUM]** Not using `@nestjs/mapped-types` (`PartialType`, `PickType`) → DTO duplication.
|
|
77
|
+
- **[LOW]** Missing `readonly` on injected dependencies in constructor → accidental reassignment.
|
|
78
|
+
- **[LOW]** `@Get()` / `@Post()` without `@HttpCode()` defaulting to wrong status codes.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Common Bugs & Pitfalls
|
|
83
|
+
|
|
84
|
+
- **[HIGH]** `@Body()` without DTO class → ValidationPipe has nothing to validate, all input passes.
|
|
85
|
+
- **[HIGH]** `await dataSource.initialize()` not awaited → DB not ready, first requests fail.
|
|
86
|
+
- **[HIGH]** `@Param('id')` returns `string` but query expects `number` → silent miss, returns null.
|
|
87
|
+
- **[HIGH]** Exception thrown inside `@EventEmitter2` listener not caught → unhandled rejection crashes process.
|
|
88
|
+
- **[HIGH]** Microservice pattern not handling connection errors → service silently stops consuming.
|
|
89
|
+
- **[MEDIUM]** `onModuleInit` async work not properly awaited → app starts before dependencies ready.
|
|
90
|
+
- **[MEDIUM]** Lifecycle hook (`OnModuleDestroy`) missing → open DB connections on process exit.
|
|
91
|
+
- **[MEDIUM]** `@nestjs/schedule` cron jobs not handling errors → silent failure, job stops running.
|
|
92
|
+
- **[MEDIUM]** Transaction not rolled back on partial failure in multi-step operations.
|
|
93
|
+
- **[MEDIUM]** `ValidationPipe` with `transform: true` but `@Type(() => Number)` missing → string not converted.
|
|
94
|
+
- **[LOW]** `@Optional()` missing on truly optional injected dependencies → crashes at startup.
|
|
95
|
+
- **[LOW]** Health check endpoint not implemented → load balancer can't detect unhealthy instances.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Next.js — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `next.config.*`, `pages/` or `app/` directory, `getServerSideProps`, `getStaticProps`, `from 'next'`, `next/router`, `next/navigation`, `Server Components`, `Server Actions`, `use client`, `use server`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
|
|
10
|
+
- **[CRITICAL]** Server-side secrets (API keys, DB URLs) accessed in Client Components → exposed in JS bundle. Only use in Server Components, Route Handlers, or Server Actions.
|
|
11
|
+
- **[CRITICAL]** `dangerouslySetInnerHTML` with user content → XSS.
|
|
12
|
+
- **[CRITICAL]** Server Action missing authorization check → any authenticated (or unauthenticated) user can invoke with arbitrary data.
|
|
13
|
+
- **[HIGH]** Missing authentication check in Route Handler / API route (`pages/api/`) → unprotected endpoint.
|
|
14
|
+
- **[HIGH]** `NEXT_PUBLIC_` prefix on sensitive env vars → exposed to browser. Only prefix truly public values.
|
|
15
|
+
- **[HIGH]** Redirect URL from query param (`?next=/`) without allowlist validation → open redirect.
|
|
16
|
+
- **[HIGH]** `cookies()` session data used without CSRF protection on mutating Server Actions.
|
|
17
|
+
- **[HIGH]** Server Action accepting file uploads without size/MIME validation → arbitrary file upload.
|
|
18
|
+
- **[HIGH]** IDOR in Server Action / Route Handler — accessing resource by ID without ownership verification.
|
|
19
|
+
- **[HIGH]** `process.env.SECRET` accessed in component that has both server and client rendering paths.
|
|
20
|
+
- **[MEDIUM]** Missing `Content-Security-Policy` header in `next.config.js`.
|
|
21
|
+
- **[MEDIUM]** `cookies()` / `headers()` data from Server Component passed as prop to Client Component → visible in RSC payload over network.
|
|
22
|
+
- **[MEDIUM]** `revalidatePath('/')` after sensitive mutation invalidating too broadly → cached sensitive data regenerated.
|
|
23
|
+
- **[LOW]** `rel="noopener noreferrer"` missing on external `target="_blank"` links.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Performance
|
|
28
|
+
|
|
29
|
+
- **[HIGH]** `getServerSideProps` used when `getStaticProps` + ISR would suffice → every request hits server, higher TTFB.
|
|
30
|
+
- **[HIGH]** Large Client Component importing heavy libraries that could stay server-side → increases client bundle.
|
|
31
|
+
- **[HIGH]** Missing `next/image` for images → no optimization, LCP degraded.
|
|
32
|
+
- **[HIGH]** `'use client'` on root layout/page → entire subtree is client-side, kills Server Component benefits.
|
|
33
|
+
- **[HIGH]** Fetching same data in multiple Server Components without `cache()` / `fetch` deduplication → redundant DB calls per request.
|
|
34
|
+
- **[HIGH]** Not using Partial Prerendering (PPR) or `<Suspense>` for dynamic sections → entire page deferred.
|
|
35
|
+
- **[HIGH]** `app/` route fetching in `useEffect` instead of Server Component → client-side waterfall.
|
|
36
|
+
- **[MEDIUM]** Missing `loading.tsx` / `<Suspense>` for async Server Components → no streaming, all-or-nothing loading.
|
|
37
|
+
- **[MEDIUM]** `fetch` with `cache: 'no-store'` when data could be cached → bypasses Next.js data cache.
|
|
38
|
+
- **[MEDIUM]** Missing `next/font` for custom fonts → layout shift (CLS), FOUT.
|
|
39
|
+
- **[MEDIUM]** `generateStaticParams` not used for dynamic routes with known values → falls back to SSR.
|
|
40
|
+
- **[MEDIUM]** Multiple unrelated `fetch` calls not parallelized via `Promise.all()` in Server Component → sequential.
|
|
41
|
+
- **[LOW]** `next/dynamic` with `ssr: false` not used for browser-only components (charts, maps) → SSR error.
|
|
42
|
+
- **[LOW]** Not using `unstable_cache` for non-fetch data sources (DB queries) in Server Components.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Architecture
|
|
47
|
+
|
|
48
|
+
- **[HIGH]** Mixing server-only and client-only logic in same component → use `server-only` / `client-only` packages.
|
|
49
|
+
- **[HIGH]** Data fetching in Client Component when Server Component could fetch directly → unnecessary client-server round trip.
|
|
50
|
+
- **[HIGH]** Server Action doing multiple mutations without transaction → partial failure leaves inconsistent state.
|
|
51
|
+
- **[HIGH]** Not using Route Handlers for external webhook endpoints → Server Actions not suitable for non-browser callers.
|
|
52
|
+
- **[MEDIUM]** Deeply nested `params`/`searchParams` drilling instead of `useSearchParams()` or server-side access.
|
|
53
|
+
- **[MEDIUM]** `pages/api/` routes used in App Router project → migrate to Route Handlers (`app/api/route.ts`).
|
|
54
|
+
- **[MEDIUM]** `useRouter().push()` for external URLs → use `window.location.href` or `<a href>`.
|
|
55
|
+
- **[MEDIUM]** `layout.tsx` fetching per-request data → layouts are cached, use `page.tsx` for dynamic data.
|
|
56
|
+
- **[MEDIUM]** Not using `@` path alias configured in `tsconfig.json` → deep relative imports `../../../../`.
|
|
57
|
+
- **[LOW]** Missing `error.tsx` boundary for error handling in App Router segments.
|
|
58
|
+
- **[LOW]** `_app.tsx` still present in App Router project → dead file causing confusion.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Code Quality
|
|
63
|
+
|
|
64
|
+
- **[HIGH]** `'use client'` on file that only uses server features → unnecessary client bundle.
|
|
65
|
+
- **[HIGH]** Missing `generateMetadata` or `export const metadata` on public pages → poor SEO.
|
|
66
|
+
- **[HIGH]** `notFound()` / `redirect()` called inside `try/catch` → these throw internally, catch swallows them.
|
|
67
|
+
- **[MEDIUM]** `useEffect` logic that could be in Server Component → unnecessary client-side complexity.
|
|
68
|
+
- **[MEDIUM]** `router.push` vs `router.replace` not chosen semantically (replace for login redirect).
|
|
69
|
+
- **[MEDIUM]** `searchParams` not typed correctly in Next.js 15+ (async) → `Promise<{ [key: string]: string }>`.
|
|
70
|
+
- **[MEDIUM]** Not using `next-safe-action` or similar for type-safe Server Actions.
|
|
71
|
+
- **[LOW]** Missing `not-found.tsx` for custom 404 pages.
|
|
72
|
+
- **[LOW]** Image `alt` prop missing on `next/image` → accessibility violation.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Common Bugs & Pitfalls
|
|
77
|
+
|
|
78
|
+
- **[HIGH]** `cookies()` / `headers()` called in static context (outside request) → throws at build time.
|
|
79
|
+
- **[HIGH]** Async Server Component not awaited in parent → renders as Promise object.
|
|
80
|
+
- **[HIGH]** Server Action called from `useEffect` → Server Actions are for user interactions/forms, not effects.
|
|
81
|
+
- **[HIGH]** `revalidatePath()` / `revalidateTag()` not called after mutation → stale data served indefinitely.
|
|
82
|
+
- **[HIGH]** Middleware `matcher` too broad → auth middleware running on static assets → performance hit.
|
|
83
|
+
- **[MEDIUM]** `searchParams` in Server Component not typed as `Promise<>` in Next.js 15+ → breaking change.
|
|
84
|
+
- **[MEDIUM]** `Link` with `href` hash to same page causing full navigation → use `useRouter` scroll behavior.
|
|
85
|
+
- **[MEDIUM]** `params` destructured from `props` synchronously in Next.js 15+ → must be awaited.
|
|
86
|
+
- **[MEDIUM]** Data fetched in `layout.tsx` not available to nested pages without prop drilling or fetch deduplication.
|
|
87
|
+
- **[LOW]** `next/head` used in App Router → use `metadata` export instead.
|
|
88
|
+
- **[LOW]** Environment variable undefined in Edge Runtime (not all Node.js APIs available).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Nginx — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: `nginx.conf`, `nginx`, `server {`, `location /`, `proxy_pass`, `upstream {`, `listen 80`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[CRITICAL]** Default server block (`server_name _;`) serving application content when no SNI match → unintended content served to scanners. Make the default server block return 444 or redirect.
|
|
10
|
+
- **[CRITICAL]** `autoindex on` in web root or uploads directory → full directory listing exposed. Set `autoindex off` globally and only enable where explicitly required.
|
|
11
|
+
- **[HIGH]** Missing security headers: `X-Frame-Options`, `X-Content-Type-Options`, `Strict-Transport-Security` → clickjacking, MIME sniffing, and downgrade attacks. Add these headers in the `server` block.
|
|
12
|
+
- **[HIGH]** `proxy_pass` to backend without `proxy_set_header Host $host` → backend receives wrong host, enabling host header injection. Always set `Host`, `X-Real-IP`, and `X-Forwarded-For` headers.
|
|
13
|
+
- **[HIGH]** `server_tokens on` (default) exposing nginx version → fingerprinting for targeted exploits. Set `server_tokens off` in the `http` block.
|
|
14
|
+
- **[HIGH]** Wildcard `Access-Control-Allow-Origin: *` added to authenticated API endpoints → any origin can make credentialed requests. Restrict to specific allowed origins using a `map` directive.
|
|
15
|
+
- **[MEDIUM]** SSL configuration allowing TLS 1.0 or 1.1 → connection vulnerable to POODLE and BEAST. Set `ssl_protocols TLSv1.2 TLSv1.3` and use a modern cipher suite.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Performance
|
|
20
|
+
- **[HIGH]** `gzip off` or gzip not configured for compressible content types → large text responses sent uncompressed. Enable gzip with `gzip on` and set `gzip_types` for HTML, CSS, JS, JSON, and SVG.
|
|
21
|
+
- **[HIGH]** `keepalive_timeout 0` → a new TCP connection is established per request. Set a reasonable `keepalive_timeout` (e.g., 65s) to allow connection reuse.
|
|
22
|
+
- **[HIGH]** Static assets served without `expires` or `Cache-Control` headers → browsers re-fetch unchanged assets on every page load. Add `expires 1y` and `add_header Cache-Control "public, immutable"` for fingerprinted assets.
|
|
23
|
+
- **[MEDIUM]** `worker_processes 1` hardcoded → nginx does not use all available CPU cores. Set `worker_processes auto` to match the CPU count.
|
|
24
|
+
- **[MEDIUM]** `proxy_buffering off` for large upstream responses → responses streamed through nginx holding connections open and increasing memory pressure. Enable buffering and tune `proxy_buffer_size`.
|
|
25
|
+
- **[MEDIUM]** `worker_connections` set too low → connection limit reached under moderate load. Set to at least 1024 and tune based on `ulimit -n`.
|
|
26
|
+
- **[LOW]** `sendfile off` → file data copied through user space. Set `sendfile on` with `tcp_nopush on` for efficient static file serving.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Architecture
|
|
31
|
+
- **[HIGH]** Monolithic `nginx.conf` with all server blocks inline → hard to manage multiple sites or services. Use `include /etc/nginx/conf.d/*.conf` and separate files per virtual host or service.
|
|
32
|
+
- **[HIGH]** Upstream backends defined as hardcoded IPs without an `upstream {}` block → no load balancing, health checks, or failover. Define named `upstream` blocks with multiple server entries.
|
|
33
|
+
- **[MEDIUM]** Rate limiting not applied to authentication or sensitive API endpoints → brute-force and credential stuffing attacks unconstrained. Add `limit_req_zone` and `limit_req` directives to login and API routes.
|
|
34
|
+
- **[MEDIUM]** Complex conditional routing implemented with `if` blocks → `if` inside `location` is fragile in nginx. Replace with `map` directives or separate `location` blocks.
|
|
35
|
+
- **[MEDIUM]** Access and error logs not directed to separate files per virtual host → all traffic mixed, hard to debug. Set `access_log` and `error_log` per `server` block.
|
|
36
|
+
- **[LOW]** Log rotation not configured for nginx logs → disk space exhaustion over time. Configure `logrotate` or use Docker log drivers with size limits.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Code Quality
|
|
41
|
+
- **[HIGH]** `try_files` misconfigured: `try_files $uri $uri/ /index.html =404` — the `=404` fallback is never reached because `/index.html` always matches → 404 pages served as 200 with HTML content. Verify `try_files` order matches the intended fallback behaviour.
|
|
42
|
+
- **[MEDIUM]** `location` block order not documented or intentional → more specific locations accidentally shadowed by broader ones. Add comments explaining match precedence; prefer prefix locations over regex where possible.
|
|
43
|
+
- **[MEDIUM]** Custom `error_page` not defined for 404 and 50x errors → nginx default error pages shown to users. Define `error_page 404 /404.html` and `error_page 500 502 503 504 /50x.html`.
|
|
44
|
+
- **[MEDIUM]** `proxy_read_timeout` and `proxy_connect_timeout` left at defaults → either too short (premature 504) or too long (connections held open). Tune timeouts to match upstream SLAs.
|
|
45
|
+
- **[LOW]** Config not validated with `nginx -t` before reload → syntax errors cause nginx reload failure and potential downtime. Run `nginx -t` in CI and before any production config change.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Common Bugs & Pitfalls
|
|
50
|
+
- **[HIGH]** `proxy_pass http://backend/` with a trailing slash → nginx strips the `location` prefix from the URI before forwarding, causing unexpected path rewriting. Remove the trailing slash unless prefix stripping is intended.
|
|
51
|
+
- **[HIGH]** WebSocket proxying missing `proxy_http_version 1.1`, `Upgrade`, and `Connection` headers → WebSocket connections fall back to HTTP and fail. Add all three directives inside the WebSocket `location` block.
|
|
52
|
+
- **[MEDIUM]** `limit_req_zone` defined in the `http` block but `limit_req` directive applied only in a nested `location` that does not inherit it → rate limiting not effective. Verify `limit_req` is in the correct `location` block and the zone name matches.
|
|
53
|
+
- **[MEDIUM]** SNI-based virtual hosting (`server_name`) not working with clients lacking SNI support → requests routed to wrong virtual host. Accept this as a known limitation and ensure the default server block is safe.
|
|
54
|
+
- **[MEDIUM]** `add_header` in a nested `location` block does not inherit `add_header` directives from the parent `server` block → security headers missing on specific routes. Repeat all required headers in every block that uses `add_header`.
|
|
55
|
+
- **[LOW]** `client_max_body_size` not set for file upload endpoints → nginx rejects uploads over 1MB with 413. Set `client_max_body_size` to a value appropriate for the maximum expected upload size.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Node.js — Stack-Specific Review Rules
|
|
2
|
+
|
|
3
|
+
> Applies to: GR · SR · PR · AR · BR
|
|
4
|
+
> Detection signals: node, package.json without framework, require(, process.env, http.createServer, fs., path., __dirname
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Security
|
|
9
|
+
- **[CRITICAL]** eval() or new Function(userInput) with user-controlled input → arbitrary code execution on the server. Never eval user input; use a sandboxed evaluator or restructure the logic to avoid dynamic code.
|
|
10
|
+
- **[CRITICAL]** child_process.exec(userInput) without sanitization → attacker injects shell metacharacters to run arbitrary commands. Use child_process.execFile() with an argument array, or use spawn() with shell: false.
|
|
11
|
+
- **[HIGH]** path.join() with user input used in file operations without normalization → path traversal allows reading or writing files outside the intended directory. Resolve the path and verify it starts with the expected base directory.
|
|
12
|
+
- **[HIGH]** Prototype pollution via Object.assign({}, req.body) or similar patterns → attacker adds __proto__ properties to poison the object prototype, affecting all downstream code. Use Object.create(null) or a validated schema for body parsing.
|
|
13
|
+
- **[CRITICAL]** require(userInput) or dynamic require with user-controlled values → arbitrary module loading gives attacker code execution. Whitelist allowed module names or use a static import map.
|
|
14
|
+
- **[HIGH]** fs.readFile(userPath) without path sanitization or whitelisting → attacker reads arbitrary files including sensitive config or credentials. Resolve the full path and verify it falls within the allowed directory.
|
|
15
|
+
- **[HIGH]** Missing HTTPS in production → credentials and session tokens transmitted in plaintext. Terminate TLS at the server or a load balancer and redirect all HTTP traffic to HTTPS.
|
|
16
|
+
- **[MEDIUM]** Session secrets hardcoded in source code → secrets exposed via version control. Load secrets from environment variables or a secrets manager; never commit them to the repository.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Performance
|
|
21
|
+
- **[CRITICAL]** Synchronous fs calls (readFileSync, writeFileSync, statSync) in request handlers → event loop blocked for the duration of the I/O, degrading all concurrent requests. Replace all synchronous fs calls with async equivalents (fs.promises.readFile, etc.).
|
|
22
|
+
- **[HIGH]** Unhandled promise rejections causing silent failures → failed async operations go unnoticed, leaving the application in an inconsistent state. Always add .catch() to promise chains or use try/catch with async/await.
|
|
23
|
+
- **[HIGH]** setInterval() without clearInterval() in request or module scope → timers accumulate over time, causing a gradual memory leak. Always store timer references and clear them when the associated resource is destroyed.
|
|
24
|
+
- **[MEDIUM]** Large Buffer allocations not released after use → GC pressure increases under load as large buffers are held longer than necessary. Release buffers explicitly or use streams for large data transfers.
|
|
25
|
+
- **[HIGH]** EventEmitter listeners added on every request without removeListener → listener count grows indefinitely, triggering MaxListenersExceededWarning and causing memory leaks. Add listeners once and clean them up appropriately.
|
|
26
|
+
- **[MEDIUM]** Not using streams for reading or writing large files → entire file loaded into memory, causing OOM on large uploads or downloads. Use fs.createReadStream/createWriteStream and pipe() for file I/O.
|
|
27
|
+
- **[HIGH]** Missing error handling in middleware pipeline → uncaught errors bypass the error middleware, returning raw stack traces to clients. Ensure all async middleware wraps operations in try/catch and calls next(err).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Architecture
|
|
32
|
+
- **[HIGH]** HTTP server logic, business logic, and database calls in one file → code becomes unmaintainable and untestable. Separate into layers: router, controller, service, repository.
|
|
33
|
+
- **[HIGH]** Mixing callbacks with async/await inconsistently → callback-style error handling bypassed when async/await used without try/catch, causing silent failures. Adopt async/await throughout and promisify callback APIs with util.promisify.
|
|
34
|
+
- **[HIGH]** Global state mutated across modules (e.g., module-level variables modified per request) → race conditions under concurrent requests corrupt shared state. Use request-scoped state (req object or cls-hooked context) rather than globals.
|
|
35
|
+
- **[MEDIUM]** Missing graceful shutdown handler for SIGTERM and SIGINT → in-flight requests dropped and database connections not released on process exit. Implement signal handlers that close servers and DB pools before exiting.
|
|
36
|
+
- **[MEDIUM]** Not organizing code into modules with clear boundaries → dependencies between unrelated parts of the codebase make refactoring risky. Define module boundaries and expose only what is needed via explicit exports.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Code Quality
|
|
41
|
+
- **[MEDIUM]** var used instead of const/let → function-scoped var causes accidental hoisting bugs and leaks variables into wider scopes. Use const by default and let when reassignment is needed.
|
|
42
|
+
- **[HIGH]** Missing process.on(uncaughtException) and process.on(unhandledRejection) handlers → unhandled errors crash the process without logging or cleanup. Register global handlers that log the error and gracefully exit.
|
|
43
|
+
- **[MEDIUM]** Using callback-based APIs without util.promisify when async/await is used elsewhere → inconsistent error handling style leads to swallowed errors. Wrap callback-based APIs with util.promisify or native promise wrappers.
|
|
44
|
+
- **[MEDIUM]** console.log for production logging instead of a structured logger (pino, winston) → logs are unstructured and cannot be queried by log aggregation tools. Replace console.log with a structured logger configured for the environment.
|
|
45
|
+
- **[LOW]** Not linting with ESLint or a strict TypeScript config → common bugs and style inconsistencies go uncaught before code review. Configure ESLint with recommended rules and enforce it in CI.
|
|
46
|
+
- **[MEDIUM]** Hardcoded port numbers and hostnames instead of environment variables → deploying to different environments requires code changes. Read server configuration from process.env with documented defaults.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Common Bugs & Pitfalls
|
|
51
|
+
- **[HIGH]** process.exit() called without waiting for async cleanup → open DB connections, in-flight HTTP requests, and pending writes are abandoned. Use a graceful shutdown sequence that drains connections before calling process.exit().
|
|
52
|
+
- **[MEDIUM]** EventEmitter max listeners warning appearing in logs → more listeners than the default limit are attached, indicating a likely memory leak. Investigate listener accumulation; use emitter.setMaxListeners() only after diagnosing the root cause.
|
|
53
|
+
- **[HIGH]** Circular require() dependencies causing undefined imports → module exports evaluated before dependencies are resolved, resulting in undefined values at import time. Refactor circular dependencies by extracting shared code into a third module.
|
|
54
|
+
- **[MEDIUM]** Timezone issues from new Date() without UTC consideration → date calculations differ across server timezones, causing inconsistent behavior. Always work in UTC internally and convert to local time only at the presentation layer.
|
|
55
|
+
- **[HIGH]** Not validating req.body fields after body-parser → missing or malformed fields cause downstream errors or incorrect behavior. Validate all incoming request data with a schema library (zod, joi, ajv) after parsing.
|
|
56
|
+
- **[MEDIUM]** DNS lookup not cached for repeated external HTTP requests → DNS resolution adds latency to every external call. Use a keep-alive HTTP agent and ensure the underlying runtime caches DNS lookups appropriately.
|