Skip to content

Latest commit

 

History

History
291 lines (211 loc) · 9.6 KB

File metadata and controls

291 lines (211 loc) · 9.6 KB
title Quick Start: Security for AI Assisted Coding
slug quick-start-ai-coding
category ai
depth 1
audit_level
1
2
last_reviewed 2026-04-21
sources
OWASP Top 10 2021 — https://owasp.org/Top10/
OWASP Top 10 for LLM Applications v2025 — https://genai.owasp.org/llm-top-10
CVE-2025-54136 Cursor MCPoison persistent RCE — https://nvd.nist.gov/vuln/detail/CVE-2025-54136 (2025-08, Check Point)
triggers_strong
ai assisted coding security
ship ai code safely
before you push ai code
quick start security
triggers_weak
ai coding
security quick start
related
vibecoder-traps
pre-push-checklist

Quick Start: Security for AI-Assisted Coding

Last reviewed: 2026-04-03 | Next review: 2026-10-03 | Priority: Essential | Automation: Partial (Gitleaks + Semgrep cover most patterns; logic review manual)

You are using Claude Code, Codex, Gemini CLI, Cursor, Copilot, ChatGPT, or another AI assistant / builder to ship fast. This guide tells you what to check before you ship, in order of importance.


The One Rule

Code generated by AI is optimized for working, not for secure. Always review before pushing.

AI assistants produce vulnerable code regularly:

  • Hardcoded API keys in examples
  • eval() or exec() with user input
  • No authorization check on endpoints
  • Passwords hashed with SHA-256
  • verify=False on HTTPS calls "to get it working"
  • SQL built by string concatenation
  • pickle.loads() or yaml.load() without safe mode

These patterns appear because they work in isolation. They are not safe in production.


Before You Push: 10 Things to Check

1. No secrets in the code

# Scan right now
git diff --cached | grep -iE "^\+.*(api.?key|secret|password|token|sk-|ghp_|AKIA|sk_live)"

If you find one: see references/ops/secret-leak-prevention.md.

2. .env is not tracked

git status | grep "\.env"
# Should return nothing

If it appears: echo ".env" >> .gitignore && git rm --cached .env

3. No dangerous function calls on user input

Check for these patterns in code you generated or pasted:

