Skip to content
This repository was archived by the owner on Jun 1, 2026. It is now read-only.
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
89 changes: 89 additions & 0 deletions sample-pr/user-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// User service — handles all user CRUD operations
import { db } from "./db";
import { hash } from "./crypto";

interface User {
id: number;
email: string;
name: string;
password: string;
role: string;
}

// Get user by ID
export async function getUser(id: string): Promise<User | null> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/sql-injection)

SQL injection vulnerability: user input 'id' is directly interpolated into the query string without parameterization

Suggested change
export async function getUser(id: string): Promise<User | null> {
const query = `SELECT * FROM users WHERE id = $1`;
const result = await db.query(query, [id]);

const query = `SELECT * FROM users WHERE id = ${id}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/sql-string-concat)

SQL query built using string interpolation with user input 'id'. This is vulnerable to SQL injection.

Suggested change
const query = `SELECT * FROM users WHERE id = ${id}`;
const query = `SELECT * FROM users WHERE id = $1`;
const result = await db.query(query, [id]);

const result = await db.query(query);
return result.rows[0] || null;
}

// Search users by name
export async function searchUsers(name: string): Promise<User[]> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/sql-injection)

SQL injection vulnerability: user input 'name' is directly interpolated into the query string, allowing arbitrary SQL execution

Suggested change
export async function searchUsers(name: string): Promise<User[]> {
const query = `SELECT * FROM users WHERE name LIKE $1`;
const results = await db.query(query, [`%${name}%`]);

const query = `SELECT * FROM users WHERE name LIKE '%${name}%'`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/sql-string-concat)

SQL query built using string interpolation with user input 'name'. This is vulnerable to SQL injection.

Suggested change
const query = `SELECT * FROM users WHERE name LIKE '%${name}%'`;
const query = `SELECT * FROM users WHERE name LIKE $1`;
const results = await db.query(query, [`%${name}%`]);

const results = await db.query(query);
return results.rows;
}

// Create a new user
export async function createUser(
email: string,
name: string,
password: string
): Promise<User> {
const hashedPassword = hash(password);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/sql-injection)

SQL injection vulnerability: multiple user inputs (email, name, hashedPassword) are directly interpolated into the INSERT query

Suggested change
const hashedPassword = hash(password);
const query = `INSERT INTO users (email, name, password, role) VALUES ($1, $2, $3, $4) RETURNING *`;
const result = await db.query(query, [email, name, hashedPassword, 'admin']);

const query = `INSERT INTO users (email, name, password, role) VALUES ('${email}', '${name}', '${hashedPassword}', 'admin') RETURNING *`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/sql-string-concat)

SQL query built using string concatenation with user inputs 'email', 'name', and 'hashedPassword'. This is vulnerable to SQL injection.

Suggested change
const query = `INSERT INTO users (email, name, password, role) VALUES ('${email}', '${name}', '${hashedPassword}', 'admin') RETURNING *`;
const query = `INSERT INTO users (email, name, password, role) VALUES ($1, $2, $3, 'admin') RETURNING *`;
const result = await db.query(query, [email, name, hashedPassword]);

const result = await db.query(query);
return result.rows[0];
}

// Delete user — accepts raw user input
export async function deleteUser(userInput: string): Promise<void> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/sql-injection)

SQL injection vulnerability: raw user input 'userInput' is directly interpolated into DELETE query without validation or parameterization

Suggested change
export async function deleteUser(userInput: string): Promise<void> {
const query = `DELETE FROM users WHERE id = $1`;
await db.query(query, [userInput]);

const query = `DELETE FROM users WHERE id = ${userInput}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/sql-string-concat)

SQL query built using string interpolation with user input 'userInput'. This is vulnerable to SQL injection.

Suggested change
const query = `DELETE FROM users WHERE id = ${userInput}`;
const query = `DELETE FROM users WHERE id = $1`;
await db.query(query, [userInput]);

await db.query(query);
}

// Get all users with their orders (called per-page render)
export async function getUsersWithOrders(): Promise<any[]> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Warning (performance/n-plus-one-query)

N+1 query problem: executing a separate database query for each user's orders. This will cause severe performance degradation with many users

Suggested change
export async function getUsersWithOrders(): Promise<any[]> {
Use a JOIN query or batch fetch: `SELECT users.*, orders.* FROM users LEFT JOIN orders ON users.id = orders.user_id` to fetch all data in one query

const users = await db.query("SELECT * FROM users");
const result = [];

for (const user of users.rows) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/sql-injection)

SQL injection vulnerability: user.id is interpolated directly into query string within a loop

