Back to Skills

security-audit

sgcarstrends
Updated Today
16 views
9
1
9
View on GitHub
Developmentgeneral

About

The security-audit skill scans codebases for security vulnerabilities including SQL injection, XSS, and OWASP Top 10 risks. Use it when handling user input, implementing authentication, or before production deployments. It employs tools like Grep and Bash to identify security flaws in your code.

Documentation

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));
}

2. 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,
});

3. 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}`);

4. 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);
}

5. 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 }
    );
  }
}

6. 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

7. 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");

8. 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:"
  }
}

9. 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;
  }
}

10. 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("[email protected]", "wrong-password");
    expect(response.status).toBe(401);
  });

  it("rate limits login attempts", async () => {
    const attempts = Array(10).fill(null).map(() =>
      login("[email protected]", "wrong-password")
    );

    await Promise.all(attempts);

    const response = await login("[email protected]", "wrong-password");
    expect(response.status).toBe(429);  // Too many requests
  });

  it("does not leak user existence", async () => {
    const response1 = await login("[email protected]", "wrong");
    const response2 = await login("[email protected]", "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("<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

  1. Validate Everything: Never trust user input
  2. Use ORM: Prevent SQL injection
  3. Hash Passwords: Use bcrypt or argon2
  4. Rate Limit: Prevent brute force
  5. Security Headers: Set proper headers
  6. HTTPS Only: Enforce HTTPS everywhere
  7. Audit Dependencies: Regularly check for vulnerabilities
  8. Least Privilege: Grant minimum necessary permissions

Quick Install

/plugin add https://github.com/sgcarstrends/sgcarstrends/tree/main/security-audit

Copy and paste this command in Claude Code to install this skill

GitHub 仓库

sgcarstrends/sgcarstrends
Path: .claude/skills/security-audit
apiaws-lambdabackendhonojob-schedulerneon-postgres

Related Skills

subagent-driven-development

Development

This skill executes implementation plans by dispatching a fresh subagent for each independent task, with code review between tasks. It enables fast iteration while maintaining quality gates through this review process. Use it when working on mostly independent tasks within the same session to ensure continuous progress with built-in quality checks.

View skill

algorithmic-art

Meta

This Claude Skill creates original algorithmic art using p5.js with seeded randomness and interactive parameters. It generates .md files for algorithmic philosophies, plus .html and .js files for interactive generative art implementations. Use it when developers need to create flow fields, particle systems, or other computational art while avoiding copyright issues.

View skill

executing-plans

Design

Use the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.

View skill

cost-optimization

Other

This Claude Skill helps developers optimize cloud costs through resource rightsizing, tagging strategies, and spending analysis. It provides a framework for reducing cloud expenses and implementing cost governance across AWS, Azure, and GCP. Use it when you need to analyze infrastructure costs, right-size resources, or meet budget constraints.

View skill