Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

language: en-US
tone_instructions: >
Be thorough and professional. Provide actionable feedback with clear explanations
of why a change is recommended. When identifying security or performance issues,
include the potential impact and a suggested fix. Use a constructive, mentor-like tone.

early_access: true

reviews:
profile: assertive

# Summaries & visuals
high_level_summary: true
high_level_summary_instructions: >
Provide a concise executive summary covering: (1) what changed and why,
(2) architectural impact, (3) security or performance concerns,
and (4) any breaking changes. Include a risk assessment (low/medium/high).
high_level_summary_in_walkthrough: true
changed_files_summary: true
sequence_diagrams: true
collapse_walkthrough: false
review_status: true

# Effort & collaboration
estimate_code_review_effort: true
assess_linked_issues: true
related_issues: true
related_prs: true
suggested_labels: true
suggested_reviewers: true

# Disable fluff
poem: false
in_progress_fortune: false

# Auto-review settings
auto_review:
enabled: true
drafts: false
auto_incremental_review: true
base_branches:
- main
- develop
ignore_title_keywords:
- WIP
- DO NOT MERGE
- DRAFT

# Path filters — focus on source code
path_filters:
include:
- "backend/src/**"
- "frontend/src/**"
- "package.json"
- "tsconfig.json"
exclude:
- "node_modules/**"
- "dist/**"
- "build/**"
- "coverage/**"
- "*.min.js"
- "*.lock"
- "*.map"

# Path-specific review instructions
path_instructions:
- path: "backend/src/routes/**"
instructions: >
API routes: Verify input validation, proper HTTP status codes,
authentication/authorization checks, and SQL injection prevention.
Flag any raw SQL queries that don't use parameterized statements.
- path: "backend/src/middleware/**"
instructions: >
Middleware: Check JWT validation logic, error handling, and
ensure auth bypass is not possible. Verify token expiry handling.
- path: "backend/src/db.ts"
instructions: >
Database layer: Check for SQL injection, proper connection handling,
and data integrity constraints. Verify migrations are safe.
- path: "backend/src/utils/**"
instructions: >
Utility functions: Verify input sanitization, edge case handling,
and that validation logic is comprehensive and consistent.
- path: "frontend/src/pages/**"
instructions: >
Pages: Check for proper state management, error boundaries,
loading states, and accessibility (ARIA labels, keyboard navigation).
- path: "frontend/src/components/**"
instructions: >
Components: Verify React best practices — proper hook usage,
memoization where needed, prop validation, and XSS prevention.
- path: "frontend/src/api/**"
instructions: >
API client: Check error handling, request/response interceptors,
auth token attachment, and that sensitive data is not logged.

# Pre-merge quality gates
pre_merge_checks:
title:
mode: warning
requirements: "Use conventional commits format (feat:, fix:, refactor:, docs:, test:, chore:)"
description:
mode: warning
docstrings:
mode: warning
threshold: 70
custom_checks:
- name: "Security"
mode: error
instructions: >
Check for hardcoded secrets, credentials, API keys, or tokens.
Verify no sensitive data is exposed in logs or error messages.
Flag any use of eval(), innerHTML, or dangerouslySetInnerHTML.
- name: "Performance"
mode: warning
instructions: >
Identify potential performance issues: N+1 queries, missing
database indexes, unbounded queries, memory leaks in React
components (missing cleanup in useEffect), and unnecessary re-renders.

# Finishing touches — automated improvements
finishing_touches:
docstrings:
enabled: true
unit_tests:
enabled: true

# Linting & analysis tools
tools:
eslint:
enabled: true
biome:
enabled: true
semgrep:
enabled: true
github-checks:
enabled: true
timeout_ms: 90000

chat:
auto_reply: true

knowledge_base:
opt_out: false
web_search:
enabled: true
learnings:
scope: auto
issues:
scope: auto
40 changes: 0 additions & 40 deletions .github/workflows/claude-code-review.yml

This file was deleted.

90 changes: 0 additions & 90 deletions .github/workflows/openai-code-review.yml

This file was deleted.

30 changes: 30 additions & 0 deletions backend/src/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Database from "better-sqlite3";
import path from "path";

const dbPath = path.join(__dirname, "..", "teamexpense.db");
const db = new Database(dbPath);

db.pragma("journal_mode = WAL");

db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
name TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'member'
);