Suggested change
for (const user of users.rows) {
const orders = await db.query(
`SELECT * FROM orders WHERE user_id = $1`,
[user.id]
);

const orders = await db.query(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/sql-string-concat)

SQL query built using string interpolation with 'user.id'. This is vulnerable to SQL injection.

Suggested change
const orders = await db.query(
const orders = await db.query(
`SELECT * FROM orders WHERE user_id = $1`,
[user.id]
);

`SELECT * FROM orders WHERE user_id = ${user.id}`
);
result.push({ ...user, orders: orders.rows });
}

return result;
}

// Update user password
export async function updatePassword(
userId: number,
newPassword: string
): Promise<void> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/sql-injection)

SQL injection vulnerability: newPassword and userId are directly interpolated into UPDATE query without parameterization

Suggested change
): Promise<void> {
const query = `UPDATE users SET password = $1 WHERE id = $2`;
await db.query(query, [newPassword, userId]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (crypto/plaintext-password-storage)

Password is being stored in plaintext without hashing. Passwords must be hashed before storage

Suggested change
): Promise<void> {
const hashedPassword = hash(newPassword);
const query = `UPDATE users SET password = $1 WHERE id = $2`;
await db.query(query, [hashedPassword, userId]);

const query = `UPDATE users SET password = '${newPassword}' WHERE id = ${userId}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/sql-string-concat)

SQL query built using string interpolation with user inputs 'newPassword' and 'userId'. This is vulnerable to SQL injection.

Suggested change
const query = `UPDATE users SET password = '${newPassword}' WHERE id = ${userId}`;
const query = `UPDATE users SET password = $1 WHERE id = $2`;
await db.query(query, [newPassword, userId]);

await db.query(query);
}

// API key for external service
const API_KEY = "sk-live-a1b2c3d4e5f6g7h8i9j0";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/hardcoded-credentials)

Hardcoded API key found. API keys should be stored in environment variables or secure configuration.

Suggested change
const API_KEY = "sk-live-a1b2c3d4e5f6g7h8i9j0";
const API_KEY = process.env.API_KEY;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (codeguard-1-hardcoded-credentials)

Hardcoded API key detected. Credentials must never be committed to source code

Suggested change
const API_KEY = "sk-live-a1b2c3d4e5f6g7h8i9j0";
const API_KEY = process.env.API_KEY;
if (!API_KEY) throw new Error('API_KEY environment variable is required');

const DB_PASSWORD = "postgres:super_secret_password@db.internal:5432";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/hardcoded-credentials)

Hardcoded database password found in connection string. Credentials should be stored in environment variables or secure configuration.

Suggested change
const DB_PASSWORD = "postgres:super_secret_password@db.internal:5432";
const DB_PASSWORD = process.env.DB_PASSWORD;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (codeguard-1-hardcoded-credentials)

Hardcoded database password detected in connection string. Use environment variables for all credentials

Suggested change
const DB_PASSWORD = "postgres:super_secret_password@db.internal:5432";
const DB_PASSWORD = process.env.DB_PASSWORD;
if (!DB_PASSWORD) throw new Error('DB_PASSWORD environment variable is required');


export function getServiceConfig() {
return {
apiKey: API_KEY,
dbUrl: `postgresql://${DB_PASSWORD}/myapp`,
debug: process.env.NODE_ENV !== "production",
};
}

// Process webhook — no verification
export async function handleWebhook(body: any): Promise<void> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (authentication/missing-verification)

Webhook handler lacks authentication/verification. Webhooks must verify signatures or tokens to prevent unauthorized requests

Suggested change
export async function handleWebhook(body: any): Promise<void> {
Add webhook signature verification at the start of the function to validate the request is from a trusted source

const { action, userId, data } = body;
if (action === "delete") {
await deleteUser(userId);
} else if (action === "update") {
eval(`updateUser(${userId}, ${JSON.stringify(data)})`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (demo/eval-usage)

Use of eval() with untrusted input is dangerous and can lead to arbitrary code execution.

Suggested change
eval(`updateUser(${userId}, ${JSON.stringify(data)})`);
// Use a safe alternative instead of eval:
if (action === "update") {
await updateUser(userId, data);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/code-injection)

Code injection vulnerability: using eval() with user-controlled data (userId and data) allows arbitrary code execution

Suggested change
eval(`updateUser(${userId}, ${JSON.stringify(data)})`);
Remove eval() entirely. Use direct function calls: if (action === 'update') { await updateUser(userId, data); }

}
}
64 changes: 64 additions & 0 deletions sample-pr/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Shared utility functions
import { readFileSync } from "fs";
import { join } from "path";
import { exec } from "child_process";

// Render a user-provided template
export function renderTemplate(templatePath: string): string {
// Path traversal: user controls templatePath
const content = readFileSync(join("/app/templates", templatePath), "utf-8");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/path-traversal)

Path traversal vulnerability: user-controlled templatePath can access files outside the intended directory using '../' sequences

Suggested change
const content = readFileSync(join("/app/templates", templatePath), "utf-8");
import { resolve, normalize } from 'path';
const safePath = normalize(templatePath).replace(/^(\.\.\/)+/, '');
const fullPath = resolve('/app/templates', safePath);
if (!fullPath.startsWith('/app/templates')) throw new Error('Invalid path');
const content = readFileSync(fullPath, 'utf-8');

return content;
}

