security-audit

This skill helps you identify and fix security vulnerabilities in the codebase.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "security-audit" with this command: npx skills add sgcarstrends/sgcarstrends/sgcarstrends-sgcarstrends-security-audit

Security Audit Skill

This skill helps you identify and fix security vulnerabilities in the codebase.

When to Use This Skill

  • Before production deployments

  • When adding authentication/authorization

  • When handling user input

  • After dependency updates

  • During code reviews

  • When integrating third-party services

OWASP Top 10 (2021)

  1. Broken Access Control

Issue: Users can access resources they shouldn't

Check For:

Search for authorization checks

grep -r "hasPermission|canAccess|isAuthorized" apps/ --include="*.ts"

Look for missing auth checks

grep -r "export.*function|export.async function" apps/api/src/routes --include=".ts"

Example Vulnerability:

// ❌ No authorization check export async function deletePost(postId: string) { await db.delete(posts).where(eq(posts.id, postId)); }

// ✅ With authorization export async function deletePost(postId: string, userId: string) { const post = await db.query.posts.findFirst({ where: eq(posts.id, postId), });

if (post.authorId !== userId) { throw new Error("Unauthorized"); }

await db.delete(posts).where(eq(posts.id, postId)); }

  1. Cryptographic Failures

Issue: Sensitive data exposed or poorly encrypted

Check For:

Look for hardcoded secrets

grep -ri "password.=|api[_-]key.=|secret.=" apps/ packages/ --include=".ts"

Check for sensitive data in logs

grep -r "console.log" apps/ --include="*.ts" | grep -i "password|token|secret"

Example Vulnerability:

// ❌ Storing passwords in plain text await db.insert(users).values({ email, password, // Plain text! });

// ✅ Hashing passwords import bcrypt from "bcrypt";

const hashedPassword = await bcrypt.hash(password, 10); await db.insert(users).values({ email, password: hashedPassword, });

  1. Injection

Issue: SQL injection, command injection, etc.

Check For:

Look for string concatenation in queries

grep -r "SELECT.${" apps/ packages/ --include=".ts" grep -r "WHERE.${" apps/ packages/ --include=".ts"

Check for eval usage

grep -r "eval(" apps/ packages/ --include="*.ts"

Example Vulnerability:

// ❌ SQL Injection const query = SELECT * FROM users WHERE id = ${userId};

// ✅ Parameterized query (Drizzle ORM) const user = await db.query.users.findFirst({ where: eq(users.id, userId), });

// ❌ Command Injection const result = exec(git log ${userInput});

// ✅ Sanitized input import { z } from "zod";

const schema = z.string().regex(/^[a-zA-Z0-9-]+$/); const sanitized = schema.parse(userInput); const result = exec(git log ${sanitized});

  1. Insecure Design

Issue: Flawed security architecture

Check For:

  • Missing rate limiting

  • No input validation

  • Weak session management

  • Missing CSRF protection

Example Vulnerability:

// ❌ No rate limiting export async function login(email: string, password: string) { // Anyone can brute force passwords const user = await verifyCredentials(email, password); return createSession(user); }

// ✅ With rate limiting import { Ratelimit } from "@upstash/ratelimit"; import { redis } from "@sgcarstrends/utils";

const ratelimit = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(5, "15 m"), // 5 attempts per 15 min });

export async function login(email: string, password: string, ip: string) { const { success } = await ratelimit.limit(ip);

if (!success) { throw new Error("Too many login attempts"); }

const user = await verifyCredentials(email, password); return createSession(user); }

  1. Security Misconfiguration

Issue: Insecure default configs, unnecessary services

Check For:

Look for debug mode in production

grep -r "debug.true" apps/ --include=".ts" --include="*.json"

Check for exposed error messages

grep -r "error.stack|error.message" apps/ --include="*.ts"

Example Vulnerability:

// ❌ Exposing stack traces in production export async function handler(req: Request) { try { // ... } catch (error) { return Response.json({ error: error.stack }, { status: 500 }); } }

// ✅ Safe error handling export async function handler(req: Request) { try { // ... } catch (error) { console.error("Error:", error); // Log internally return Response.json( { error: "Internal server error" }, // Generic message { status: 500 } ); } }

  1. Vulnerable Components

Issue: Using outdated or vulnerable dependencies

Check For:

Audit dependencies

pnpm audit

