Skip to content

Conversation

@Dexploarer
Copy link
Owner

@Dexploarer Dexploarer commented Nov 25, 2025

This app will be decommissioned on Dec 1st. Please remove this app and install Qodo Git.

PR Type

Bug fix, Enhancement, Tests


Description

  • Fix critical JWT test mode bypass by requiring proper HMAC-SHA256 signature verification

  • Add CSRF protection plugin with origin-based validation for state-changing requests

  • Fix CORS configuration to eliminate wildcard fallback and require explicit origin allowlisting

  • Enable rate limiting in all environments with environment-aware multipliers (1x prod, 10x dev, 100x test)

  • Implement proper RBAC for admin routes with actual role verification instead of deprecated single-team bypass

  • Add input length limits to TypeBox models across AssetMetadata to prevent memory exhaustion attacks

  • Disable production sourcemaps to prevent source code exposure in compiled bundles

  • Add comprehensive security headers (XSS protection, HSTS, Referrer-Policy, Permissions-Policy)

  • Implement JWT token blocklist service for logout and token revocation functionality

  • Convert asset route tests from mocks to real database with proper test data isolation

  • Create .env.example with deployment security checklist and all configuration options

  • Add database migrations to fix schema drift and create token blocklist table

  • Document ThreeViewer refactoring strategy for breaking down 6,340-line component


Diagram Walkthrough

flowchart LR
  A["Security Vulnerabilities"] --> B["JWT Test Mode Bypass"]
  A --> C["CORS Wildcard Fallback"]
  A --> D["Missing CSRF Protection"]
  A --> E["Disabled Rate Limiting"]
  A --> F["No Input Validation"]
  A --> G["Exposed Sourcemaps"]
  
  B --> B1["Implement HMAC-SHA256<br/>Signature Verification"]
  C --> C1["Explicit Origin<br/>Allowlisting"]
  D --> D1["Origin-Based CSRF<br/>Plugin"]
  E --> E1["Environment-Aware<br/>Rate Limits"]
  F --> F1["Add maxLength to<br/>TypeBox Models"]
  G --> G1["Disable Production<br/>Sourcemaps"]
  
  B1 --> H["Hardened App"]
  C1 --> H
  D1 --> H
  E1 --> H
  F1 --> H
  G1 --> H
  
  H --> I["Token Blocklist<br/>Service"]
  H --> J["Admin RBAC<br/>Enforcement"]
  H --> K["Security Headers"]
Loading

File Walkthrough

Relevant files
Bug fix
7 files
auth.ts
Implement proper JWT HMAC-SHA256 signature verification   
+19/-6   
api-elysia.ts
Add CSRF plugin and fix CORS origin allowlisting                 
+48/-6   
models.ts
Add maxLength constraints to prevent memory exhaustion     
+21/-21 
auth.plugin.ts
Fix JWT test verification and implement admin RBAC checks
+73/-16 
rate-limiting.plugin.ts
Enable rate limiting in all environments with multipliers
+33/-20 
vite.config.ts
Disable production sourcemaps to prevent code exposure     
+3/-2     
0032_fix_schema_drift.sql
Drop unused admin_whitelist table from schema                       
+12/-0   
Tests
1 files
assets.test.ts
Convert asset tests from mocks to real database                   
+102/-192
Enhancement
7 files
index.ts
Export token blocklist schema for JWT invalidation             
+5/-0     
token-blocklist.schema.ts
Create token blocklist schema for logout functionality     
+38/-0   
cron.plugin.ts
Add cleanup jobs for activity logs and token blocklist     
+56/-2   
csrf.plugin.ts
Implement origin-based CSRF protection for state-changing requests
+129/-0 
security-headers.ts
Add comprehensive security headers including HSTS and XSS protection
+25/-2   
TokenBlocklistService.ts
Implement JWT token blocklist service for revocation         
+107/-0 
0033_add_token_blocklist.sql
Create token blocklist table for JWT invalidation               
+16/-0   
Documentation
2 files
REFACTORING_PLAN.ts
Document ThreeViewer component refactoring strategy           
+54/-0   
.env.example
Create comprehensive environment configuration with deployment
checklist
+165/-0 
Configuration changes
1 files
_journal.json
Update migration journal with new migrations                         
+15/-1   

The managed version of the open source project PR-Agent is sunsetting on the 1st December 2025. The commercial version of this project will remain available and free to use as a hosted service. Install Qodo.

Summary by CodeRabbit

  • New Features

    • DB-backed token blocklist for logout/token revocation; scheduled cleanup job
    • CSRF protection for state-changing requests
    • Example environment configuration added
  • Security

    • Stronger security headers and conditional HSTS in production; stricter CORS origin handling
    • Admin guard now enforces admin role
  • Improvements

    • Rate limiting enabled by default with environment-aware scaling
  • Tests / Dev

    • Tests exercise real DB fixtures; improved test JWT signing and type-safe test clients
  • Docs

    • ThreeViewer refactoring plan added

✏️ Tip: You can customize this high-level summary in your review settings.

Week 1 Critical Security:
- Fix JWT test mode bypass (now requires TEST_JWT_SECRET with proper signature)
- Add CSRF protection plugin (origin-based validation)
- Fix CORS configuration (no more fallback to wildcard *)
- Enable rate limiting in all environments (with multipliers)
- Implement proper RBAC for admin routes (actual role checks)

Week 2-3 High Priority:
- Add input length limits to TypeBox models (prevent memory exhaustion)
- Disable production sourcemaps (don't expose source code)
- Add security headers (XSS protection, HSTS, Referrer-Policy, etc.)
- Fix schema-migration drift (drop unused admin_whitelist table)
- Create .env.example with deployment checklist

Month 1 Medium Priority:
- Add JWT invalidation via token blocklist table and service
- Add token blocklist schema and migration

This addresses the critical security vulnerabilities identified in the app review.
- Remove mockAssetService that was not even being used (createAssetRoutes
  signature changed to only take rootDir)
- Convert tests to use real database with test data isolation
- Fix CDN URL Handling tests to work with real assetDatabaseService
- Add cleanup jobs for activity_log and token_blocklist tables

Per CLAUDE.md: "NO MOCKS for internal code (database, HTTP handlers,
business logic)" - only mock external APIs (Meshy, OpenAI, Privy).
Document the recommended approach for breaking down the 1500+ line
ThreeViewer.tsx into smaller, maintainable components. This was
identified during the critical security review as a code quality issue.

The plan outlines:
- Component extraction strategy (SceneManager, ModelLoader, etc.)
- Implementation phases
- Testing requirements
@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds JWT token blocklist support (DB/schema/service/migration), CSRF plugin, environment-aware rate limiting, security header changes, auth plugin updates (blocklist check, admin guard, test signing), cron cleanup jobs, real-DB asset tests, test helpers, config/example env, and assorted docs/refactor plan.

Changes

Cohort / File(s) Summary
Environment & Configuration
apps/core/.env.example
New comprehensive example environment file with variables for Node, DB, server, auth, API key encryption, AI/service integrations, CORS, CDN, webhooks, rate limiting, logging, testing, and deployment notes.
DB Migrations & Schema
apps/core/server/db/migrations/0032_fix_schema_drift.sql, apps/core/server/db/migrations/0033_add_token_blocklist.sql, apps/core/server/db/migrations/meta/_journal.json, apps/core/server/db/schema/token-blocklist.schema.ts, apps/core/server/db/schema/index.ts
Drops unused admin_whitelist; adds token_blocklist table with indexes and registers schema; updates migrations journal.
Token Blocklist Service
apps/core/server/services/TokenBlocklistService.ts
New service that hashes tokens, persists blocklist entries, provides isTokenBlocklisted(), blockToken(), blockUserTokens(), cleanupExpired(), and exports a singleton.
Authentication Plugin & Test JWTs
apps/core/server/plugins/auth.plugin.ts, apps/core/__tests__/helpers/auth.ts
Auth plugin now checks blocklist for non-API-key JWTs, enforces admin role in requireAdminGuard, uses HMAC verification in test mode requiring TEST_JWT_SECRET; test helper creates properly signed HMAC-SHA256 JWTs.
CSRF Protection
apps/core/server/plugins/csrf.plugin.ts
New CSRF plugin enforcing Origin/Referer checks for state-changing methods, builds allowed origins from env plus dev localhost, skips API-key requests, returns 403 on mismatch.
API Middleware & CORS
apps/core/server/api-elysia.ts
Registers CSRF plugin, refactors dynamic CORS origin computation, and conditions debug endpoints on non-production.
Rate Limiting & Cron Jobs
apps/core/server/plugins/rate-limiting.plugin.ts, apps/core/server/plugins/cron.plugin.ts
Rate limiting defaults on unless DISABLE_RATE_LIMITING=true and scales via RATE_LIMIT_MULTIPLIER (env-aware); cron adds daily cleanup-old-activity and cleanup-token-blocklist jobs.
Security Headers
apps/core/server/plugins/security-headers.ts
Removes COEP header; adds X-XSS-Protection, Referrer-Policy, Permissions-Policy; applies HSTS only in production.
Asset API Tests
apps/core/__tests__/integration/api/routes/assets.test.ts
Migrates asset tests from mocked AssetService to real DB-backed tests; creates/cleans test data and updates expectations to use persisted CDN fields.
Data Models
apps/core/server/models.ts
Adds maxLength constraints to many AssetMetadata string fields and new metadata fields (completedAt, isFavorite, notes, lastViewedAt, status, projectId).
Build & Docs
apps/core/vite.config.ts, apps/core/src/components/three-viewer/REFACTORING_PLAN.ts
Conditional sourcemap disabled in production; adds ThreeViewer refactoring plan (documentation-only).
Test Helpers / API Client
apps/core/__tests__/helpers/api.ts
Adds createTestClient and createAuthTestClient (Eden Treaty) helpers for type-safe, zero-network tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as API / Middleware
    participant Auth as Auth Plugin
    participant Blocklist as TokenBlocklistService
    participant DB
    participant Handler

    Client->>API: Request with Authorization (JWT or Bearer af_ API key)
    API->>Auth: optionalAuth()
    Auth->>Auth: Extract token
    alt API key (starts "af_")
        Auth->>Handler: Authenticate as API key (skip blocklist)
    else JWT
        Auth->>Blocklist: isTokenBlocklisted(token)
        Blocklist->>DB: SELECT hashed token
        DB-->>Blocklist: Found / Not found
        alt Found
            Auth-->>Client: 401/403 (token revoked)
        else Not found
            Auth->>Auth: Verify JWT (Privy or HMAC in test mode)
            alt Verified
                Auth->>Handler: Inject authenticated user
            else Verification failed
                Auth-->>Client: 401 Unauthorized
            end
        end
    end
    Handler-->>Client: Response
Loading
sequenceDiagram
    participant Client
    participant API as API / CSRF Plugin
    participant Allowed as AllowedOriginsBuilder

    Client->>API: POST/PUT/DELETE (Origin / Referer)
    API->>Allowed: Build allowed origins from FRONTEND_URL, CORS_ALLOWED_ORIGINS, dev localhost
    alt Authorization is API key (af_)
        API->>API: Skip CSRF check
        API-->>Client: Continue
    else Regular request
        API->>API: Determine origin from Origin header or Referer
        API->>Allowed: Check origin in allowed list
        alt Match
            API-->>Client: Continue
        else No match
            API-->>Client: 403 CSRF_VALIDATION_FAILED
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay extra attention to:
    • apps/core/server/plugins/auth.plugin.ts — blocklist integration, test-mode HMAC branch, admin guard semantics.
    • apps/core/server/services/TokenBlocklistService.ts and apps/core/server/db/migrations/0033_add_token_blocklist.sql — hashing, uniqueness handling (23505), expiry logic.
    • apps/core/__tests__/integration/api/routes/assets.test.ts — real DB test lifecycle, data isolation, and expected field changes.
    • apps/core/server/plugins/rate-limiting.plugin.ts — multiplier-based scaling logic and DISABLE_RATE_LIMITING default-on behavior.
    • apps/core/server/plugins/csrf.plugin.ts — Origin/Referer extraction and allowed-origin validation under dev/prod conditions.

Possibly related PRs

  • Dexploorer/hyper-forge#1 — overlaps with auth plugin changes (blocklist check, test-mode JWT signing, admin guard enforcement).

Suggested labels

Possible security concern

Poem

🐰 I hashed the token, tucked it neat,
Cleared old crumbs at dawn's soft beat,
CSRF fences guard each busy street,
Rate limits hum so pulses meet,
A rabbit hops on—secure and fleet! 🔐

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and generic, using non-descriptive language that does not convey meaningful information about the changeset. Replace with a specific, descriptive title that captures the main change, e.g., 'Add CSRF protection, JWT blocklist, and security hardening' or 'Implement JWT token blocklist and security enhancements'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/critical-app-review-019txope9jjiPCuNTQJC1Qh5

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Dexploarer, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the application's security posture, improves the reliability of its testing suite, and lays groundwork for better maintainability. Key changes include the introduction of CSRF protection, a robust JWT token blocklist, and more stringent security headers. The asset routes now undergo more realistic integration testing against a live database, and environment configurations are clearer. Furthermore, rate limiting has been made more flexible and always-on, and a strategic plan for refactoring a major frontend component has been documented.