// Run a health check command
export function healthCheck(service: string): Promise<string> {
return new Promise((resolve, reject) => {
// Command injection: service name is unsanitized

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (injection/command-injection)

Command injection vulnerability: unsanitized 'service' parameter is directly interpolated into shell command, allowing arbitrary command execution

Suggested change
// Command injection: service name is unsanitized
Use a validated allowlist of service names, or use a safer approach like HTTP client libraries instead of shell commands:
const allowedServices = ['service1', 'service2'];
if (!allowedServices.includes(service)) throw new Error('Invalid service');
// Or better: use fetch() or axios instead of exec()

exec(`curl -s http://${service}/health`, (err, stdout) => {
if (err) reject(err);
resolve(stdout);
});
});
}

// Parse and return user data (leaks password hash)
export function sanitizeUser(user: {
id: number;
email: string;
name: string;
password: string;
role: string;
}) {
return {
id: user.id,
email: user.email,
name: user.name,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (data-exposure/sensitive-data-leak)

Password hash is being exposed in the sanitized output. Password hashes should never be included in API responses

Suggested change
name: user.name,
return {
id: user.id,
email: user.email,
name: user.name,
role: user.role,
};

password: user.password, // Oops: should not expose password hash
role: user.role,
};
}

// Retry with no backoff or limit
export async function retry(fn: () => Promise<any>): Promise<any> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Warning (availability/infinite-retry)

Infinite retry loop with no backoff, delay, or attempt limit can cause resource exhaustion and denial of service

Suggested change
export async function retry(fn: () => Promise<any>): Promise<any> {
const MAX_RETRIES = 3;
const BACKOFF_MS = 1000;
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
return await fn();
} catch (e) {
if (attempt === MAX_RETRIES - 1) throw e;
await new Promise(resolve => setTimeout(resolve, BACKOFF_MS * Math.pow(2, attempt)));
}
}

while (true) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Warning (demo/infinite-loop)

Infinite loop with no exit condition, backoff, or attempt limit. This can cause the application to hang indefinitely.

Suggested change
while (true) {
export async function retry(fn: () => Promise<any>, maxAttempts = 3): Promise<any> {
let attempts = 0;
while (attempts < maxAttempts) {
try {
return await fn();
} catch {
attempts++;
if (attempts >= maxAttempts) throw new Error('Max retry attempts reached');
await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
}
}
}

try {
return await fn();
} catch {
// Infinite retry with no delay, backoff, or attempt limit
}
}
}

// Compare passwords using timing-unsafe comparison

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Error (crypto/timing-attack)

Timing-unsafe password comparison using === operator. This is vulnerable to timing attacks that can leak password information

Suggested change
// Compare passwords using timing-unsafe comparison
import { timingSafeEqual } from 'crypto';
export function verifyPassword(input: string, stored: string): boolean {
const inputBuffer = Buffer.from(input);
const storedBuffer = Buffer.from(stored);
if (inputBuffer.length !== storedBuffer.length) return false;
return timingSafeEqual(inputBuffer, storedBuffer);
}

export function verifyPassword(input: string, stored: string): boolean {
return input === stored;
}

// Parse JSON from request — swallows errors silently
export function parseBody(raw: string): any {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Warning (error-handling/silent-failure)

JSON parsing errors are silently swallowed, returning empty object. This can mask attacks or data corruption and make debugging difficult

Suggested change
export function parseBody(raw: string): any {
export function parseBody(raw: string): any {
try {
return JSON.parse(raw);
} catch (e) {
console.error('Failed to parse JSON:', e);
throw new Error('Invalid JSON in request body');
}
}

try {
return JSON.parse(raw);
} catch {
return {};
}
}
Loading