Check for outdated packages

pnpm outdated

Look for specific vulnerable packages

pnpm list | grep "package-name"

Fix:

Update vulnerable packages

pnpm update package-name

Or update all

pnpm update -r

Check audit after update

pnpm audit

  1. Authentication Failures

Issue: Weak authentication mechanisms

Check For:

Look for weak password requirements

grep -r "password.length" apps/ --include=".ts"

Check for missing password hashing

grep -r "password.=" apps/ --include=".ts" | grep -v "bcrypt|argon2|hash"

Example Vulnerability:

// ❌ Weak password validation const passwordSchema = z.string().min(6);

// ✅ Strong password validation const passwordSchema = z.string() .min(12) .regex(/[A-Z]/, "Must contain uppercase") .regex(/[a-z]/, "Must contain lowercase") .regex(/[0-9]/, "Must contain number") .regex(/[^A-Za-z0-9]/, "Must contain special character");

  1. Software and Data Integrity Failures

Issue: Unverified updates, insecure CI/CD

Check For:

Look for unsigned packages

grep -r "npm install|pnpm add" .github/ --include="*.yml"

Check for pinned versions

cat package.json | grep -v "^\s*".":\s"[^~]"

Example Fix:

// ❌ Unpinned versions { "dependencies": { "react": "^18.0.0", // Could install 18.9.9 "next": "~15.0.0" // Could install 15.0.9 } }

// ✅ Pinned versions (with pnpm catalog) { "dependencies": { "react": "catalog:", // Exact version from catalog "next": "catalog:" } }

  1. Logging and Monitoring Failures

Issue: Insufficient logging, no alerting

Check For:

Look for authentication logging

grep -r "login|authenticate" apps/api --include="*.ts" | grep -c "log|console"

Check for error logging

grep -r "catch.error" apps/ --include=".ts" | grep -v "log|console"

Example Vulnerability:

// ❌ No logging export async function login(email: string, password: string) { const user = await verifyCredentials(email, password); return createSession(user); }

// ✅ With security logging export async function login(email: string, password: string, ip: string) { try { const user = await verifyCredentials(email, password);

// Log successful login
console.log(`Login success: ${email} from ${ip}`);

