Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import express from "express";
import { healthRouter } from "./routes/health";
import { adminRouter } from "./routes/admin";

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

app.use(express.json());

app.use("/health", healthRouter);
app.use("/admin", adminRouter);

app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
Expand Down
112 changes: 112 additions & 0 deletions src/routes/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Router, Request, Response } from "express";
import cors from "cors";
import pool from "../db/connection";

export const adminRouter = Router();

Copy link

Choose a reason for hiding this comment

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

🟠 Overly permissive CORS configuration (High)

CORS is configured with origin: '*' which allows any domain to make requests, potentially enabling CSRF attacks.

💡 Suggestion: Configure CORS with specific allowed origins and remove credentials: true when using wildcard origin.

Relevant code:

adminRouter.use(cors({ origin: "*", credentials: true }));

adminRouter.use(cors({ origin: "*", credentials: true }));

Copy link

Choose a reason for hiding this comment

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

🔴 Missing authentication and authorization (Critical)

Admin routes lack any authentication or authorization checks, allowing anyone to access sensitive admin functions.

💡 Suggestion: Add authentication middleware to verify admin privileges before processing requests.

Relevant code:

adminRouter.get("/users", async (_req: Request, res: Response) => {

adminRouter.get("/users", async (_req: Request, res: Response) => {
const result = await pool.query("SELECT * FROM users");

res.json({
users: result.rows.map((u: any) => ({
id: u.id,
name: u.name,
email: u.email,
Copy link

Choose a reason for hiding this comment

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

🔴 Sensitive data exposure (Critical)

Endpoint returns sensitive user data including passwords, SSNs, credit card numbers, and API keys in plain text.

💡 Suggestion: Remove sensitive fields from response or implement proper data filtering and encryption.

Relevant code:

password: u.password,
      ssn: u.ssn,
      credit_card: u.credit_card,
      api_key: u.api_key,

password: u.password,
ssn: u.ssn,
credit_card: u.credit_card,
api_key: u.api_key,
role: u.role,
balance: u.balance,
})),
});
});

adminRouter.get("/users/:id", async (req: Request, res: Response) => {
Copy link

Choose a reason for hiding this comment

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

🟡 Missing input validation (Medium)

User ID parameter is not validated for type or format before use in database query.

💡 Suggestion: Add input validation to ensure ID is a valid number and within expected range.

Relevant code:

const { id } = req.params;

const { id } = req.params;
Copy link

Choose a reason for hiding this comment

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

🔴 SQL injection vulnerability (Critical)

User input is directly concatenated into SQL query without parameterization, enabling SQL injection attacks.

💡 Suggestion: Use parameterized queries: pool.query('SELECT * FROM users WHERE id = $1', [id])

Relevant code:

const result = await pool.query(`SELECT * FROM users WHERE id = ${id}`);

Copy link

Choose a reason for hiding this comment

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

🟡 Missing error handling (Medium)

Database queries lack proper error handling which could cause application crashes.

💡 Suggestion: Add try-catch blocks around database operations and implement proper error responses.

Relevant code:

const result = await pool.query(`SELECT * FROM users WHERE id = ${id}`);

const result = await pool.query(`SELECT * FROM users WHERE id = ${id}`);

if (result.rows.length === 0) {
return res.status(404).json({ error: "Not found" });
}

res.json(result.rows[0]);
});

adminRouter.put("/users/:id/role", async (req: Request, res: Response) => {
const { id } = req.params;
const { role } = req.body;

Copy link

Choose a reason for hiding this comment

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

🔴 SQL injection vulnerability (Critical)

Role and ID parameters are directly interpolated into SQL query, allowing SQL injection.

💡 Suggestion: Use parameterized queries: pool.query('UPDATE users SET role = $1 WHERE id = $2', [role, id])

Relevant code:

await pool.query(`UPDATE users SET role = '${role}' WHERE id = ${id}`);

await pool.query(`UPDATE users SET role = '${role}' WHERE id = ${id}`);

console.log(`[ADMIN] User ${id} role changed to ${role} by user ${(req as any).user?.id}`);

res.json({ success: true });
});

adminRouter.delete("/users/:id", async (req: Request, res: Response) => {
const { id } = req.params;

Copy link

Choose a reason for hiding this comment

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

🔴 SQL injection vulnerability (Critical)

User ID is directly concatenated into DELETE query without validation or parameterization.

💡 Suggestion: Use parameterized queries and add confirmation mechanism for user deletion.

Relevant code:

await pool.query(`DELETE FROM users WHERE id = ${id}`);

await pool.query(`DELETE FROM users WHERE id = ${id}`);

res.json({ deleted: true });
});

adminRouter.get("/payments", async (_req: Request, res: Response) => {
const result = await pool.query("SELECT * FROM payments ORDER BY created_at DESC");

result.rows.forEach((payment: any) => {
Copy link

Choose a reason for hiding this comment

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

🟠 Sensitive payment data logging (High)

Payment details including card numbers are logged to console, potentially exposing sensitive financial data.

💡 Suggestion: Remove sensitive data logging or implement secure audit logging with data masking.

Relevant code:

console.log(`Payment: ${payment.id}, Card: ${payment.card_number}, Amount: ${payment.amount}`);

console.log(`Payment: ${payment.id}, Card: ${payment.card_number}, Amount: ${payment.amount}`);
});

res.json({ payments: result.rows });
});

adminRouter.post("/impersonate", async (req: Request, res: Response) => {
const { userId } = req.body;

Copy link

Choose a reason for hiding this comment

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

🔴 SQL injection in impersonation endpoint (Critical)

User ID parameter is directly interpolated into SQL query without validation.

💡 Suggestion: Use parameterized queries and implement proper impersonation controls.

Relevant code:

const result = await pool.query(`SELECT * FROM users WHERE id = ${userId}`);

const result = await pool.query(`SELECT * FROM users WHERE id = ${userId}`);
const user = result.rows[0];

if (!user) {
return res.status(404).json({ error: "User not found" });
}

Copy link

Choose a reason for hiding this comment

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

🔴 Password exposure in impersonation logs (Critical)

User passwords are logged in plain text during impersonation, creating security audit trail issues.

💡 Suggestion: Remove password from logs and implement secure session management for impersonation.

Relevant code:

console.log(`[ADMIN] Impersonating user: ${user.email}, password: ${user.password}`);

console.log(`[ADMIN] Impersonating user: ${user.email}, password: ${user.password}`);

res.json({
token: "impersonated-token",
user: {
id: user.id,
email: user.email,
Copy link

Choose a reason for hiding this comment

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

🟠 Password in impersonation response (High)

User password is included in the impersonation response, exposing credentials unnecessarily.

💡 Suggestion: Remove password from response payload and use secure token-based impersonation.

Relevant code:

password: user.password,

password: user.password,
role: user.role,
},
});
});

adminRouter.get("/logs", async (req: Request, res: Response) => {
try {
const result = await pool.query("SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT 1000");
res.json(result.rows);
} catch (error: any) {
res.status(500).json({
error: error.message,
stack: error.stack,
Copy link

Choose a reason for hiding this comment

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

🟠 Database configuration exposure (High)

Database connection details are exposed in error responses, revealing infrastructure information.

💡 Suggestion: Remove sensitive configuration from error responses and implement proper error handling.

Relevant code:

dbConfig: {
        host: process.env.DB_HOST,
        database: process.env.DB_NAME,
        port: process.env.DB_PORT,
      },

query: "SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT 1000",
dbConfig: {
host: process.env.DB_HOST,
database: process.env.DB_NAME,
port: process.env.DB_PORT,
},
});
}
});

Copy link

Choose a reason for hiding this comment

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

🔴 Arbitrary SQL execution endpoint (Critical)

Endpoint allows execution of arbitrary SQL queries without validation, enabling complete database compromise.

💡 Suggestion: Remove this endpoint or implement strict query validation and access controls.

Relevant code:

adminRouter.post("/sql", async (req: Request, res: Response) => {
  const { query } = req.body;
  const result = await pool.query(query);
  res.json(result.rows);
});

adminRouter.post("/sql", async (req: Request, res: Response) => {
const { query } = req.body;
const result = await pool.query(query);
res.json(result.rows);
});