Pattern Language Risk
eval( JS/Python/PHP Code injection
exec( Python Code injection
shell=True Python subprocess Command injection
os.system( Python Command injection
innerHTML = JavaScript XSS
pickle.loads( Python Remote code execution
yaml.load( without Loader=yaml.SafeLoader Python Remote code execution
verify=False Python requests MITM attack
SQL string + user input Any SQL injection

4. Every endpoint has an authorization check

For each new route the AI generated, ask: "Can any logged-in user call this, or only the owner?"

# AI often generates this - no ownership check
@app.get("/invoices/{id}")
def get_invoice(id: int, user=Depends(auth)):
    return db.query(Invoice).filter(Invoice.id == id).first()
    # any user can read any invoice by changing the ID

# This is what you need
def get_invoice(id: int, user=Depends(auth)):
    invoice = db.query(Invoice).filter(
        Invoice.id == id,
        Invoice.owner_id == user.id  # ownership check
    ).first()
    if not invoice:
        raise HTTPException(404)
    return invoice

5. Passwords use a real password hash

AI often generates MD5, SHA-256, or SHA-512. None of these are acceptable for passwords.

# What AI gives you (wrong)
import hashlib
hashed = hashlib.sha256(password.encode()).hexdigest()

# What you need
from argon2 import PasswordHasher
ph = PasswordHasher()
hashed = ph.hash(password)

6. No API keys in frontend code

If the AI generated a frontend file that calls OpenAI, Anthropic, Stripe, or any service directly from the browser using a secret key, that key will be visible to every user who opens devtools.

Never acceptable in frontend: OpenAI key, Anthropic key, Stripe secret key, Supabase service_role, any OAuth client secret.

Move the call to a backend route. The frontend calls your backend; your backend calls the external API.

7. Error messages do not reveal internals

# What AI often gives you
return {"error": str(e), "traceback": traceback.format_exc()}

# What you need
logger.error(f"Error: {e}", exc_info=True)
return {"error": "Something went wrong.", "id": error_id}

8. Input is validated server-side

AI often generates frontend validation but skips server-side validation. Attackers call the API directly.

# Minimum: validate types and length on every endpoint
from pydantic import BaseModel, Field

class CreatePost(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    content: str = Field(min_length=1, max_length=10000)
    # status not in the model - user cannot set it

9. No TLS verification disabled

# AI sometimes adds this to "fix" SSL errors in dev
requests.get(url, verify=False)  # remove this before prod

# Fix the certificate properly, or pass the CA bundle:
requests.get(url, verify="/path/to/ca-bundle.crt")

10. Dependencies are not obviously vulnerable

npm audit --audit-level=high
pip-audit

Extra Checks for Cursor, Browser Builders, and No-Code Tools

If you are using an AI IDE assistant, a browser coding tool, or a no-code / low-code platform, add these checks:

11. No public preview or shared app without authentication

  • Preview links should not expose internal tools, admin panels, or customer data.
  • Demo mode is not a security control.

12. No production connector owned by a personal account

  • Use a service account, not a founder or employee mailbox.
  • Record token owner, scope, and rotation date.

13. No raw production data in prompts, memory, or knowledge uploads

  • Use synthetic or masked data.
  • Never upload HR, finance, support exports, or legal files blindly.

14. Webhooks and automations are verified

  • Verify signatures and timestamps.
  • Review trigger conditions so untrusted email/form data cannot launch sensitive actions.

15. The app or workflow can be exported, reviewed, and backed up

  • If there is no versioned export or Git sync, recovery and peer review are weak.

Full guide: references/ai/ai-ide-no-code-security.md


False Sense of Security - What Does NOT Protect You

What you might think Reality
"The repo is private" Private repos can be made public, leaked, or accessed by collaborators. A secret in Git history is compromised.
"I deleted the commit" Deletion does not remove from history. Force-push + filter-repo is needed. And GitHub may have cached it.
"It's just a test key" Test keys often have the same access as production keys. They also train you to accept bad habits.
"CORS protects my API" CORS only affects browsers. curl, Postman, and backend scripts bypass it entirely.
"The frontend is minified" Minified JavaScript is trivially readable with browser devtools. Any secret inside is visible.
"It's behind a VPN" VPNs control network access, not application authorization. IDOR and injection still work inside a VPN.
"The endpoint is not documented" Endpoints are discovered by scanners, JS bundle analysis, and Certificate Transparency logs.
"We use HTTPS" HTTPS encrypts transit. It does not protect against XSS, CSRF, IDOR, injection, or weak passwords.

What to Do When Something Goes Wrong

"I pushed a secret to Git"

  1. Revoke the key immediately (do not wait to clean history first).
  2. Check usage logs for unauthorized calls.
  3. Clean history with git filter-repo.
  4. Force push.
  5. Full guide: references/ops/secret-leak-prevention.md

"My app is returning stack traces to users"

  • Add a generic error handler. See references/appsec/production-error-handling.md.

"I think there's an injection vulnerability"

  • Check references/appsec/language-patterns.md for your language.
  • Replace with parameterized queries or safe equivalents.

"I'm not sure if my auth is correct"

  • Check references/iam/authorization-rbac.md for IDOR patterns.
  • Test manually: log in as user A, try to access user B's resources by changing IDs.

"I used a weak password hash"

  • Migrate hashes: on next login, re-hash with Argon2id and store the new hash.
  • Never re-hash without the plaintext (you need the user to log in again).

5-Minute Security Setup for a New Project

# 1. Create .gitignore with security template
curl -o .gitignore https://raw.githubusercontent.com/stealthsrc/security-hardening/main/.gitignore-security-template

# 2. Create .env.example (edit with placeholder values, commit this)
#    Create .env (real values, never commit)
cp .env.example .env
# edit .env with real values

# 3. Install Gitleaks pre-commit hook
pip install pre-commit
cat > .pre-commit-config.yaml << 'EOF'
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.4
    hooks:
      - id: gitleaks
EOF
pre-commit install

# 4. Copy security CI workflow
mkdir -p .github/workflows
# Copy .github/workflows/security.yml from this repo

# 5. Verify no secrets are already staged
gitleaks detect --source . --verbose

Quick Reference Card

Print or bookmark this.

BEFORE EVERY PUSH:
[ ] No API key / token / password in code or .env committed
[ ] No eval/exec/shell=True/pickle.loads on user input
[ ] Every endpoint checks auth AND ownership
[ ] Passwords use Argon2id or bcrypt
[ ] No secret in frontend code
[ ] Error responses are generic (no stack trace)
[ ] Input validated server-side
[ ] verify=False removed
[ ] npm audit / pip-audit clean
[ ] .env not in git status