CREATE TABLE IF NOT EXISTS expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
amount REAL NOT NULL,
description TEXT NOT NULL,
category TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (user_id) REFERENCES users(id)
);
`);

export default db;
30 changes: 30 additions & 0 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import express from "express";
import cors from "cors";
import authRoutes from "./routes/auth";
import expenseRoutes from "./routes/expenses";
import reportRoutes from "./routes/reports";

const app = express();
const PORT = process.env.PORT || 3001;

// BUG: Wildcard CORS — allows any origin to make authenticated requests
app.use(cors());
app.use(express.json());

app.use("/api/auth", authRoutes);
app.use("/api/expenses", expenseRoutes);
app.use("/api/reports", reportRoutes);

// BUG: No rate limiting on any endpoints — susceptible to brute-force attacks

// BUG: Global error handler leaks stack traces to the client in production
app.use((err: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error(err);
res.status(500).json({ error: err.message, stack: err.stack });
});
Comment on lines +10 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

9. Insecure server defaults 🐞 Bug ⛨ Security

Server enables wildcard CORS, has no rate limiting, and returns error stack traces to clients,
increasing attack surface and leaking sensitive internals.
Agent Prompt
## Issue description
Backend server defaults increase security risk: wildcard CORS, no rate limiting, and stack trace leakage.

## Issue Context
These settings commonly enable brute force attacks and information disclosure and can make other vulnerabilities easier to exploit.

## Fix Focus Areas
- backend/src/index.ts[10-24]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +21 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium: Stack traces exposed to clients in production

The global error handler sends err.stack in the JSON response body. In production this reveals internal file paths, library versions, and code structure — information that meaningfully aids an attacker. Stack traces should only be logged server-side.

Suggested change
app.use((err: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error(err);
res.status(500).json({ error: err.message, stack: err.stack });
});
app.use((err: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error(err);
const message = process.env.NODE_ENV === "production" ? "Internal server error" : err.message;
res.status(500).json({ error: message });
});


app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

export default app;
36 changes: 36 additions & 0 deletions backend/src/middleware/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";

const JWT_SECRET = "supersecretkey123"; // BUG: Hardcoded JWT secret — should use environment variable

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Critical: Hardcoded JWT signing key in source control

The JWT signing key is a short, static string committed directly to the repository. Anyone with read access can forge valid JWTs for any user or role. This is further worsened by the key being re-exported on line 36.

Replace it with an environment variable and fail at startup if missing. The key should never be stored in code.


export interface AuthRequest extends Request {
user?: { id: number; email: string; role: string };
}

export function authenticate(req: AuthRequest, res: Response, next: NextFunction) {
const header = req.headers.authorization;
if (!header) {
return res.status(401).json({ error: "No token provided" });
}

// BUG: Does not validate "Bearer " prefix — any string with a valid JWT payload passes
const token = header.replace("Bearer ", "");

try {
const payload = jwt.verify(token, JWT_SECRET) as any;
req.user = payload;
next();
} catch {
return res.status(401).json({ error: "Invalid token" });
}
}

export function requireAdmin(req: AuthRequest, res: Response, next: NextFunction) {
// BUG: Checks role from the JWT payload which the user controls — should verify role from DB
if (req.user?.role !== "admin") {
return res.status(403).json({ error: "Admin access required" });
}
next();
Comment on lines +4 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Insecure jwt auth 🐞 Bug ⛨ Security

JWT authentication is insecure: a hardcoded signing secret, non-expiring tokens, lax Authorization
header parsing, and admin authorization based only on token claims enable token forgery and
privilege escalation.
Agent Prompt
## Issue description
JWT auth is vulnerable to token forgery and privilege escalation due to (1) hardcoded `JWT_SECRET`, (2) missing token expiry, (3) weak Authorization parsing, and (4) `requireAdmin` trusting JWT role claim.

## Issue Context
Hardcoded secret + role-in-token check means anyone with the secret can mint an `admin` token. Even without leakage, role changes won't be reflected, and long-lived tokens increase blast radius.

## Fix Focus Areas
- backend/src/middleware/auth.ts[4-34]
- backend/src/routes/auth.ts[25-35]
- backend/src/routes/auth.ts[65-71]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +28 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

High: Admin role authorization trusts JWT payload instead of database

requireAdmin checks req.user?.role which is read directly from the JWT claims. Because the JWT secret is hardcoded, an attacker who discovers it can craft a token with { "role": "admin" } and gain full admin access. Even with a strong secret, roles should be verified against the database on each request so that a revoked privilege takes effect immediately.

Verify the current database role rather than relying solely on the token payload.

}

export { JWT_SECRET };
Loading