return createSession(user);

} catch (error) { // Log failed attempt console.warn(Login failed: ${email} from ${ip}); throw error; } }

  1. Server-Side Request Forgery (SSRF)

Issue: Server makes requests to attacker-controlled URLs

Check For:

Look for user-controlled URLs

grep -r "fetch(.*req|axios(.req" apps/ --include=".ts"

Example Vulnerability:

// ❌ SSRF vulnerability export async function fetchUrl(url: string) { return await fetch(url); // User controls URL! }

// ✅ Whitelist approach const ALLOWED_DOMAINS = ["api.example.com", "data.gov.sg"];

export async function fetchUrl(url: string) { const parsedUrl = new URL(url);

if (!ALLOWED_DOMAINS.includes(parsedUrl.hostname)) { throw new Error("Domain not allowed"); }

return await fetch(url); }

Input Validation

Always Validate User Input

import { z } from "zod";

// Define schema const userInputSchema = z.object({ name: z.string().min(1).max(100), email: z.string().email(), age: z.number().int().min(0).max(150), website: z.string().url().optional(), });

// Validate export async function createUser(data: unknown) { const validated = userInputSchema.parse(data); // Throws if invalid // Now safe to use validated data }

Sanitize HTML

import sanitizeHtml from "sanitize-html";

export function sanitizeUserInput(html: string): string { return sanitizeHtml(html, { allowedTags: ["b", "i", "em", "strong", "a", "p"], allowedAttributes: { a: ["href"], }, }); }

XSS Prevention

React Automatic Escaping

// ✅ Safe - React escapes by default <div>{userInput}</div>

// ❌ Dangerous <div dangerouslySetInnerHTML={{ __html: userInput }} />

// ✅ Safe if sanitized <div dangerouslySetInnerHTML={{ __html: sanitizeHtml(userInput) }} />

URL Sanitization

// ❌ XSS via javascript: protocol <a href={userUrl}>Click</a>

// ✅ Validate URL function isSafeUrl(url: string): boolean { try { const parsed = new URL(url); return parsed.protocol === "http:" || parsed.protocol === "https:"; } catch { return false; } }

<a href={isSafeUrl(userUrl) ? userUrl : "#"}>Click</a>

CORS Configuration

// ❌ Too permissive app.use(cors({ origin: "*", // Allows any origin! }));

// ✅ Whitelist specific origins app.use(cors({ origin: [ "https://sgcarstrends.com", "https://staging.sgcarstrends.com", process.env.NODE_ENV === "development" ? "http://localhost:3001" : "", ].filter(Boolean), credentials: true, }));

Environment Variables

Never Commit Secrets

Check for committed secrets

git log -p | grep -i "password|secret|key" | head -20

Use git-secrets to prevent commits

git secrets --scan

Use Environment Variables

// ❌ Hardcoded secret const apiKey = "sk_live_abc123";

// ✅ From environment const apiKey = process.env.API_KEY!;

// ✅ With validation const envSchema = z.object({ API_KEY: z.string().min(1), DATABASE_URL: z.string().url(), });

const env = envSchema.parse(process.env);

Security Headers

// next.config.js const securityHeaders = [ { key: "X-DNS-Prefetch-Control", value: "on", }, { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload", }, { key: "X-Frame-Options", value: "SAMEORIGIN", }, { key: "X-Content-Type-Options", value: "nosniff", }, { key: "X-XSS-Protection", value: "1; mode=block", }, { key: "Referrer-Policy", value: "origin-when-cross-origin", }, { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()", }, ];

module.exports = { async headers() { return [ { source: "/:path*", headers: securityHeaders, }, ]; }, };

Automated Security Scanning

npm/pnpm Audit

Check for vulnerabilities

pnpm audit

Fix automatically

pnpm audit --fix

Get JSON report

pnpm audit --json > audit-report.json

Snyk

Install Snyk

npm install -g snyk

Authenticate

snyk auth

Test for vulnerabilities

snyk test

Monitor project

snyk monitor

OWASP Dependency Check

Run dependency check

dependency-check --project sgcarstrends --scan .

Security Testing

Test Authentication

// tests/security/auth.test.ts describe("Authentication Security", () => { it("rejects invalid credentials", async () => { const response = await login("user@example.com", "wrong-password"); expect(response.status).toBe(401); });

it("rate limits login attempts", async () => { const attempts = Array(10).fill(null).map(() => login("user@example.com", "wrong-password") );

await Promise.all(attempts);

const response = await login("user@example.com", "wrong-password");
expect(response.status).toBe(429);  // Too many requests

});

it("does not leak user existence", async () => { const response1 = await login("exists@example.com", "wrong"); const response2 = await login("noexist@example.com", "wrong");

// Same error message for both
expect(response1.message).toBe(response2.message);

}); });

Test Input Validation

describe("Input Validation", () => { it("rejects SQL injection attempts", async () => { const malicious = "'; DROP TABLE users; --";

await expect(
  createUser({ name: malicious })
).rejects.toThrow();

});

it("rejects XSS attempts", async () => { const xss = "<script>alert('xss')</script>";

const result = await createPost({ content: xss });

expect(result.content).not.toContain("&#x3C;script>");

}); });

Security Checklist

Before deployment:

  • All user input validated

  • SQL injection prevented (using ORM)

  • XSS prevented (React escaping, sanitization)

  • CSRF protection enabled

  • Authentication implemented correctly

  • Authorization checks in place

  • Passwords hashed (bcrypt/argon2)

  • Rate limiting configured

  • Security headers set

  • CORS configured properly

  • HTTPS enforced

  • Dependencies audited

  • Secrets in environment variables

  • Error messages don't leak info

  • Logging enabled for security events

References

Best Practices

  • Validate Everything: Never trust user input

  • Use ORM: Prevent SQL injection

  • Hash Passwords: Use bcrypt or argon2

  • Rate Limit: Prevent brute force

  • Security Headers: Set proper headers

  • HTTPS Only: Enforce HTTPS everywhere

  • Audit Dependencies: Regularly check for vulnerabilities

  • Least Privilege: Grant minimum necessary permissions

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

security-audit

No summary provided by upstream source.

Repository SourceNeeds Review
Security

security-audit

No summary provided by upstream source.

Repository SourceNeeds Review
Security

security-audit

No summary provided by upstream source.

Repository SourceNeeds Review
Security

security-audit

No summary provided by upstream source.

Repository SourceNeeds Review