Highlights

  • Security Enhancements: Implemented robust CSRF protection, improved JWT handling in test environments with proper signature verification, and added a suite of new security headers (X-XSS-Protection, Referrer-Policy, Permissions-Policy, Strict-Transport-Security) to bolster application security.
  • Token Blocklist for JWT Invalidation: Introduced a new token_blocklist database table and service to enable immediate invalidation of JWT tokens upon logout or revocation, enhancing session management security.
  • Improved Testing Infrastructure: Refactored asset routes integration tests to interact directly with a real database instead of mocks, significantly increasing test fidelity and reliability. Test JWTs are now properly signed using a configurable secret.
  • Environment Configuration and Deployment Checklist: A comprehensive .env.example file has been added, detailing all environment variables with explanations and including a deployment checklist to guide secure and correct production setup.
  • Refined CORS and Rate Limiting: CORS origin handling is now more dynamic and secure, building allowed origins based on environment variables. Rate limiting is always enabled by default, with environment-aware multipliers for flexible configuration across production, development, and test environments.
  • Database Schema and Model Updates: New database migrations were added to clean up an unused admin_whitelist table and create the token_blocklist. Additionally, maxLength constraints were applied to various string fields in the AssetMetadata model for better data validation.
  • New Cron Jobs: Two new daily cron jobs were introduced: one for cleaning up old activity logs (retaining 90 days) and another for removing expired entries from the token blocklist.
  • Frontend Refactoring Plan: A detailed refactoring plan for the large ThreeViewer component has been outlined, proposing a phased approach to break it down into smaller, more manageable UI components, logic hooks, and complex features.
  • Production Build Optimization: Vite build configuration was updated to disable source maps in production environments for enhanced security, preventing exposure of source code, while keeping them enabled for development debugging.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codiumai-pr-agent-free
Copy link
Contributor

codiumai-pr-agent-free bot commented Nov 25, 2025

This app will be decommissioned on Dec 1st. Please remove this app and install Qodo Git.

PR Compliance Guide 🔍

(Compliance updated until commit d57bfd3)

Below is a summary of compliance checks for this PR:

Security Compliance
🔴
Incomplete Session Revocation

Description: The blockUserTokens function is marked as deprecated and explicitly does not implement
"logout everywhere" functionality, leaving a security gap where revoked user sessions may
remain active. TokenBlocklistService.ts [91-109]

Referred Code
 * Block all tokens for a user (logout everywhere)
 * @deprecated This function is a placeholder and does not provide a secure "logout everywhere" implementation.
 * A robust solution requires a mechanism like a `tokensValidFrom` timestamp on the user model.
 */
async blockUserTokens(userId: string, reason?: string): Promise<void> {
  // This is a critical security operation that is NOT implemented.
  // A proper implementation should invalidate all of a user's sessions,
  // for example by updating a `tokensValidFrom` timestamp in the user's database record
  // and checking it during token verification.
  // Leaving this as a no-op to prevent a false sense of security.
  logger.error(
    { userId, reason, context: "auth" },
    "CRITICAL: blockUserTokens is not implemented. User tokens were NOT invalidated.",
  );
  // To prevent accidental use, throw an error in non-production environments.
  if (process.env.NODE_ENV !== "production") {
    throw new Error("blockUserTokens is not implemented.");
  }
}
JWT Test Mode

Description: The JWT test mode verification now requires proper HMAC-SHA256 signature verification, but
still allows test tokens in production if TEST_JWT_SECRET is set, creating a potential
authentication bypass. auth.plugin.ts [161-209]

Referred Code
// In test mode, use TEST_JWT_SECRET for verification instead of Privy
// This allows tests to create valid tokens without external Privy calls
if (env.NODE_ENV === "test") {
  try {
    // Require TEST_JWT_SECRET in test mode - prevents accidental production bypass
    const testSecret = process.env.TEST_JWT_SECRET;
    if (!testSecret) {
      logger.error(
        { context: "auth" },
        "TEST MODE - TEST_JWT_SECRET not configured, rejecting token",
      );
      return {};
    }

    // Simple HMAC verification for test tokens
    const parts = token.split(".");
    if (parts.length !== 3) {
      throw new Error("Invalid JWT format");
    }

    // Verify signature using TEST_JWT_SECRET


 ... (clipped 28 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Partial Audit Logging: The token blocklist check logs when tokens are blocked but lacks comprehensive logging for
all authentication events like successful logins or token validations

Referred Code
if (!token.startsWith("af_")) {
  try {
    const isBlocked = await tokenBlocklistService.isTokenBlocklisted(token);
    if (isBlocked) {
      logger.info(
        { context: "auth" },
        "Token is blocklisted (revoked/logged out)",
      );
      return {};
    }
  } catch (error) {
    // Fail-closed: If blocklist check fails, reject the token for security.
    logger.error(
      { err: error, context: "auth" },
      "Failed to check token blocklist. Rejecting token as a security precaution.",
    );
    return {};
  }
}
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Previous compliance checks

Compliance check up to commit 51a22c5
Security Compliance
JWT Test Verification

Description: The JWT verification in test mode uses a hardcoded TEST_JWT_SECRET environment variable
that could be exploited if not properly configured in test environments. auth.plugin.ts [162-196]

Referred Code
if (env.NODE_ENV === "test") {
  try {
    // Require TEST_JWT_SECRET in test mode - prevents accidental production bypass
    const testSecret = process.env.TEST_JWT_SECRET;
    if (!testSecret) {
      logger.error(
        { context: "auth" },
        "TEST MODE - TEST_JWT_SECRET not configured, rejecting token",
      );
      return {};
    }

    // Simple HMAC verification for test tokens
    const parts = token.split(".");
    if (parts.length !== 3) {
      throw new Error("Invalid JWT format");
    }

    // Verify signature using TEST_JWT_SECRET
    const crypto = await import("crypto");
    const signatureInput = `${parts[0]}.${parts[1]}`;


 ... (clipped 14 lines)
CORS Configuration

Description: The CORS configuration allows multiple origins including development URLs in
non-production environments, which could lead to cross-origin attacks if misconfigured. api-elysia.ts [255-289]

Referred Code
origin: (request) => {
  // Build allowed origins list
  const allowedOrigins: string[] = [];

  // Add FRONTEND_URL if configured (required in production)
  if (env.FRONTEND_URL) {
    allowedOrigins.push(env.FRONTEND_URL);
  }

  // Add any additional CORS_ALLOWED_ORIGINS
  if (env.CORS_ALLOWED_ORIGINS && env.CORS_ALLOWED_ORIGINS.length > 0) {
    allowedOrigins.push(...env.CORS_ALLOWED_ORIGINS);
  }

  // In development, allow localhost origins
  if (env.NODE_ENV !== "production") {
    allowedOrigins.push(
      "http://localhost:3000",
      "http://localhost:3004",
      "http://127.0.0.1:3000",
      "http://127.0.0.1:3004",


 ... (clipped 14 lines)
CSRF Protection Bypass

Description: CSRF protection is bypassed for API key authentication, which could be exploited if an API
key is leaked or if the authentication header is spoofed. csrf.plugin.ts [76-80]

Referred Code
const authHeader =
  headers?.authorization || request.headers.get("authorization");
if (authHeader?.startsWith("Bearer af_")) {
  return; // API key requests don't need CSRF protection
}
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

The managed version of the open source project PR-Agent is sunsetting on the 1st December 2025. The commercial version of this project will remain available and free to use as a hosted service. Install Qodo.

@codiumai-pr-agent-free
Copy link
Contributor

codiumai-pr-agent-free bot commented Nov 25, 2025

CI Feedback 🧐

(Feedback updated until commit 51a22c5)

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: Unit & Integration Tests

Failed stage: Run database migrations [❌]

Failed test name: db:migrate

Failure summary:

The action failed during database migration because it tried to modify a table that doesn't exist.
Specifically, the migration script attempted to drop the admin_whitelist table and its constraints,
but PostgreSQL returned error code 42P01 indicating "relation 'admin_whitelist' does not exist". The
error occurred in a migration script that was trying to fix schema drift by removing an unused
table.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

287:  [11:43:34 UTC] �[32mINFO�[39m: �[36m[Migrations] Starting migration process�[39m
288:  �[35menv�[39m: "development"
289:  �[35mmigrationsFolder�[39m: "/home/runner/work/hyper-forge/hyper-forge/apps/core/server/db/migrations"
290:  �[35mdatabaseUrl�[39m: "***localhost:5432/asset_forge_test"
291:  [11:43:34 UTC] �[32mINFO�[39m: �[36m[Migrations] Found migration files�[39m
292:  �[35menv�[39m: "development"
293:  �[35mmigrationsFolder�[39m: "/home/runner/work/hyper-forge/hyper-forge/apps/core/server/db/migrations"
294:  �[35mtotalFiles�[39m: 36
295:  �[35msqlFiles�[39m: 34
296:  [11:43:34 UTC] �[32mINFO�[39m: �[36m[Migrations] Database connection test passed�[39m
297:  �[35menv�[39m: "development"
298:  [11:43:34 UTC] �[32mINFO�[39m: �[36m[Migrations] Fresh database detected�[39m
299:  �[35menv�[39m: "development"
300:  [11:43:34 UTC] �[32mINFO�[39m: �[36m[Migrations] Running migrations...�[39m
301:  �[35menv�[39m: "development"
302:  [Migrations] ✗ MIGRATION FAILED - ERROR DETAILS:
303:  Error Code: 42P01
304:  Error Message: Failed query: -- Fix Schema Drift Migration
305:  -- Drop unused admin_whitelist table that exists in DB but not in TypeScript schemas
306:  -- This table was created in migration 0000 but was never needed (single-team app)
307:  -- Drop the foreign key constraint first
308:  ALTER TABLE "admin_whitelist" DROP CONSTRAINT IF EXISTS "admin_whitelist_added_by_users_id_fk";
309:  -- Drop the index
310:  DROP INDEX IF EXISTS "idx_admin_whitelist_wallet";
311:  -- Drop the table
312:  DROP TABLE IF EXISTS "admin_whitelist";
313:  params: 
314:  Full Error: {
315:  "query": "-- Fix Schema Drift Migration\n-- Drop unused admin_whitelist table that exists in DB but not in TypeScript schemas\n-- This table was created in migration 0000 but was never needed (single-team app)\n\n-- Drop the foreign key constraint first\nALTER TABLE \"admin_whitelist\" DROP CONSTRAINT IF EXISTS \"admin_whitelist_added_by_users_id_fk\";\n\n-- Drop the index\nDROP INDEX IF EXISTS \"idx_admin_whitelist_wallet\";\n\n-- Drop the table\nDROP TABLE IF EXISTS \"admin_whitelist\";\n",
316:  "params": [],
317:  "cause": {
318:  "name": "PostgresError",
319:  "severity_local": "ERROR",
320:  "severity": "ERROR",
321:  "code": "42P01",
322:  "file": "namespace.c",
323:  "routine": "RangeVarGetRelidExtended"
324:  }
325:  }
326:  Error Stack: Error
327:  at queryWithCache (/home/runner/work/hyper-forge/hyper-forge/node_modules/drizzle-orm/pg-core/session.js:41:19)
328:  at processTicksAndRejections (unknown:7:39)
329:  Error Cause: {
330:  "name": "PostgresError",
331:  "severity_local": "ERROR",
332:  "severity": "ERROR",
333:  "code": "42P01",
334:  "file": "namespace.c",
335:  "routine": "RangeVarGetRelidExtended"
336:  }
337:  [11:43:35 UTC] �[31mERROR�[39m: �[36m[Migrations] ✗ Migration failed�[39m
338:  �[35menv�[39m: "development"
339:  �[35mcode�[39m: "42P01"
340:  �[35mmessage�[39m: "Failed query: -- Fix Schema Drift Migration\n-- Drop unused admin_whitelist table that exists in DB but not in TypeScript schemas\n-- This table was created in migration 0000 but was never needed (single-team app)\n\n-- Drop the foreign key constraint first\nALTER TABLE \"admin_whitelist\" DROP CONSTRAINT IF EXISTS \"admin_whitelist_added_by_users_id_fk\";\n\n-- Drop the index\nDROP INDEX IF EXISTS \"idx_admin_whitelist_wallet\";\n\n-- Drop the table\nDROP TABLE IF EXISTS \"admin_whitelist\";\n\nparams: "
341:  �[35mstack�[39m: "Error\n    at queryWithCache (/home/runner/work/hyper-forge/hyper-forge/node_modules/drizzle-orm/pg-core/session.js:41:19)\n    at processTicksAndRejections (unknown:7:39)"
342:  �[35mcause�[39m: {
343:  "name": "PostgresError",
344:  "severity_local": "ERROR",
345:  "severity": "ERROR",
346:  "code": "42P01",
347:  "file": "namespace.c",
348:  "routine": "RangeVarGetRelidExtended"
349:  }
350:  �[35mmigrationsFolder�[39m: "/home/runner/work/hyper-forge/hyper-forge/apps/core/server/db/migrations"
351:  error: script "db:migrate" exited with code 1
352:  ##[error]Process completed with exit code 1.
353:  ##[group]Run actions/upload-artifact@v4
...

437:  server stopped
438:  PostgreSQL init process complete; ready for start up.
439:  sh: locale: not found
440:  2025-11-25 11:43:01.892 UTC [35] WARNING:  no usable system locales were found
441:  initdb: warning: enabling "trust" authentication for local connections
442:  initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb.
443:  2025-11-25 11:43:02.685 UTC [1] LOG:  starting PostgreSQL 16.11 on x86_64-pc-linux-musl, compiled by gcc (Alpine 14.2.0) 14.2.0, 64-bit
444:  2025-11-25 11:43:02.685 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
445:  2025-11-25 11:43:02.685 UTC [1] LOG:  listening on IPv6 address "::", port 5432
446:  2025-11-25 11:43:02.687 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
447:  2025-11-25 11:43:02.690 UTC [57] LOG:  database system was shut down at 2025-11-25 11:43:02 UTC
448:  2025-11-25 11:43:02.694 UTC [1] LOG:  database system is ready to accept connections
449:  2025-11-25 11:43:11.494 UTC [67] FATAL:  role "root" does not exist
450:  2025-11-25 11:43:21.538 UTC [74] FATAL:  role "root" does not exist
451:  2025-11-25 11:43:31.588 UTC [81] FATAL:  role "root" does not exist
452:  2025-11-25 11:43:35.228 UTC [83] ERROR:  relation "admin_whitelist" does not exist
453:  2025-11-25 11:43:35.228 UTC [83] STATEMENT:  -- Fix Schema Drift Migration

@greptile-apps
Copy link

greptile-apps bot commented Nov 25, 2025

Greptile Overview

Greptile Summary

This PR implements critical security hardening and refactoring improvements for the Asset-Forge application. The changes focus on JWT token invalidation infrastructure, authentication security enhancements, security headers, CSRF protection, improved input validation, and source map security. Key architectural improvements include adding a token blocklist system for proper logout functionality, fixing authentication test vulnerabilities, implementing comprehensive security headers, and enhancing CORS configuration. The PR also addresses code quality concerns by creating a refactoring plan for a massive 6,340-line React component and improving test reliability by removing mock services in favor of real database integration.

Important Files Changed

Filename Score Overview
apps/core/server/services/TokenBlocklistService.ts 4/5 New service implementing JWT token invalidation with secure hashing and cleanup operations
apps/core/server/plugins/auth.plugin.ts 5/5 Critical authentication security fixes including token blocklist integration and proper JWT verification
apps/core/server/db/schema/token-blocklist.schema.ts 5/5 New database schema for JWT token invalidation with proper indexes and constraints
apps/core/server/db/migrations/0033_add_token_blocklist.sql 4/5 Database migration creating token_blocklist table with performance optimizations
apps/core/server/api-elysia.ts 4/5 Enhanced security with improved CORS, CSRF protection, and conditional debug endpoints
apps/core/server/plugins/security-headers.ts 4/5 Additional security headers including HSTS, permissions policy, and XSS protection
apps/core/server/plugins/csrf.plugin.ts 4/5 New CSRF protection plugin with origin validation and API key bypass
apps/core/server/plugins/rate-limiting.plugin.ts 4/5 Environment-aware rate limiting with significantly reduced production limits
apps/core/__tests__/integration/api/routes/assets.test.ts 4/5 Refactored to use real database instead of mocks, aligning with testing philosophy
apps/core/__tests__/helpers/auth.ts 4/5 Fixed test authentication to use proper JWT signatures instead of mock tokens
apps/core/server/models.ts 5/5 Added input validation constraints with maxLength limits on all string fields
apps/core/vite.config.ts 5/5 Disabled source maps in production to prevent source code exposure
apps/core/.env.example 5/5 Comprehensive environment configuration template with security guidelines
apps/core/src/components/three-viewer/REFACTORING_PLAN.ts 5/5 Systematic plan for breaking down massive 6,340-line React component
apps/core/server/db/migrations/0032_fix_schema_drift.sql 5/5 Cleanup migration removing unused admin_whitelist table

Confidence score: 4/5

  • This PR implements critical security improvements but requires careful review due to authentication system changes
  • Score reflects comprehensive security hardening with mostly safe changes, but authentication modifications need thorough testing
  • Main concerns are potential method chaining issues in api-elysia.ts and ensuring token blocklist doesn't break existing authentication flows
  • Pay close attention to authentication plugin changes and database migration files

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Comments (3)

  1. apps/core/__tests__/helpers/auth.ts, line 96-109 (link)

    logic: Mock verification function doesn't validate the JWT signature it just signed, defeating the security improvement. Should verify the HMAC signature or throw if invalid.

    Should the mock verification function validate the JWT signature for more realistic testing?

  2. apps/core/__tests__/helpers/auth.ts, line 102 (link)

    syntax: Using base64 instead of base64url for decoding - inconsistent with encoding on line 31.

  3. apps/core/server/plugins/rate-limiting.plugin.ts, line 112 (link)

    syntax: Comment says 'Automatically disabled in development mode' but this is no longer accurate with the new environment-aware approach

18 files reviewed, 12 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 98 to 101
// This is safe because browsers always send Origin on cross-origin requests
if (!effectiveOrigin) {
return;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: The comment about skipping requests without origin being safe needs verification - some legitimate same-origin requests might not include Origin header, potentially creating a bypass. Have you verified that all legitimate same-origin requests in your application will include the Origin header, or should there be additional validation for requests without Origin?

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/server/plugins/csrf.plugin.ts
Line: 98:101

Comment:
**style:** The comment about skipping requests without origin being safe needs verification - some legitimate same-origin requests might not include Origin header, potentially creating a bypass. Have you verified that all legitimate same-origin requests in your application will include the Origin header, or should there be additional validation for requests without Origin?

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 95 to 103
async blockUserTokens(userId: string, reason?: string): Promise<void> {
// We can't block tokens we haven't seen
// For a proper "logout everywhere", the user should change their password
// or we should track active sessions
logger.info(
{ userId, reason, context: "auth" },
"User token blocklist requested (note: only blocks known tokens)",
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The blockUserTokens method only logs but doesn't actually block any tokens, which could be misleading. Should this method query existing sessions or tokens for the user and block them, or is the current no-op implementation intentional?

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/server/services/TokenBlocklistService.ts
Line: 95:103

Comment:
**logic:** The blockUserTokens method only logs but doesn't actually block any tokens, which could be misleading. Should this method query existing sessions or tokens for the user and block them, or is the current no-op implementation intentional?

How can I resolve this? If you propose a fix, please make it concise.

);
} catch (error: any) {
// Ignore duplicate key errors (token already blocklisted)
if (error.code === "23505") {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: PostgreSQL-specific error code hardcoded - consider making this more database-agnostic or documenting the assumption

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/server/services/TokenBlocklistService.ts
Line: 63:63

Comment:
**style:** PostgreSQL-specific error code hardcoded - consider making this more database-agnostic or documenting the assumption

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 127 to +139
afterEach(async () => {
// Reset mock assets with CDN URLs
mockAssets.length = 0;
mockAssets.push(
{
// Re-create test assets if they were deleted during tests
// Check if assets still exist
const asset1Exists = await db.query.assets.findFirst({
where: eq(assets.id, "test-asset-1"),
});
const asset2Exists = await db.query.assets.findFirst({
where: eq(assets.id, "test-asset-2"),
});

if (!asset1Exists) {
testAsset1 = await createTestAsset(testUser1.user.id, {
id: "test-asset-1",
name: "Test Sword",
type: "weapon",
tier: 1,
category: "melee",
filePath: "test-asset-1/model.glb",
status: "completed",
visibility: "public",
cdnUrl: "https://cdn.asset-forge.com/models/test-asset-1/model.glb",
cdnThumbnailUrl:
"https://cdn.asset-forge.com/models/test-asset-1/thumbnail.png",
hasSpriteSheet: false,
createdBy: "user-123",
walletAddress: "0xABC",
isPublic: true,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
{
});
}

if (!asset2Exists) {
testAsset2 = await createTestAsset(testAdmin.user.id, {
id: "test-asset-2",
name: "Admin Asset",
type: "armor",
tier: 2,
category: "heavy",
filePath: "test-asset-2/model.glb",
status: "completed",
visibility: "public",
cdnUrl: "https://cdn.asset-forge.com/models/test-asset-2/model.glb",
createdBy: "admin-456",
isPublic: true,
createdAt: new Date().toISOString(),
},
);
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: the afterEach recreation logic may cause issues if tests run in parallel - consider using unique asset IDs per test instead of shared ones

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/__tests__/integration/api/routes/assets.test.ts
Line: 103:139

Comment:
**style:** the afterEach recreation logic may cause issues if tests run in parallel - consider using unique asset IDs per test instead of shared ones

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

it("should return empty array when no assets exist", async () => {
mockAssets.length = 0;
// Delete all test assets from real database
await db.delete(assets).execute();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: deleting all assets in the test database could affect parallel tests - consider scoping deletion to specific test assets only

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/__tests__/integration/api/routes/assets.test.ts
Line: 191:191

Comment:
**logic:** deleting all assets in the test database could affect parallel tests - consider scoping deletion to specific test assets only

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +107 to +109
const { db } = await import("../db/db");
const { activityLog } = await import("../db/schema");
const { lt } = await import("drizzle-orm");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: inconsistent with other cleanup jobs that use service methods - consider creating an ActivityLogService with a cleanupOld() method for better separation of concerns

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/server/plugins/cron.plugin.ts
Line: 107:109

Comment:
**style:** inconsistent with other cleanup jobs that use service methods - consider creating an ActivityLogService with a `cleanupOld()` method for better separation of concerns

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

set.headers["X-Frame-Options"] = "DENY";

// Legacy XSS protection (for older browsers)
set.headers["X-XSS-Protection"] = "1; mode=block";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: X-XSS-Protection is deprecated and can actually introduce vulnerabilities in some browsers. Consider removing this header and relying on CSP instead.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/server/plugins/security-headers.ts
Line: 38:38

Comment:
**style:** `X-XSS-Protection` is deprecated and can actually introduce vulnerabilities in some browsers. Consider removing this header and relying on CSP instead.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

*/
const RATE_LIMITING_ENABLED =
env.NODE_ENV === "production" || env.ENABLE_RATE_LIMITING === true;
const RATE_LIMITING_ENABLED = process.env.DISABLE_RATE_LIMITING !== "true";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using process.env directly bypasses the env validation system - should use env.DISABLE_RATE_LIMITING for consistency

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/server/plugins/rate-limiting.plugin.ts
Line: 43:43

Comment:
**style:** Using `process.env` directly bypasses the env validation system - should use `env.DISABLE_RATE_LIMITING` for consistency

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +336 to +337
// Continue the chain
app
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: method chaining after conditional block creates confusing code structure - the app variable continues the chain but may not include the debug plugin. Should this be restructured to avoid the confusing method chaining pattern after the conditional debug plugin registration?

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/core/server/api-elysia.ts
Line: 336:337

Comment:
**style:** method chaining after conditional block creates confusing code structure - the `app` variable continues the chain but may not include the debug plugin. Should this be restructured to avoid the confusing method chaining pattern after the conditional debug plugin registration?

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 101 to 142
});

afterEach(async () => {
// Reset mock assets with CDN URLs
mockAssets.length = 0;
mockAssets.push(
{
// Re-create test assets if they were deleted during tests
// Check if assets still exist
const asset1Exists = await db.query.assets.findFirst({
where: eq(assets.id, "test-asset-1"),
});
const asset2Exists = await db.query.assets.findFirst({
where: eq(assets.id, "test-asset-2"),
});

if (!asset1Exists) {
testAsset1 = await createTestAsset(testUser1.user.id, {
id: "test-asset-1",
name: "Test Sword",
type: "weapon",
tier: 1,
category: "melee",
filePath: "test-asset-1/model.glb",
status: "completed",
visibility: "public",
cdnUrl: "https://cdn.asset-forge.com/models/test-asset-1/model.glb",
cdnThumbnailUrl:
"https://cdn.asset-forge.com/models/test-asset-1/thumbnail.png",
hasSpriteSheet: false,
createdBy: "user-123",
walletAddress: "0xABC",
isPublic: true,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
{
});
}

if (!asset2Exists) {
testAsset2 = await createTestAsset(testAdmin.user.id, {
id: "test-asset-2",
name: "Admin Asset",
type: "armor",
tier: 2,
category: "heavy",
filePath: "test-asset-2/model.glb",
status: "completed",
visibility: "public",
cdnUrl: "https://cdn.asset-forge.com/models/test-asset-2/model.glb",
createdBy: "admin-456",
isPublic: true,
createdAt: new Date().toISOString(),
},
);
});
}
});

describe("GET /api/assets", () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Data Race in Asset Recreation

The afterEach hook attempts to recreate test assets if they were deleted during tests. If tests are executed in parallel or if asset creation is not atomic, this could lead to inconsistent test state or race conditions, resulting in flaky tests or unexpected failures.

Recommended Solution:

  • Ensure tests are run sequentially, or use database transactions/locks to guarantee atomicity of asset creation and deletion.
  • Consider using a dedicated test database with transaction rollback for each test to ensure isolation.

// Plus CDN fields merged from database
expect(returnedAsset.cdnUrl).toBe(
"https://cdn.example.com/models/full-asset/model.glb",
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Insufficient Validation of Asset Metadata Fields

The CDN URL handling and migration validation tests check for the presence or absence of certain fields (e.g., cdnUrl, modelUrl, filePath) but do not comprehensively verify the integrity or correctness of all asset metadata. If the asset schema changes or the API response includes unexpected fields, these tests may not catch regressions.

Recommended Solution:

  • Strengthen assertions to validate all expected asset fields and their types/values.
  • Add negative tests to ensure no legacy or deprecated fields are present in the response.

Comment on lines +279 to +286
// If no origins configured in production, reject (don't fall back to wildcard)
if (allowedOrigins.length === 0) {
logger.warn(
{ context: "cors" },
"No CORS origins configured - rejecting cross-origin requests",
);
return false;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential for Outage Due to Strict CORS Configuration

If no allowed origins are configured (i.e., env.FRONTEND_URL and env.CORS_ALLOWED_ORIGINS are unset), the CORS middleware will reject all cross-origin requests, even in production. While this is secure, it could cause unexpected outages if environment variables are misconfigured or missing. The current implementation only logs a warning and does not prevent server startup.

Recommendation:
Consider adding a startup check that throws an error and prevents the server from starting if in production and no CORS origins are configured. This will ensure misconfiguration is detected early and avoids silent failures:

if (env.NODE_ENV === "production" && allowedOrigins.length === 0) {
  throw new Error("No CORS origins configured in production. Set FRONTEND_URL or CORS_ALLOWED_ORIGINS.");
}

Comment on lines +4 to +12
CREATE TABLE "token_blocklist" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"token_id" varchar(255) NOT NULL,
"user_id" uuid,
"reason" varchar(100),
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"expires_at" timestamp with time zone NOT NULL,
CONSTRAINT "token_blocklist_token_id_unique" UNIQUE("token_id")
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential for Unbounded Table Growth

The migration does not include any mechanism to automatically remove expired tokens from the token_blocklist table. Over time, this could result in unbounded growth and degrade query performance. Consider implementing a scheduled job or database trigger to periodically delete rows where expires_at < now(). For example:

DELETE FROM token_blocklist WHERE expires_at < now();

This will help maintain optimal performance and storage efficiency.

CREATE TABLE "token_blocklist" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"token_id" varchar(255) NOT NULL,
"user_id" uuid,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Foreign Key Constraint on user_id

The user_id column is defined as a UUID but does not reference any users table. This could lead to orphaned records if user accounts are deleted. If possible, add a foreign key constraint to ensure referential integrity:

FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL

This will help maintain data consistency between the blocklist and user records.

error: "TOO_MANY_REQUESTS",
message,
} as any;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type Safety Issue: Use of as any in Error Response

The createRateLimitError function (lines 32-37) returns an object cast as any. This undermines type safety and could lead to runtime errors or inconsistent error responses if the structure changes.

Recommendation:
Define and use a specific error response type, e.g.:

type RateLimitError = { error: string; message: string };
function createRateLimitError(message: string): RateLimitError { ... }

This ensures consistent error handling and better maintainability.

Comment on lines +44 to +45
set.headers["Permissions-Policy"] =
"geolocation=(), microphone=(), camera=(), payment=()";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Permissions-Policy header uses deprecated syntax.

The Permissions-Policy header is currently set as:

set.headers["Permissions-Policy"] = "geolocation=(), microphone=(), camera=(), payment=()";

Modern browsers expect the new syntax, e.g.:

set.headers["Permissions-Policy"] = "geolocation=(), microphone=(), camera=(), payment=()";

However, the correct format is:

set.headers["Permissions-Policy"] = "geolocation=(), microphone=(), camera=(), payment=()";

But for broader compatibility, consider using the new Feature-Policy header or the updated Permissions-Policy syntax:

set.headers["Permissions-Policy"] = "geolocation=(), microphone=(), camera=(), payment=()";

Review the latest MDN documentation and update the header for maximum browser support.

Comment on lines +49 to +50
set.headers["Strict-Transport-Security"] =
"max-age=31536000; includeSubDomains";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strict-Transport-Security header missing preload directive.

The HSTS header is set as:

set.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains";

For sites intended to be included in browser preload lists, add the preload directive:

set.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload";

This increases security for first-time visitors. If you do not intend to submit your domain to the preload list, this can be omitted.

Comment on lines 95 to 103
async blockUserTokens(userId: string, reason?: string): Promise<void> {
// We can't block tokens we haven't seen
// For a proper "logout everywhere", the user should change their password
// or we should track active sessions
logger.info(
{ userId, reason, context: "auth" },
"User token blocklist requested (note: only blocks known tokens)",
);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misleading Implementation of blockUserTokens

The blockUserTokens method only logs the request and does not actually block any tokens for the user. This could lead to a false sense of security for developers or users expecting all tokens to be invalidated. While the comments clarify the limitation, the method's name and signature suggest that it performs a blocking operation.

Recommendation:

  • Either implement logic to block all known tokens for the user (e.g., by inserting blocklist entries for all tokens associated with the user), or rename the method and update its documentation to make its behavior explicit.
  • Alternatively, throw an error or warning if the method is called, to prevent misuse.

sourcemap: true,
// Disable source maps in production (security - don't expose source code)
// Enable in development for debugging
sourcemap: process.env.NODE_ENV !== "production",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Security Risk: The sourcemap option is enabled based on process.env.NODE_ENV !== "production". If NODE_ENV is not set or is set incorrectly in the production environment, source maps may be unintentionally exposed, leaking source code and sensitive information.

Recommendation: Add a default fallback or stricter validation for NODE_ENV to ensure source maps are never exposed in production builds. For example:

sourcemap: process.env.NODE_ENV && process.env.NODE_ENV !== "production"

Or explicitly default to false if NODE_ENV is not set:

sourcemap: process.env.NODE_ENV ? process.env.NODE_ENV !== "production" : false

@codiumai-pr-agent-free
Copy link
Contributor

codiumai-pr-agent-free bot commented Nov 25, 2025

PR Code Suggestions ✨

This app will be decommissioned on Dec 1st. Please remove this app and install Qodo Git.

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
PR description contradicts its content

The PR description incorrectly states the application is being decommissioned.
It should be updated to accurately describe the extensive security hardening and
feature enhancements that are actually being implemented in the code.

Examples:

Solution Walkthrough:

Before:

# PR Description
> This app will be decommissioned on Dec 1st. Please remove this app and install [Qodo Git](https://github.com/marketplace/qodo-merge-pro).

### **PR Type**
Bug fix, Enhancement, Tests

After:

# PR Description
### Security Hardening & Feature Enhancement

This PR introduces a comprehensive set of security fixes and feature enhancements to harden the application, including:
- Fix critical JWT test mode bypass by requiring proper HMAC-SHA256 signature verification
- Add CSRF protection plugin with origin-based validation
- Fix CORS configuration to eliminate wildcard fallback
- Enable rate limiting in all environments
- Implement proper RBAC for admin routes
- Add input length limits to prevent memory exhaustion attacks
... and other critical improvements.

Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical and dangerously misleading contradiction between the PR description, which claims the app is being decommissioned, and the actual code changes, which introduce extensive security hardening and new features.

High
Security
Prevent misuse of incomplete security function
Suggestion Impact:The commit directly implemented the suggestion by marking the function as deprecated, changing the log level from info to error with a critical warning message, and adding code to throw an error in non-production environments

code diff:

-   * Note: This only blocks tokens we've seen - new tokens won't be affected
-   * For complete invalidation, change the user's secret or rotate keys
+   * @deprecated This function is a placeholder and does not provide a secure "logout everywhere" implementation.
+   * A robust solution requires a mechanism like a `tokensValidFrom` timestamp on the user model.
    */
   async blockUserTokens(userId: string, reason?: string): Promise<void> {
-    // We can't block tokens we haven't seen
-    // For a proper "logout everywhere", the user should change their password
-    // or we should track active sessions
-    logger.info(
+    // This is a critical security operation that is NOT implemented.
+    // A proper implementation should invalidate all of a user's sessions,
+    // for example by updating a `tokensValidFrom` timestamp in the user's database record
+    // and checking it during token verification.
+    // Leaving this as a no-op to prevent a false sense of security.
+    logger.error(
       { userId, reason, context: "auth" },
-      "User token blocklist requested (note: only blocks known tokens)",
+      "CRITICAL: blockUserTokens is not implemented. User tokens were NOT invalidated.",
     );
+    // To prevent accidental use, throw an error in non-production environments.
+    if (process.env.NODE_ENV !== "production") {
+      throw new Error("blockUserTokens is not implemented.");
+    }

The blockUserTokens function is an unimplemented placeholder. To prevent misuse
and a false sense of security, mark it as deprecated, log an error when called,
and throw an error in non-production environments.

apps/core/server/services/TokenBlocklistService.ts [90-103]

 /**
  * Block all tokens for a user (logout everywhere)
- * Note: This only blocks tokens we've seen - new tokens won't be affected
- * For complete invalidation, change the user's secret or rotate keys
+ * @deprecated This function is a placeholder and does not provide a secure "logout everywhere" implementation.
+ * A robust solution requires a mechanism like a `tokensValidFrom` timestamp on the user model.
  */
 async blockUserTokens(userId: string, reason?: string): Promise<void> {
-  // We can't block tokens we haven't seen
-  // For a proper "logout everywhere", the user should change their password
-  // or we should track active sessions
-  logger.info(
+  // This is a critical security operation that is NOT implemented.
+  // A proper implementation should invalidate all of a user's sessions,
+  // for example by updating a `tokensValidFrom` timestamp in the user's database record
+  // and checking it during token verification.
+  // Leaving this as a no-op to prevent a false sense of security.
+  logger.error(
     { userId, reason, context: "auth" },
-    "User token blocklist requested (note: only blocks known tokens)",
+    "CRITICAL: blockUserTokens is not implemented. User tokens were NOT invalidated.",
   );
+  // To prevent accidental use, throw an error in non-production environments.
+  if (process.env.NODE_ENV !== "production") {
+    throw new Error("blockUserTokens is not implemented.");
+  }
 }

[Suggestion processed]

Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies that the blockUserTokens function is an unimplemented, high-risk security feature and proposes making it fail loudly to prevent a false sense of security.

High
Fail-closed on token blocklist check failure
Suggestion Impact:The commit implemented the exact security improvement suggested - changing from a fail-open to fail-closed approach by adding a 'return {}' statement to reject authentication when the blocklist check fails, changing the log level from warn to error, and updating the error message to reflect the security-focused approach

code diff:

-        // Don't block auth if blocklist check fails - log and continue
-        logger.warn(
+        // Fail-closed: If blocklist check fails, reject the token for security.
+        logger.error(
           { err: error, context: "auth" },
-          "Failed to check token blocklist, continuing",
-        );
+          "Failed to check token blocklist. Rejecting token as a security precaution.",
+        );
+        return {};
       }

Change the token blocklist check to "fail-closed" for better security. If the
blocklist service is unavailable, reject the authentication attempt instead of
allowing it to proceed.

apps/core/server/plugins/auth.plugin.ts [73-91]

 // Check token blocklist for JWT tokens (not API keys)
 if (!token.startsWith("af_")) {
   try {
     const isBlocked = await tokenBlocklistService.isTokenBlocklisted(token);
     if (isBlocked) {
       logger.info(
         { context: "auth" },
         "Token is blocklisted (revoked/logged out)",
       );
       return {};
     }
   } catch (error) {
-    // Don't block auth if blocklist check fails - log and continue
-    logger.warn(
+    // Fail-closed: If blocklist check fails, reject the token for security.
+    logger.error(
       { err: error, context: "auth" },
-      "Failed to check token blocklist, continuing",
+      "Failed to check token blocklist. Rejecting token as a security precaution.",
     );
+    return {};
   }
 }

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a security risk where the token blocklist check "fails open" and proposes a "fail-closed" approach, which is a critical security best practice.

Medium
Enforce stricter CSRF protection in production
Suggestion Impact:The commit directly implemented the suggestion by adding code to reject state-changing requests that lack Origin/Referer headers in production environments, including the exact error handling and logging logic proposed in the suggestion.

code diff:

-    // Skip for requests without origin (same-origin requests in some browsers)
-    // This is safe because browsers always send Origin on cross-origin requests
+    // In production, reject state-changing requests without an origin header.
+    // This is a stricter CSRF check.
     if (!effectiveOrigin) {
+      if (env.NODE_ENV === "production") {
+        logger.warn(
+          {
+            context: "csrf",
+            method,
+            path: new URL(request.url).pathname,
+          },
+          "CSRF validation failed - missing Origin/Referer header in production",
+        );
+        return new Response(
+          JSON.stringify({
+            error: "CSRF_VALIDATION_FAILED",
+            message: "Missing Origin header",
+          }),
+          {
+            status: 403,
+            headers: { "Content-Type": "application/json" },
+          },
+        );
+      }
+      // In non-production, allow requests without an origin for easier testing.
       return;

Enforce stricter CSRF protection by rejecting state-changing requests that lack
an Origin or Referer header in production environments. This provides a stronger
defense against potential attacks.

apps/core/server/plugins/csrf.plugin.ts [97-101]

-// Skip for requests without origin (same-origin requests in some browsers)
-// This is safe because browsers always send Origin on cross-origin requests
+// In production, reject state-changing requests without an origin header.
+// This is a stricter CSRF check.
 if (!effectiveOrigin) {
+  if (env.NODE_ENV === "production") {
+    logger.warn(
+      {
+        context: "csrf",
+        method,
+        path: new URL(request.url).pathname,
+      },
+      "CSRF validation failed - missing Origin/Referer header in production",
+    );
+    return new Response(
+      JSON.stringify({
+        error: "CSRF_VALIDATION_FAILED",
+        message: "Missing Origin header",
+      }),
+      {
+        status: 403,
+        headers: { "Content-Type": "application/json" },
+      },
+    );
+  }
+  // In non-production, allow requests without an origin for easier testing.
   return;
 }

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: This suggestion correctly identifies a potential weakness in the new CSRF protection and proposes a "fail-closed" strategy for production, which is a security best practice.

Medium
  • Update

The managed version of the open source project PR-Agent is sunsetting on the 1st December 2025. The commercial version of this project will remain available and free to use as a hosted service. Install Qodo.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive set of critical security and quality improvements across the application. The changes, including fixing the JWT test bypass, adding CSRF protection, hardening CORS and rate-limiting, and implementing a JWT blocklist, are excellent and significantly improve the application's security posture. The move from mocks to a real database in integration tests is also a major step forward for test reliability. My review focuses on a few areas to further strengthen these new security features, such as ensuring the token blocklist logic is fully robust and improving test coverage for API responses.

Comment on lines +38 to +55
async blockToken(
token: string,
userId?: string,
reason?: string,
expiresAt?: Date,
): Promise<void> {
const tokenId = this.hashToken(token);

// Default expiry to 24 hours if not specified
const expiry = expiresAt || new Date(Date.now() + 24 * 60 * 60 * 1000);

try {
await db.insert(tokenBlocklist).values({
tokenId,
userId: userId || null,
reason: reason || "logout",
expiresAt: expiry,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The default 24-hour expiration for a blocklisted token is risky. If a JWT's original lifetime is longer than 24 hours, it could become valid again after the blocklist entry is prematurely removed. This would defeat the purpose of the logout/revocation feature. The expiration of the blocklist entry should always match the exp claim of the JWT itself. This change makes the expiresAt parameter mandatory and uses it directly, removing the unsafe default.

  async blockToken(
    token: string,
    expiresAt: Date,
    userId?: string,
    reason?: string,
  ): Promise<void> {
    const tokenId = this.hashToken(token);

    try {
      await db.insert(tokenBlocklist).values({
        tokenId,
        userId: userId || null,
        reason: reason || "logout",
        expiresAt,
      });

Comment on lines +793 to 799
// Should have all fields from database
expect(returnedAsset).toBeDefined();
expect(returnedAsset.name).toBe("Full Asset");
expect(returnedAsset.description).toBe("Test description");
expect(returnedAsset.type).toBe("weapon");
expect(returnedAsset.tier).toBe(3);
expect(returnedAsset.modelUrl).toBe("/gdd-assets/full-asset/model.glb");

// Plus CDN fields merged from database
expect(returnedAsset.cdnUrl).toBe(
"https://cdn.example.com/models/full-asset/model.glb",
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test is named should preserve all asset fields from database, but it doesn't check all the fields of the created asset. For example, the fullAsset is created with a description, but there's no assertion to verify its presence in the response. This could lead to undetected regressions if fields are accidentally removed from the API response in the future. To make the test more robust and align with its name, consider adding assertions for other important fields like description.

      // Should have all fields from database
      expect(returnedAsset).toBeDefined();
      expect(returnedAsset.name).toBe("Full Asset");
      expect(returnedAsset.description).toBe("Test description");
      expect(returnedAsset.type).toBe("weapon");
      expect(returnedAsset.cdnUrl).toBe(
        "https://cdn.example.com/models/full-asset/model.glb",
      );

Comment on lines 84 to 90
} catch (error) {
// Don't block auth if blocklist check fails - log and continue
logger.warn(
{ err: error, context: "auth" },
"Failed to check token blocklist, continuing",
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The token blocklist check currently uses a 'fail-open' approach. If the database connection fails and isTokenBlocklisted throws an error, the check is bypassed, and authentication proceeds. This could allow a revoked token to be used during a database outage. For enhanced security, consider a 'fail-closed' strategy by rejecting the token within the catch block. This prioritizes security over availability in this specific failure scenario.

      } catch (error) {
        // Fail-closed: If blocklist check fails, reject the token for security.
        logger.error(
          { err: error, context: "auth" },
          "Failed to check token blocklist, rejecting token.",
        );
        return {};
      }

Co-authored-by: codiumai-pr-agent-free[bot] <138128286+codiumai-pr-agent-free[bot]@users.noreply.github.com>
Co-authored-by: codiumai-pr-agent-free[bot] <138128286+codiumai-pr-agent-free[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
apps/core/server/models.ts (1)

159-159: Add maxLength constraint to notes field for consistency.

The notes field in AssetMetadata lacks a maxLength constraint, while the same field in AssetUpdate (line 225) has maxLength: 5000. This inconsistency could allow unbounded notes during creation/retrieval but limit them during updates.

Apply this diff to enforce consistent validation:

-  notes: t.Optional(t.String()), // User notes for this asset
+  notes: t.Optional(t.String({ maxLength: 5000 })), // User notes for this asset
apps/core/server/plugins/rate-limiting.plugin.ts (2)

109-113: Outdated comment about development mode.

The comment on line 112 says "Automatically disabled in development mode" but the new logic keeps rate limiting enabled by default in all environments (with higher limits in dev/test). Update to reflect the actual behavior.

 /**
  * Rate Limiting Plugin
  * Applies global rate limiting and endpoint-specific limits
- * Automatically disabled in development mode
+ * Rate limits scale by environment (10x in dev, 100x in test)
+ * Can be disabled with DISABLE_RATE_LIMITING=true
  */

156-158: Fallback to hostname creates shared rate limit bucket.

When IP detection fails, falling back to new URL(req.url).hostname means all requests to the same host share one rate limit bucket, which could either:

  1. Block legitimate users if one abuser hits the limit
  2. Be easily bypassed since all users share the quota

Consider using a unique request identifier or returning a more restrictive fallback:

-          // Last resort: use request URL
-          return new URL(req.url).hostname || "unknown";
+          // Last resort: use a unique identifier per request
+          // This is restrictive but prevents abuse when IP detection fails
+          return `unknown-${Date.now()}-${Math.random()}`;

Alternatively, log a warning and apply stricter limits for "unknown" clients.

apps/core/__tests__/integration/api/routes/assets.test.ts (1)

189-201: Test deletes all assets but may affect other concurrent test runs.

db.delete(assets).execute() deletes ALL assets in the database, not just test assets. This could cause issues if:

  1. Tests run in parallel
  2. Other test suites share the same database

Consider scoping the deletion to test assets only.

-      // Delete all test assets from real database
-      await db.delete(assets).execute();
+      // Delete only test assets to avoid affecting other tests
+      await db.delete(assets).where(
+        eq(assets.id, "test-asset-1")
+      ).execute();
+      await db.delete(assets).where(
+        eq(assets.id, "test-asset-2")
+      ).execute();
🧹 Nitpick comments (9)
apps/core/src/components/three-viewer/REFACTORING_PLAN.ts (1)

1-52: Consider using Markdown format for documentation.

While this refactoring plan is well-structured and thorough, using a .ts file extension for pure documentation is unconventional. This file contains no executable TypeScript code—only comments.

Consider these alternatives:

  • Move to REFACTORING_PLAN.md in the same directory or a docs/ folder
  • Create an ADR (Architecture Decision Record) if your project uses them
  • Document this in a GitHub issue or project board for better visibility and tracking

Additionally, the metrics on lines 45-48 (ref/state/effect counts) are point-in-time snapshots that may become outdated if the ThreeViewer component changes before refactoring begins.

</comment_end>

apps/core/server/plugins/security-headers.ts (2)

37-38: X-XSS-Protection is deprecated in modern browsers.

This header is ignored by modern browsers that have built-in XSS protections. Consider implementing a Content-Security-Policy (CSP) header instead for comprehensive XSS protection:

// Example CSP - adjust based on your needs
set.headers["Content-Security-Policy"] = 
  "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';";

Not blocking since X-XSS-Protection doesn't cause harm and provides backward compatibility.


47-51: Consider adding preload directive for HSTS submission.

If you plan to submit to the HSTS preload list for maximum security, the directive requires preload:

-    set.headers["Strict-Transport-Security"] =
-      "max-age=31536000; includeSubDomains";
+    set.headers["Strict-Transport-Security"] =
+      "max-age=31536000; includeSubDomains; preload";

Only add preload if you're certain all subdomains support HTTPS and you intend to submit to hstspreload.org.

apps/core/.env.example (1)

98-98: Clarify minimum length requirement for WEBHOOK_SECRET.

The comment says "32_chars_minimum" but the placeholder value your_webhook_secret_32_chars_minimum is 36 characters and could be confused as the actual minimum. Consider:

-WEBHOOK_SECRET=your_webhook_secret_32_chars_minimum
+WEBHOOK_SECRET=  # Generate with: openssl rand -base64 32
apps/core/server/plugins/csrf.plugin.ts (1)

82-129: CSRF validation logic is secure with minor improvement opportunity.

The origin validation correctly:

  • Extracts origin from Origin or Referer headers
  • Skips validation for same-origin requests (when Origin is absent)
  • Returns 403 for disallowed origins with comprehensive logging

Optional defensive improvement: Line 112's new URL(request.url).pathname could be wrapped in a try-catch if Elysia doesn't guarantee valid URLs, though this is likely unnecessary since Elysia validates requests.

apps/core/server/services/TokenBlocklistService.ts (2)

61-67: Avoid using any type for error handling.

Per coding guidelines, any types should not be used. Use a typed approach for PostgreSQL error handling.

-    } catch (error: any) {
+    } catch (error: unknown) {
       // Ignore duplicate key errors (token already blocklisted)
-      if (error.code === "23505") {
+      if (error instanceof Error && 'code' in error && (error as { code: string }).code === "23505") {
         return;
       }
       throw error;

Alternatively, create a type guard for PostgreSQL errors:

function isPostgresError(error: unknown): error is { code: string } {
  return typeof error === 'object' && error !== null && 'code' in error;
}

90-103: Clarify or implement blockUserTokens functionality.

This method logs a message but performs no actual blocking. If this is intentional design (as noted in the comment), consider:

  1. Marking the method as deprecated or removing it until implemented
  2. Throwing a NotImplementedError to make the limitation explicit at runtime
  3. Adding a TODO comment with an issue reference

The current implementation could mislead callers into thinking tokens were blocked.

apps/core/server/plugins/auth.plugin.ts (1)

181-186: Consider using static import for crypto module.

The dynamic await import("crypto") on every test-mode authentication adds unnecessary overhead. The crypto module is already imported at the top of TokenBlocklistService.ts, suggesting it's available in this environment.

Add to existing imports at the top of the file:

import { createHmac } from "crypto";

Then simplify the verification:

-        const crypto = await import("crypto");
-        const signatureInput = `${parts[0]}.${parts[1]}`;
-        const expectedSignature = crypto
-          .createHmac("sha256", testSecret)
+        const signatureInput = `${parts[0]}.${parts[1]}`;
+        const expectedSignature = createHmac("sha256", testSecret)
           .update(signatureInput)
           .digest("base64url");
apps/core/__tests__/integration/api/routes/assets.test.ts (1)

103-140: Test isolation approach has a potential race condition.

The afterEach re-creates assets only if they don't exist, which is good for isolation. However, if a test modifies an asset (e.g., changes its name), subsequent tests may see stale data since re-creation only happens when the asset is deleted.

Consider either:

  1. Always deleting and re-creating test assets in afterEach
  2. Or resetting asset fields to known values after each test

If full isolation is needed:

  afterEach(async () => {
-    // Re-create test assets if they were deleted during tests
-    // Check if assets still exist
-    const asset1Exists = await db.query.assets.findFirst({
-      where: eq(assets.id, "test-asset-1"),
-    });
+    // Always reset test assets to known state
+    await db.delete(assets).where(eq(assets.id, "test-asset-1")).execute();
+    await db.delete(assets).where(eq(assets.id, "test-asset-2")).execute();
+
+    testAsset1 = await createTestAsset(testUser1.user.id, {
+      id: "test-asset-1",
+      // ... original values
+    });
+    testAsset2 = await createTestAsset(testAdmin.user.id, {
+      id: "test-asset-2",
+      // ... original values
+    });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c563f9 and 51a22c5.

📒 Files selected for processing (18)
  • apps/core/.env.example (1 hunks)
  • apps/core/__tests__/helpers/auth.ts (2 hunks)
  • apps/core/__tests__/integration/api/routes/assets.test.ts (13 hunks)
  • apps/core/server/api-elysia.ts (4 hunks)
  • apps/core/server/db/migrations/0032_fix_schema_drift.sql (1 hunks)
  • apps/core/server/db/migrations/0033_add_token_blocklist.sql (1 hunks)
  • apps/core/server/db/migrations/meta/_journal.json (1 hunks)
  • apps/core/server/db/schema/index.ts (3 hunks)
  • apps/core/server/db/schema/token-blocklist.schema.ts (1 hunks)
  • apps/core/server/models.ts (3 hunks)
  • apps/core/server/plugins/auth.plugin.ts (5 hunks)
  • apps/core/server/plugins/cron.plugin.ts (4 hunks)
  • apps/core/server/plugins/csrf.plugin.ts (1 hunks)
  • apps/core/server/plugins/rate-limiting.plugin.ts (2 hunks)
  • apps/core/server/plugins/security-headers.ts (2 hunks)
  • apps/core/server/services/TokenBlocklistService.ts (1 hunks)
  • apps/core/src/components/three-viewer/REFACTORING_PLAN.ts (1 hunks)
  • apps/core/vite.config.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> in the Asset-Forge project
Use const over let, never use var
Use template literals for strings instead of string concatenation

Files:

  • apps/core/vite.config.ts
  • apps/core/server/db/schema/index.ts
  • apps/core/server/db/schema/token-blocklist.schema.ts
  • apps/core/server/api-elysia.ts
  • apps/core/server/plugins/cron.plugin.ts
  • apps/core/server/plugins/rate-limiting.plugin.ts
  • apps/core/server/services/TokenBlocklistService.ts
  • apps/core/server/plugins/security-headers.ts
  • apps/core/server/models.ts
  • apps/core/server/plugins/csrf.plugin.ts
  • apps/core/__tests__/integration/api/routes/assets.test.ts
  • apps/core/__tests__/helpers/auth.ts
  • apps/core/server/plugins/auth.plugin.ts
  • apps/core/src/components/three-viewer/REFACTORING_PLAN.ts
**/*.{ts,tsx,js,jsx,json,css,scss}

📄 CodeRabbit inference engine (CLAUDE.md)

Use 2-space indentation for all code

Files:

  • apps/core/vite.config.ts
  • apps/core/server/db/migrations/meta/_journal.json
  • apps/core/server/db/schema/index.ts
  • apps/core/server/db/schema/token-blocklist.schema.ts
  • apps/core/server/api-elysia.ts
  • apps/core/server/plugins/cron.plugin.ts
  • apps/core/server/plugins/rate-limiting.plugin.ts
  • apps/core/server/services/TokenBlocklistService.ts
  • apps/core/server/plugins/security-headers.ts
  • apps/core/server/models.ts
  • apps/core/server/plugins/csrf.plugin.ts
  • apps/core/__tests__/integration/api/routes/assets.test.ts
  • apps/core/__tests__/helpers/auth.ts
  • apps/core/server/plugins/auth.plugin.ts
  • apps/core/src/components/three-viewer/REFACTORING_PLAN.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript strict mode everywhere
No any or unknown types in TypeScript code
Use explicit return types on public methods in TypeScript

Files:

  • apps/core/vite.config.ts
  • apps/core/server/db/schema/index.ts
  • apps/core/server/db/schema/token-blocklist.schema.ts
  • apps/core/server/api-elysia.ts
  • apps/core/server/plugins/cron.plugin.ts
  • apps/core/server/plugins/rate-limiting.plugin.ts
  • apps/core/server/services/TokenBlocklistService.ts
  • apps/core/server/plugins/security-headers.ts
  • apps/core/server/models.ts
  • apps/core/server/plugins/csrf.plugin.ts
  • apps/core/__tests__/integration/api/routes/assets.test.ts
  • apps/core/__tests__/helpers/auth.ts
  • apps/core/server/plugins/auth.plugin.ts
  • apps/core/src/components/three-viewer/REFACTORING_PLAN.ts
apps/core/server/db/migrations/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

Generate database migration with bun run db:generate after editing schema

Files:

  • apps/core/server/db/migrations/0032_fix_schema_drift.sql
  • apps/core/server/db/migrations/0033_add_token_blocklist.sql
apps/core/server/db/schema/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Edit TypeScript schema in apps/core/server/db/schema/ when schema changes are needed

Files:

  • apps/core/server/db/schema/index.ts
  • apps/core/server/db/schema/token-blocklist.schema.ts
apps/core/server/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

apps/core/server/**/*.ts: Research with Deepwiki before adding new endpoints in Elysia
Define API types in service file with TypeBox validation in Elysia endpoints
Implement business logic in service class for API endpoints
Create route handler with TypeBox validation for API endpoints
Add Swagger documentation to new API endpoints
Follow REST conventions for API endpoints

Files:

  • apps/core/server/db/schema/index.ts
  • apps/core/server/db/schema/token-blocklist.schema.ts
  • apps/core/server/api-elysia.ts
  • apps/core/server/plugins/cron.plugin.ts
  • apps/core/server/plugins/rate-limiting.plugin.ts
  • apps/core/server/services/TokenBlocklistService.ts
  • apps/core/server/plugins/security-headers.ts
  • apps/core/server/models.ts
  • apps/core/server/plugins/csrf.plugin.ts
  • apps/core/server/plugins/auth.plugin.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Use bun test instead of jest or vitest for testing
NO MOCKS for internal code (database, HTTP handlers, business logic) - use real PostgreSQL database with test data isolation
SMART MOCKS for external APIs (OpenAI, Meshy, Privy) to avoid costs and ensure reliability

Files:

  • apps/core/__tests__/integration/api/routes/assets.test.ts
🧬 Code graph analysis (6)
apps/core/server/plugins/cron.plugin.ts (4)
apps/core/server/services/GenerationPipelineService.ts (1)
  • generationPipelineService (333-333)
apps/core/server/db/db.ts (1)
  • db (95-98)
apps/core/server/db/schema/users.schema.ts (1)
  • activityLog (143-183)
apps/core/server/services/TokenBlocklistService.ts (1)
  • tokenBlocklistService (107-107)
apps/core/server/plugins/rate-limiting.plugin.ts (1)
apps/core/server/config/env.ts (1)
  • env (320-320)
apps/core/server/services/TokenBlocklistService.ts (3)
apps/core/server/db/db.ts (1)
  • db (95-98)
apps/core/server/db/schema/token-blocklist.schema.ts (1)
  • tokenBlocklist (14-35)
apps/core/server/routes/content/shared.ts (1)
  • logger (23-23)
apps/core/server/plugins/csrf.plugin.ts (2)
apps/core/server/config/env.ts (1)
  • env (320-320)
apps/core/server/routes/content/shared.ts (1)
  • logger (23-23)
apps/core/__tests__/integration/api/routes/assets.test.ts (3)
apps/core/__tests__/helpers/db.ts (2)
  • createTestUser (125-154)
  • createTestAsset (168-188)
apps/core/server/db/db.ts (1)
  • db (95-98)
apps/core/server/db/schema/assets.schema.ts (1)
  • assets (25-145)
apps/core/server/plugins/auth.plugin.ts (3)
apps/core/server/services/TokenBlocklistService.ts (1)
  • tokenBlocklistService (107-107)
apps/core/server/routes/content/shared.ts (3)
  • logger (23-23)
  • Elysia (50-50)
  • ForbiddenError (55-55)
apps/core/server/config/env.ts (1)
  • env (320-320)
🪛 dotenv-linter (4.0.0)
apps/core/.env.example

[warning] 11-11: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 22-22: [UnorderedKey] The API_PORT key should go before the PORT key

(UnorderedKey)


[warning] 72-72: [UnorderedKey] The QDRANT_API_KEY key should go before the QDRANT_URL key

(UnorderedKey)


[warning] 87-87: [UnorderedKey] The CDN_API_KEY key should go before the CDN_URL key

(UnorderedKey)


[warning] 89-89: [UnorderedKey] The AUTO_PUBLISH_TO_CDN key should go before the CDN_API_KEY key

(UnorderedKey)


[warning] 99-99: [UnorderedKey] The CDN_WEBHOOK_ENABLED key should go before the WEBHOOK_SECRET key

(UnorderedKey)


[warning] 112-112: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Seer Code Review
🔇 Additional comments (30)
apps/core/src/components/three-viewer/REFACTORING_PLAN.ts (1)

54-54: Empty export is unnecessary for documentation.

The export {} pattern is typically used to force TypeScript to treat a file as a module. However, since this file is pure documentation, module scope provides no benefit. If converted to a Markdown file as suggested above, this line would be unnecessary.

</comment_end>

apps/core/vite.config.ts (1)

118-120: Good security practice to disable sourcemaps in production.

The conditional logic correctly enables sourcemaps for development/test debugging while hiding source code in production builds. Ensure NODE_ENV=production is set during CI/CD build steps to take effect.

apps/core/server/plugins/security-headers.ts (1)

17-17: LGTM!

Using const with environment check at module load time is efficient and follows the coding guidelines.

apps/core/.env.example (2)

1-7: Well-structured environment configuration template.

The organization with clear sections, required/optional markers, and the deployment checklist is excellent for developer onboarding and production readiness.


36-39: Good guidance on encryption secret generation.

The openssl rand -base64 32 instruction is helpful. Consider adding a note that this secret must be consistent across deployments to decrypt existing API keys.

apps/core/server/plugins/rate-limiting.plugin.ts (1)

54-63: LGTM - Well-defined base rate limits.

The base limits are reasonable for production, and the multiplier pattern provides appropriate scaling for development and testing environments.

apps/core/server/db/migrations/0032_fix_schema_drift.sql (1)

1-12: Migration 0032 duplicates the table drop already executed in migration 0007.

Migration 0007 already dropped admin_whitelist with CASCADE. Migration 0032 attempts to drop the same table again 25 versions later. While the IF EXISTS clauses make this harmless, it's redundant and indicates either:

  • A migration history issue if 0007 wasn't applied, or
  • Unnecessary duplication if 0007 was successfully applied

The original review's concerns about data loss and backups are misdirected—any data in admin_whitelist would have been lost in migration 0007, not 0032.

Likely an incorrect or invalid review comment.

apps/core/server/db/migrations/meta/_journal.json (1)

228-242: LGTM! Migration journal entries added correctly.

The new migration entries follow the established pattern and properly track the schema drift fix and token blocklist additions.

apps/core/server/db/schema/index.ts (1)

45-46: LGTM! Token blocklist schema properly integrated.

The new schema is correctly exported, imported, and spread into the central schema object, following the established pattern used by other schemas in this file.

Also applies to: 62-62, 78-78

apps/core/server/plugins/cron.plugin.ts (3)

20-20: LGTM! Import follows established pattern.

The TokenBlocklistService import is consistent with other service imports in the file.


99-129: LGTM! Activity log cleanup properly implemented.

The cron job correctly:

  • Uses dynamic imports to minimize memory footprint
  • Applies a reasonable 90-day retention policy
  • Handles potential null rowCount with fallback to 0
  • Includes comprehensive error handling and logging

131-146: LGTM! Token blocklist cleanup properly delegated.

The cron job correctly delegates to the service layer for cleanup logic, maintaining good separation of concerns with proper error handling and logging.

apps/core/__tests__/helpers/auth.ts (1)

23-46: LGTM! JWT signing correctly implemented.

The test helper now generates properly signed JWTs using:

  • HMAC-SHA256 matching the HS256 algorithm
  • Base64url encoding (JWT spec compliant)
  • Correct signature computation over header.body
  • Reasonable token expiration (1 hour)

This ensures test tokens behave like real JWTs and can be validated cryptographically.

apps/core/server/api-elysia.ts (4)

42-42: LGTM! CSRF plugin import added.


255-293: LGTM! Secure CORS origin handling with no wildcard fallback.

The dynamic origin function correctly:

  • Requires explicit origin configuration (no wildcard)
  • Rejects requests when origins are unconfigured
  • Adds localhost origins only in non-production environments
  • Logs warnings for rejected origins

The NODE_ENV !== "production" check (line 270) allows localhost in staging/test environments, which is appropriate for flexibility during deployment validation.


303-306: LGTM! Middleware order is correct.

The plugin registration order is appropriate:

  1. Rate limiting (prevent abuse)
  2. Auth (identify user/API key)
  3. CSRF (validate origin, with API key bypass)

This ensures CSRF can properly detect and skip API key requests.


325-334: Debug plugin properly restricted to non-production.

The conditional registration correctly prevents debug endpoints from being exposed in production environments. The pattern temporarily breaks the fluent chain (line 337) but is functionally correct.

apps/core/server/plugins/csrf.plugin.ts (2)

46-60: LGTM! Origin matching logic is robust.

The function correctly:

  • Rejects null/missing origins
  • Performs exact matches first (efficient)
  • Handles trailing slash variations (prevents false rejections)

66-80: LGTM! Method filtering and API key bypass correctly implemented.

The plugin appropriately:

  • Only validates state-changing methods (POST, PUT, DELETE, PATCH)
  • Skips CSRF checks for API key authentication (machine-to-machine)
  • Uses defensive optional chaining for header access
apps/core/server/db/schema/token-blocklist.schema.ts (1)

37-38: LGTM! Type exports follow Drizzle ORM conventions.

The select and insert types are properly inferred and clearly named for use throughout the application.

apps/core/server/services/TokenBlocklistService.ts (2)

1-18: LGTM - Secure token hashing implementation.

Good security practice to hash tokens before storage using SHA-256, preventing exposure of raw tokens if the database is compromised.


20-33: LGTM - Clean blocklist lookup.

The implementation correctly hashes the token before lookup and uses a limit(1) for efficiency.

apps/core/server/plugins/auth.plugin.ts (3)

73-91: LGTM - Resilient blocklist integration.

Good design choices:

  1. Only checking blocklist for JWT tokens (not API keys)
  2. Graceful degradation - auth continues if blocklist check fails
  3. Appropriate logging for both blocked tokens and errors

449-470: LGTM - Proper admin role enforcement.

The updated requireAdminGuard now correctly:

  1. Requires authentication first
  2. Checks role === "admin" explicitly
  3. Throws ForbiddenError (403) for non-admin users
  4. Logs access attempts for audit trail

160-194: NODE_ENV validation confirmed—test-mode JWT verification is secure.

The Zod enum validation in apps/core/server/config/env.ts (lines 20-22) strictly constrains NODE_ENV to ["development", "production", "test"], making it impossible to manipulate. The test-mode code path is reliably guarded and cannot be accidentally triggered in production. The non-constant-time string comparison in the JWT verification is acceptable for test-mode only.

apps/core/__tests__/integration/api/routes/assets.test.ts (5)

4-7: LGTM - Good alignment with testing guidelines.

The comment correctly references the CLAUDE.md testing policy about using real databases instead of mocks for internal services.


165-187: Test assertions for legacy fields should use strict checks.

The assertions at lines 185-186 verify legacy fields don't exist, which is good for validating CDN migration. The test properly validates that cdnUrl contains the expected domain.


634-683: LGTM - Comprehensive CDN URL validation tests.

These tests properly validate:

  1. CDN URL fields are populated from database records
  2. All CDN-related fields (cdnUrl, cdnThumbnailUrl, cdnConceptArtUrl, cdnFiles) are correctly returned
  3. Test data is created with real database operations

685-715: Good edge case coverage for assets without CDN URLs.

Testing the null/falsy case for cdnUrl ensures the API handles both CDN-enabled and local-only assets correctly.


822-836: Smart approach to validate CDN migration completeness.

Checking the entire JSON response string for legacy field names (modelUrl, filePath, /gdd-assets/) is an effective way to catch any accidental leakage of deprecated fields.

Comment on lines 14 to 35
export const tokenBlocklist = pgTable(
"token_blocklist",
{
id: uuid("id").defaultRandom().primaryKey(),
// Token identifier (jti claim from JWT or hash of token)
tokenId: varchar("token_id", { length: 255 }).notNull().unique(),
// User who owned this token (for audit)
userId: uuid("user_id"),
// Reason for blocklisting
reason: varchar("reason", { length: 100 }),
// When the token was blocklisted
createdAt: timestamp("created_at", { withTimezone: true })
.defaultNow()
.notNull(),
// When the original token expires (for cleanup)
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
},
(table) => [
index("idx_token_blocklist_token_id").on(table.tokenId),
index("idx_token_blocklist_expires_at").on(table.expiresAt),
],
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove redundant index on tokenId (consistent with SQL migration issue).

Line 19 declares tokenId with .unique(), which automatically creates a unique index. Line 32 then explicitly creates another index on the same column, resulting in duplicate indexes.

Remove the redundant index definition:

   (table) => [
-    index("idx_token_blocklist_token_id").on(table.tokenId),
     index("idx_token_blocklist_expires_at").on(table.expiresAt),
   ],
 );

This issue is consistent with the redundant CREATE INDEX in the SQL migration (apps/core/server/db/migrations/0033_add_token_blocklist.sql line 14).

🤖 Prompt for AI Agents
In apps/core/server/db/schema/token-blocklist.schema.ts around lines 14 to 35,
the tokenId column is declared with .unique() which already creates a unique
index, but there is an explicit index added later on the same column; remove the
redundant index definition inside the pgTable index array (delete the
index("idx_token_blocklist_token_id").on(table.tokenId) entry) and also update
the corresponding SQL migration
(apps/core/server/db/migrations/0033_add_token_blocklist.sql) to remove the
duplicate CREATE INDEX for token_id so only one index is created.

Comment on lines 40 to 52
* Rate limiting is now ALWAYS enabled by default
* Can only be disabled explicitly with DISABLE_RATE_LIMITING=true (for specific tests)
*/
const RATE_LIMITING_ENABLED =
env.NODE_ENV === "production" || env.ENABLE_RATE_LIMITING === true;
const RATE_LIMITING_ENABLED = process.env.DISABLE_RATE_LIMITING !== "true";

/**
* Multiplier for rate limits based on environment
* - Production: 1x (base limits)
* - Development: 10x (relaxed for testing)
* - Test: 100x (very relaxed for automated tests)
*/
const RATE_LIMIT_MULTIPLIER =
env.NODE_ENV === "production" ? 1 : env.NODE_ENV === "test" ? 100 : 10;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Inconsistent environment variable access pattern.

Line 43 uses process.env.DISABLE_RATE_LIMITING while line 52 uses env.NODE_ENV from the imported config. This inconsistency could cause issues if the env config applies transformations or validation.

-const RATE_LIMITING_ENABLED = process.env.DISABLE_RATE_LIMITING !== "true";
+const RATE_LIMITING_ENABLED = env.DISABLE_RATE_LIMITING !== true;

Verify that DISABLE_RATE_LIMITING is defined in your env schema (apps/core/server/config/env.ts) and use the parsed env object consistently.


🏁 Script executed:

#!/bin/bash
# Check if DISABLE_RATE_LIMITING is defined in env schema
echo "=== Checking env schema for DISABLE_RATE_LIMITING ==="
rg -n "DISABLE_RATE_LIMITING" apps/core/server/config/

Length of output: 53


Now I need to look at the actual env config and rate-limiting plugin files to verify the issue:


🏁 Script executed:

#!/bin/bash
# Look at the env schema file to understand its structure
echo "=== Checking env schema file ==="
cat apps/core/server/config/env.ts 2>/dev/null | head -100

echo -e "\n=== Checking imports in rate-limiting plugin ==="
head -20 apps/core/server/plugins/rate-limiting.plugin.ts

echo -e "\n=== Checking all DISABLE_RATE_LIMITING usages ==="
rg -n "DISABLE_RATE_LIMITING" --type ts --type tsx

Length of output: 4097


🏁 Script executed:

#!/bin/bash
# Search for the complete env schema to see how optional flags are typed
echo "=== Checking complete env schema for optional flags ==="
cat apps/core/server/config/env.ts | grep -A 2 "\.optional\|\.default\|\.boolean" | head -40

# Look for test-related environment variables
echo -e "\n=== Searching for test-related env variables ==="
rg "test|TEST" apps/core/server/config/env.ts -n

# Check rate-limiting plugin full context around line 43 and 52
echo -e "\n=== Context around DISABLE_RATE_LIMITING and NODE_ENV usage ==="
sed -n '35,60p' apps/core/server/plugins/rate-limiting.plugin.ts

Length of output: 2052


Add DISABLE_RATE_LIMITING to env schema and use consistent environment variable access.

Line 43 uses direct process.env.DISABLE_RATE_LIMITING access while line 52 uses validated env.NODE_ENV from the schema. This inconsistency violates the application's env validation pattern.

Add DISABLE_RATE_LIMITING to apps/core/server/config/env.ts:

DISABLE_RATE_LIMITING: z.string().optional().transform((val) => val === "true").default("false"),

Then update line 43 to use the validated env object:

const RATE_LIMITING_ENABLED = !env.DISABLE_RATE_LIMITING;
🤖 Prompt for AI Agents
In apps/core/server/plugins/rate-limiting.plugin.ts around lines 40 to 52, the
code inconsistently reads DISABLE_RATE_LIMITING from process.env while NODE_ENV
is read from the validated env schema; add DISABLE_RATE_LIMITING to
apps/core/server/config/env.ts as a validated optional boolean (transforming the
"true" string to true and defaulting to false) and then replace the process.env
access with the schema-backed value by using env.DISABLE_RATE_LIMITING (so
RATE_LIMITING_ENABLED becomes the negation of env.DISABLE_RATE_LIMITING).

Per Elysia best practices, add createTestClient() and createAuthTestClient()
helpers that use Eden Treaty for zero-overhead, type-safe testing.

Eden Treaty is the RECOMMENDED approach because:
- Full TypeScript autocomplete for routes
- Compile-time type validation
- Zero network overhead (calls app.handle() internally)
- No manual Request construction needed

The existing app.handle() utilities remain available for low-level testing.

See: https://elysiajs.com/patterns/unit-test
See: https://elysiajs.com/eden/treaty/unit-test
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 30 to 33
// In development, allow localhost
if (env.NODE_ENV === "development") {
origins.push(
"http://localhost:3000",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge CSRF plugin rejects localhost when NODE_ENV=test

The CSRF whitelist only adds localhost origins when NODE_ENV === "development" (csrf.plugin.ts lines 30-33), while the CORS middleware still allows the same localhost origins for any non‑production environment (api-elysia.ts lines 269-276). When the server runs under the normal test/CI setting NODE_ENV=test without FRONTEND_URL/CORS_ALLOWED_ORIGINS configured, the allowedOrigins array stays empty and every browser POST/PUT/DELETE from http://localhost:3000/3004 will hit the CSRF 403 even though CORS preflight succeeds, effectively blocking state‑changing requests in test/staging unless extra env vars are supplied.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/core/server/plugins/auth.plugin.ts (1)

96-96: Consider using a singleton ApiKeyService instance.

This instantiation-per-request pattern was previously flagged as a potential performance issue. If thread safety and statelessness allow, consider using a shared instance.

🧹 Nitpick comments (2)
apps/core/__tests__/helpers/api.ts (1)

62-65: Handle potential null privyUserId safely.

According to the AuthUser type definition, privyUserId can be null, but createAuthHeader expects a string. While authenticated test users typically have a non-null privyUserId, the type system doesn't enforce this.

Add a runtime check or non-null assertion:

  return treaty(app, {
    headers: {
-      Authorization: createAuthHeader(
-        user.privyUserId,
-        user.email || undefined,
-      ),
+      Authorization: createAuthHeader(
+        user.privyUserId ?? '',
+        user.email || undefined,
+      ),
    },
  });

Alternatively, throw an error if privyUserId is null to make the requirement explicit:

+  if (!user.privyUserId) {
+    throw new Error('createAuthTestClient requires a user with a valid privyUserId');
+  }
  return treaty(app, {
    headers: {
      Authorization: createAuthHeader(
        user.privyUserId,
        user.email || undefined,
      ),
    },
  });
apps/core/server/plugins/auth.plugin.ts (1)

182-182: Import crypto module at the top of the file.

The crypto module is a built-in Node.js module and doesn't need dynamic import. This adds unnecessary overhead on every request in test mode.

Apply this change:

+import crypto from "crypto";

Then update line 182-187:

-        const crypto = await import("crypto");
         const signatureInput = `${parts[0]}.${parts[1]}`;
         const expectedSignature = crypto
           .createHmac("sha256", testSecret)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51a22c5 and d95324f.

📒 Files selected for processing (3)
  • apps/core/__tests__/helpers/api.ts (1 hunks)
  • apps/core/server/plugins/auth.plugin.ts (5 hunks)
  • apps/core/server/services/TokenBlocklistService.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/core/server/services/TokenBlocklistService.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> in the Asset-Forge project
Use const over let, never use var
Use template literals for strings instead of string concatenation

Files:

  • apps/core/__tests__/helpers/api.ts
  • apps/core/server/plugins/auth.plugin.ts
**/*.{ts,tsx,js,jsx,json,css,scss}

📄 CodeRabbit inference engine (CLAUDE.md)

Use 2-space indentation for all code

Files:

  • apps/core/__tests__/helpers/api.ts
  • apps/core/server/plugins/auth.plugin.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript strict mode everywhere
No any or unknown types in TypeScript code
Use explicit return types on public methods in TypeScript

Files:

  • apps/core/__tests__/helpers/api.ts
  • apps/core/server/plugins/auth.plugin.ts
apps/core/server/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

apps/core/server/**/*.ts: Research with Deepwiki before adding new endpoints in Elysia
Define API types in service file with TypeBox validation in Elysia endpoints
Implement business logic in service class for API endpoints
Create route handler with TypeBox validation for API endpoints
Add Swagger documentation to new API endpoints
Follow REST conventions for API endpoints

Files:

  • apps/core/server/plugins/auth.plugin.ts
🧬 Code graph analysis (1)
apps/core/__tests__/helpers/api.ts (2)
apps/core/server/types/auth.ts (1)
  • AuthUser (7-17)
apps/core/__tests__/helpers/auth.ts (1)
  • createAuthHeader (52-55)
🔇 Additional comments (1)
apps/core/server/plugins/auth.plugin.ts (1)

460-471: LGTM! Proper admin role enforcement.

The admin guard correctly implements role-based access control:

  • Checks authentication first
  • Validates role === "admin"
  • Throws appropriate ForbiddenError (403) when access denied
  • Includes helpful logging for audit trails

This aligns with the PR's security objectives to implement RBAC for admin routes.

Comment on lines 42 to 46
export function createTestClient<
T extends Elysia<any, any, any, any, any, any>,
>(app: T) {
return treaty(app);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Add explicit return type and avoid any in generic constraints.

This function violates two coding guidelines:

  1. Uses any types in the generic constraint
  2. Missing explicit return type

As per coding guidelines, TypeScript strict mode requires explicit return types on public methods and prohibits any types.

Consider using the actual Elysia type parameters or unknown with proper type constraints. Also add an explicit return type. Please verify the correct type signature for treaty return type:


🌐 Web query:

What is the return type of the treaty function from @elysiajs/eden package?

💡 Result:

The TypeScript return type is Treaty.Create — i.e. treaty is declared as
(domain: string | App, config?: Treaty.Config) => Treaty.Create. [1]

Sources:
[1] @elysiajs/eden dist types (index.d.ts).


🏁 Script executed:

#!/bin/bash
# Check imports and usage of Elysia and treaty in the test file
rg -n "import.*Elysia|import.*treaty" apps/core/__tests__/helpers/api.ts -B2 -A2

Length of output: 206


🏁 Script executed:

#!/bin/bash
# Search for other uses of createTestClient or similar patterns in codebase
rg -n "Treaty\.Create|treaty\(" apps/core/__tests__ -C2 --type=ts | head -40

Length of output: 1038


🏁 Script executed:

#!/bin/bash
# Check if there are type definitions or other createTestClient-like functions
rg -n "extends Elysia" apps/core --type=ts -C2

Length of output: 647


🏁 Script executed:

#!/bin/bash
# Check @elysiajs/eden package documentation for proper typing
echo "Checking if there's a way to properly type Elysia without 'any'..."
echo "The treaty function signature: <const App extends Elysia>(domain: string | App, config?: Treaty.Config) => Treaty.Create<App>"

Length of output: 192


🏁 Script executed:

#!/bin/bash
# Verify the exact location of the code section
cat << 'EOF' | head -1
File: apps/core/__tests__/helpers/api.ts
Lines: 42-46 (function definition)
Current code uses:
- Generic constraint: T extends Elysia<any, any, any, any, any, any>
- No explicit return type
- treaty(app) call

Required fixes per coding guidelines:
1. Explicit return type: Treaty.Create<T>
2. Address use of 'any' types
EOF

Length of output: 40


Add explicit return type and remove any type parameters from generic constraint.

This function violates two coding guidelines:

  1. Uses any in the generic constraint (5 instances)
  2. Missing explicit return type on public method

Replace the generic constraint T extends Elysia<any, any, any, any, any, any> with T extends Elysia and add the explicit return type Treaty.Create<T>.

export function createTestClient<
  T extends Elysia,
>(app: T): Treaty.Create<T> {
  return treaty(app);
}

Also add the missing import: import type { Treaty } from "@elysiajs/eden";

Note: The same issues exist in createAuthTestClient on line 57–62 and should be fixed identically.

Co-authored-by: codiumai-pr-agent-free[bot] <138128286+codiumai-pr-agent-free[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
apps/core/server/plugins/csrf.plugin.ts (3)

30-37: NODE_ENV check inconsistency already flagged.

Multiple previous reviewers have identified that line 31's NODE_ENV === "development" check is inconsistent with the CORS configuration in api-elysia.ts (which uses NODE_ENV !== "production"). This causes localhost origins to be blocked in test and staging environments even though CORS allows them.


83-95: Origin/Referer header format validation concern already raised.

A previous reviewer noted that the origin and referer headers are used without validating their format (beyond the try-catch for URL parsing). While the try-catch on lines 89-94 prevents crashes from malformed referer URLs, it doesn't validate that the extracted origin conforms to expected patterns before using it in security decisions on line 126.


97-122: Missing origin header handling already addressed.

Previous reviewers raised concerns about skipping CSRF validation for requests without an Origin/Referer header. The current implementation addresses this by enforcing stricter checks in production (lines 100-118), rejecting such requests with a 403, while allowing them in non-production environments for easier testing.

🧹 Nitpick comments (2)
apps/core/server/plugins/csrf.plugin.ts (2)

88-95: Consider logging referer parsing failures for debugging.

The empty catch block silently swallows URL parsing errors when extracting the origin from the Referer header. While this is safe from a security perspective (failing to parse results in no effective origin, which triggers downstream validation), logging a debug or warning message would help troubleshoot malformed Referer headers in production.

       try {
         const url = new URL(referer);
         effectiveOrigin = url.origin;
-      } catch {
-        // Invalid referer URL
+      } catch (error) {
+        logger.debug(
+          { context: "csrf", referer, error },
+          "Failed to parse origin from Referer header"
+        );
       }

124-126: Ensure allowed origins are configured to prevent blocking all requests.

When both FRONTEND_URL and CORS_ALLOWED_ORIGINS are unset in non-development environments, getAllowedOrigins() returns an empty array, causing all state-changing requests to be rejected with a 403. Verify that production deployments configure at least one of these environment variables, or consider adding a startup validation check to warn operators about this misconfiguration.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d95324f and 01cdece.

📒 Files selected for processing (1)
  • apps/core/server/plugins/csrf.plugin.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> in the Asset-Forge project
Use const over let, never use var
Use template literals for strings instead of string concatenation

Files:

  • apps/core/server/plugins/csrf.plugin.ts
apps/core/server/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

apps/core/server/**/*.ts: Research with Deepwiki before adding new endpoints in Elysia
Define API types in service file with TypeBox validation in Elysia endpoints
Implement business logic in service class for API endpoints
Create route handler with TypeBox validation for API endpoints
Add Swagger documentation to new API endpoints
Follow REST conventions for API endpoints

Files:

  • apps/core/server/plugins/csrf.plugin.ts
**/*.{ts,tsx,js,jsx,json,css,scss}

📄 CodeRabbit inference engine (CLAUDE.md)

Use 2-space indentation for all code

Files:

  • apps/core/server/plugins/csrf.plugin.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript strict mode everywhere
No any or unknown types in TypeScript code
Use explicit return types on public methods in TypeScript

Files:

  • apps/core/server/plugins/csrf.plugin.ts
🧬 Code graph analysis (1)
apps/core/server/plugins/csrf.plugin.ts (2)
apps/core/server/config/env.ts (1)
  • env (320-320)
apps/core/server/routes/content/shared.ts (2)
  • Elysia (50-50)
  • logger (23-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: E2E Tests (Playwright) (webkit)
🔇 Additional comments (1)
apps/core/server/plugins/csrf.plugin.ts (1)

17-41: Add explicit return type to comply with coding guidelines.

The getAllowedOrigins function is missing an explicit return type annotation, which violates the project's TypeScript coding guideline requiring explicit return types on public methods.

Apply this diff:

-function getAllowedOrigins(): string[] {
+function getAllowedOrigins(): string[] {

Wait, the function already has : string[] return type annotation on line 17. Let me re-examine the code...

Actually, looking at line 17 again: function getAllowedOrigins(): string[] { - the return type IS present. No issue here.

Let me reconsider - I should skip this comment.

- CSRF plugin now uses NODE_ENV !== "production" to match CORS behavior,
  allowing localhost in both development and test environments
- Remove redundant token_blocklist index (unique constraint auto-creates one)
- TEST_JWT_SECRET helper now throws if env var missing (fail-closed)
- Eden Treaty helpers use AnyElysiaApp type alias instead of explicit anys
- Replace all `any` types with `unknown` in test helper functions
- Add documentation comment for rate-limiting DISABLE_RATE_LIMITING check
.defaultNow()
.notNull(),
// When the original token expires (for cleanup)
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential performance and data consistency issue with expired tokens

The expiresAt field is used for cleanup, but the schema does not enforce or automate the removal of expired blocklist entries. If expired tokens are not regularly purged, the table may grow unnecessarily, impacting query performance and storage.

Recommendation:
Implement a scheduled cleanup process (e.g., a cron job or database job) that periodically deletes entries where expiresAt < NOW(). This ensures the blocklist remains performant and does not accumulate obsolete data.

Comment on lines +78 to +79
if (authHeader?.startsWith("Bearer af_")) {
return; // API key requests don't need CSRF protection

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Security Issue: Overly Broad API Key CSRF Bypass

The CSRF check is skipped for any request with an Authorization header starting with Bearer af_. If this pattern is not strictly enforced and API keys are not securely managed, attackers could potentially forge such headers to bypass CSRF protection.

Recommendation:
Ensure that API key authentication is robust and that only trusted machine-to-machine clients can use this bypass. Consider validating the API key format and origin more strictly, or limiting this bypass to specific endpoints where machine-to-machine communication is required.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/core/server/plugins/rate-limiting.plugin.ts (1)

113-117: Misleading comment: rate limiting is not disabled in development.

The comment on line 116 states "Automatically disabled in development mode," but this is no longer accurate. Rate limiting is now environment-aware (10x in dev, 100x in test) and only disabled when DISABLE_RATE_LIMITING=true.

 /**
  * Rate Limiting Plugin
  * Applies global rate limiting and endpoint-specific limits
- * Automatically disabled in development mode
+ * Environment-aware: stricter in production, relaxed in dev/test
  */
♻️ Duplicate comments (3)
apps/core/server/plugins/rate-limiting.plugin.ts (2)

32-37: Type safety: Replace as any cast with a proper type.

The as any cast undermines TypeScript's type safety. Define a specific error response type to ensure consistent error handling.

This was already flagged in a previous review. Consider applying a fix like:

+type RateLimitErrorResponse = { error: string; message: string };
+
-function createRateLimitError(message: string) {
+function createRateLimitError(message: string): RateLimitErrorResponse {
   return {
     error: "TOO_MANY_REQUESTS",
     message,
-  } as any;
+  };
 }

If elysia-rate-limit requires a specific type for errorResponse, you may need to check the library's type exports.


39-56: Environment variable access inconsistency persists.

While the added comment (lines 43-45) explains the rationale for using process.env.DISABLE_RATE_LIMITING directly, this still creates an inconsistency with the validated env.NODE_ENV usage on line 56. Additionally, as flagged in previous reviews, this allows rate limiting to be disabled in production if the env var is set.

Previous reviewers recommended adding DISABLE_RATE_LIMITING to the env schema with production safeguards:

// In apps/core/server/config/env.ts:
DISABLE_RATE_LIMITING: z.string().optional().transform((val) => val === "true").default("false"),

Then enforce production safety:

-const RATE_LIMITING_ENABLED = process.env.DISABLE_RATE_LIMITING !== "true";
+const RATE_LIMITING_ENABLED = env.NODE_ENV === "production" ? true : !env.DISABLE_RATE_LIMITING;

This ensures rate limiting cannot be bypassed in production while maintaining test flexibility.

apps/core/__tests__/helpers/api.ts (1)

49-51: Add explicit return types for Treaty test clients (and align with Treaty typings)

These exported helpers still lack explicit return types, which violates the “explicit return types on public methods” guideline and repeats the concern from the previous review. You can tighten the typing and match Eden Treaty’s Create<App> return type:

-import { treaty } from "@elysiajs/eden";
+import { treaty, type Treaty } from "@elysiajs/eden";

-export function createTestClient<T extends AnyElysiaApp>(app: T) {
-  return treaty(app);
-}
+export function createTestClient<T extends AnyElysiaApp>(
+  app: T,
+): Treaty.Create<T> {
+  return treaty(app);
+}

-export function createAuthTestClient<T extends AnyElysiaApp>(
-  app: T,
-  user: AuthUser,
-) {
-  return treaty(app, {
+export function createAuthTestClient<T extends AnyElysiaApp>(
+  app: T,
+  user: AuthUser,
+): Treaty.Create<T> {
+  return treaty(app, {
     headers: {
       Authorization: createAuthHeader(
-        user.privyUserId,
+        // If AuthUser.privyUserId can be null, consider guarding or asserting here
+        user.privyUserId,
         user.email || undefined,
       ),
     },
   });
 }

Also, if AuthUser["privyUserId"] is typed as string | null (as in apps/core/server/types/auth.ts), you may want to narrow or assert non‑null before passing it into createAuthHeader to keep strict mode happy and avoid accidental null subjects in mock JWTs. Please verify which AuthUser type this file actually imports.

This relies on the Eden Treaty type definitions and your shared AuthUser type; please confirm against your installed @elysiajs/eden version and auth typings.

Also applies to: 62-74

🧹 Nitpick comments (3)
apps/core/server/plugins/rate-limiting.plugin.ts (1)

160-168: Use server.requestIP(req) instead of URL.hostname for more reliable client IP detection.

The server parameter is available in the generator function scope. Bun's requestIP method properly returns the client's SocketAddress, while the current fallback using new URL(req.url).hostname returns the server's hostname instead of the client's address. This causes all clients without proper headers to share the same rate limit bucket.

Replace the fallback logic at lines 160-161 with:

          // Last resort: use request URL
-         return new URL(req.url).hostname || "unknown";
+         const addr = server.requestIP(req);
+         return addr?.address || "unknown";
apps/core/server/db/schema/token-blocklist.schema.ts (1)

20-29: Optionally index userId (and/or add FK) if user-scoped queries are hot.

If blockUserTokens or other user-scoped operations are frequent, consider adding an index on userId (and possibly a foreign key to the users table) to keep those queries efficient and maintain referential integrity. This can be deferred until profiling shows need.

Example index addition:

   (table) => [
     // Note: tokenId already has a unique constraint which creates an index automatically
     // Only add index for expiresAt which is used for cleanup queries
     index("idx_token_blocklist_expires_at").on(table.expiresAt),
+    // Optional: speed up user-scoped revocation queries
+    index("idx_token_blocklist_user_id").on(table.userId),
   ],
apps/core/__tests__/helpers/api.ts (1)

18-29: Consider using a type‑only import for Elysia

Elysia is only used for typing (AnyElysiaApp, function params), so you can avoid a runtime import by switching to a type‑only import:

-import { Elysia } from "elysia";
+import type { Elysia } from "elysia";

This keeps the helper purely type‑level and avoids pulling elysia into the test bundle unnecessarily.

As this touches TypeScript import semantics, please confirm with your TS config / bundler behavior.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01cdece and d57bfd3.

📒 Files selected for processing (5)
  • apps/core/__tests__/helpers/api.ts (7 hunks)
  • apps/core/__tests__/helpers/auth.ts (2 hunks)
  • apps/core/server/db/schema/token-blocklist.schema.ts (1 hunks)
  • apps/core/server/plugins/csrf.plugin.ts (1 hunks)
  • apps/core/server/plugins/rate-limiting.plugin.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/core/tests/helpers/auth.ts
  • apps/core/server/plugins/csrf.plugin.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> in the Asset-Forge project
Use const over let, never use var
Use template literals for strings instead of string concatenation

Files:

  • apps/core/server/db/schema/token-blocklist.schema.ts
  • apps/core/server/plugins/rate-limiting.plugin.ts
  • apps/core/__tests__/helpers/api.ts
apps/core/server/db/schema/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Edit TypeScript schema in apps/core/server/db/schema/ when schema changes are needed

Files:

  • apps/core/server/db/schema/token-blocklist.schema.ts
apps/core/server/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

apps/core/server/**/*.ts: Research with Deepwiki before adding new endpoints in Elysia
Define API types in service file with TypeBox validation in Elysia endpoints
Implement business logic in service class for API endpoints
Create route handler with TypeBox validation for API endpoints
Add Swagger documentation to new API endpoints
Follow REST conventions for API endpoints

Files:

  • apps/core/server/db/schema/token-blocklist.schema.ts
  • apps/core/server/plugins/rate-limiting.plugin.ts
**/*.{ts,tsx,js,jsx,json,css,scss}

📄 CodeRabbit inference engine (CLAUDE.md)

Use 2-space indentation for all code

Files:

  • apps/core/server/db/schema/token-blocklist.schema.ts
  • apps/core/server/plugins/rate-limiting.plugin.ts
  • apps/core/__tests__/helpers/api.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript strict mode everywhere
No any or unknown types in TypeScript code
Use explicit return types on public methods in TypeScript

Files:

  • apps/core/server/db/schema/token-blocklist.schema.ts
  • apps/core/server/plugins/rate-limiting.plugin.ts
  • apps/core/__tests__/helpers/api.ts
🧬 Code graph analysis (2)
apps/core/server/plugins/rate-limiting.plugin.ts (1)
apps/core/server/config/env.ts (1)
  • env (320-320)
apps/core/__tests__/helpers/api.ts (3)
apps/core/server/routes/content/shared.ts (1)
  • Elysia (50-50)
apps/core/server/types/auth.ts (1)
  • AuthUser (7-17)
apps/core/__tests__/helpers/auth.ts (1)
  • createAuthHeader (63-66)
🔇 Additional comments (4)
apps/core/server/plugins/rate-limiting.plugin.ts (1)

58-76: Environment-aware rate limits look good.

The base limit constants with multiplier approach is a clean pattern for environment-specific rate limiting. Production gets strict limits (1x), while dev (10x) and test (100x) get progressively relaxed limits.

apps/core/server/db/schema/token-blocklist.schema.ts (1)

14-36: Schema definition looks solid and resolves prior redundant-index concern.

The token_blocklist schema is well-structured: appropriate types, tokenId uniqueness without an extra manual index (fixing the earlier redundant-index issue), and an expiresAt index to support cleanup queries. Exported infer types are correctly wired for select/insert use.

apps/core/__tests__/helpers/api.ts (2)

4-15: Doc updates align well with Elysia testing best practices

The added guidance around Eden Treaty vs app.handle() is clear, follows current Elysia testing patterns, and is helpful context for test authors. No changes requested.


102-106: Replace explicit unknown usages with a JSON value type alias to satisfy guidelines

Several helpers now use unknown in parameter types and generic defaults:

  • post, patch: body: unknown
  • authPost, authPatch: body: unknown
  • testRouteJSON<T = unknown>
  • extractJSON<T = unknown>

This conflicts with the project guideline of “No any or unknown types in TypeScript code.” You can keep strong typing without unknown by introducing a reusable JSON value alias and constraining usages to it. For example:

 type AnyElysiaApp = Elysia;
 
+// JSON value type used for request/response bodies
+type JsonValue =
+  | string
+  | number
+  | boolean
+  | null
+  | JsonValue[]
+  | { [key: string]: JsonValue };
+
 /**
  * Create a POST request with JSON body
  */
-export function post(
-  path: string,
-  body: unknown,
-  headers?: HeadersInit,
-): Request {
+export function post(
+  path: string,
+  body: JsonValue,
+  headers?: HeadersInit,
+): Request {
@@
 /**
  * Create a PATCH request with JSON body
  */
-export function patch(
-  path: string,
-  body: unknown,
-  headers?: HeadersInit,
-): Request {
+export function patch(
+  path: string,
+  body: JsonValue,
+  headers?: HeadersInit,
+): Request {
@@
-export function authPost(path: string, body: unknown, user: AuthUser): Request {
+export function authPost(
+  path: string,
+  body: JsonValue,
+  user: AuthUser,
+): Request {
@@
-export function authPatch(
-  path: string,
-  body: unknown,
-  user: AuthUser,
-): Request {
+export function authPatch(
+  path: string,
+  body: JsonValue,
+  user: AuthUser,
+): Request {
@@
-export async function testRouteJSON<T = unknown>(
+export async function testRouteJSON<T extends JsonValue>(
   app: Elysia,
   request: Request,
 ): Promise<{ response: Response; data: T }> {
@@
-export async function extractJSON<T = unknown>(response: Response): Promise<T> {
+export async function extractJSON<T extends JsonValue>(
+  response: Response,
+): Promise<T> {

This keeps the helpers flexible for any JSON‑serializable payload, removes explicit unknown, and stays within the strict TypeScript rules you’ve defined.

As this relies on TypeScript’s handling of recursive JSON types and generic constraints, please confirm with your TS version and lint rules.

Also applies to: 120-124, 163-164, 180-184, 227-234, 278-283

@Dexploarer Dexploarer merged commit 29066ac into main Nov 25, 2025
1 of 7 checks passed
@Dexploarer Dexploarer deleted the claude/critical-app-review-019txope9jjiPCuNTQJC1Qh5 branch November 25, 2025 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants