Skip to content

Security Operations with AI

Snyk just flagged 14 “critical” CVEs in your dependency tree. Eleven are in dev-only packages that never ship, two are unreachable from any code path you actually call, and the one that matters is buried at the bottom of the report. Your release is blocked while you triage by hand, again. The promise of “AI security” is rarely a new scanner — it is an assistant that reads the scanner output the way a senior engineer would: separating real exploitable findings from noise, and turning the real ones into reviewed pull requests instead of a 200-line JSON dump nobody opens.

This guide builds that loop with tools that exist today — Semgrep, npm audit, detect-secrets, Trivy, and the GitHub MCP server — driven from Cursor, Claude Code, or Codex.

  • A triage prompt that turns a raw semgrep --config=auto dump into ranked, exploitable-vs-false-positive findings with minimal patches
  • A dependency-CVE prompt that reads npm audit --json and tells you which CVEs are actually reachable in your code
  • A secret-rotation prompt for when detect-secrets finds a live credential in history
  • The per-tool mechanics for running this loop in Cursor (agent mode + MCP), Claude Code (headless -p in CI + hooks), and Codex (cloud automation + GitHub)
  • A CI job that runs the scan-and-triage step on every PR and fails on genuinely new high-severity findings

The mistake most teams make is wiring AI to replace the scanner. The scanner (Semgrep, CodeQL, Trivy) is deterministic, fast, and auditable — keep it. The AI’s job is the expensive part a human currently does: reading the findings, ruling out false positives, and proposing the smallest correct fix. The loop is three steps.

  1. Run the deterministic scanners and capture machine-readable output. These are real commands, not prompts — paste them into a terminal:

    Terminal window
    # Static analysis (registry rules)
    semgrep --config=auto --json --output semgrep.json .
    # Dependency CVEs
    npm audit --json > audit.json
    # Secrets in working tree + history
    detect-secrets scan --all-files > secrets.json
    # Container image (if you ship one)
    trivy image --format json --output trivy.json myapp:latest

    semgrep installs via pip install semgrep or brew install semgrep; detect-secrets via pip install detect-secrets; trivy via brew install trivy or its install script. None of these are npm packages — do not npm install them.

  2. Hand the output to the AI for triage. This is where the value is. The scanner says “potential SQL injection at db.ts:42”; the assistant tells you whether db.ts:42 is reachable from untrusted input or is a parameterized query the rule mis-flagged. Use the triage prompt below.

  3. Turn the real findings into a reviewed PR — never an auto-merged one. Let the AI draft the patch and the PR body, but a human (or a required CI gate) approves it. Auto-applying AI patches to a security-sensitive file is how you ship a regression with a green checkmark.

The mechanics of steps 2 and 3 differ per tool. The scanner commands in step 1 are identical everywhere.

Open the repo in Cursor, run the scanners so semgrep.json / audit.json exist in the workspace, then drop into Agent mode (Cmd/Ctrl+I). Agent mode can read the JSON files directly and edit the flagged source files in place, showing each change as a checkpoint you accept or reject.

Add the GitHub MCP server so the agent can open the remediation PR without leaving the editor. In Cursor’s MCP settings (or .cursor/mcp.json), register the official remote server:

{
"mcpServers": {
"github": {
"url": "https://api.githubcopilot.com/mcp/",
"headers": { "Authorization": "Bearer ${GITHUB_PAT}" }
}
}
}

Now the agent reads semgrep.json, patches the two real findings, and calls the GitHub MCP create_pull_request tool — all from one prompt.

These are the recipes. They assume you have already captured the scanner JSON as described above.

Two extensibility options change this workflow, and they are not interchangeable.

  • GitHub MCP server (github/github-mcp-server, remote at https://api.githubcopilot.com/mcp/) is the persistent connection: it lets the agent search code across the org, read Dependabot/secret-scanning alerts, and open the remediation PR. Use it when the agent needs to act on GitHub repeatedly. There is no @anthropic/* or one-string new MCPClient('github') convenience layer — MCP is connect(transport) then callTool({ name, arguments }). If you script directly against the SDK, the package is @modelcontextprotocol/sdk and the client lives at @modelcontextprotocol/sdk/client/index.js.
  • The Semgrep skill (semgrep/skills, installable with npx skills add semgrep/skills) is the lighter-weight option: it teaches the agent to write custom Semgrep rules for your codebase and run scans, without standing up a server. Reach for the skill when the job is “author and run rules”; reach for the MCP server when the job is “operate on GitHub.”

Hardening the cluster (current Kubernetes)

Section titled “Hardening the cluster (current Kubernetes)”

If you ship to Kubernetes, the AI can draft your admission policy — but it must draft the current one. PodSecurityPolicy (policy/v1beta1) was removed in Kubernetes 1.25 and does not exist on any supported cluster. The supported successor is Pod Security Admission via namespace labels, paired with a NetworkPolicy for traffic isolation.

# namespace with Pod Security Standards enforced (replaces PodSecurityPolicy)
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
---
# default-deny egress/ingress, then allow only what's needed
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: payments-isolation
namespace: payments
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes: [Ingress, Egress]
ingress:
- from:
- podSelector:
matchLabels: { app: api-gateway }
ports:
- { protocol: TCP, port: 8443 }
egress:
- to:
- podSelector:
matchLabels: { app: postgres }
ports:
- { protocol: TCP, port: 5432 }

Run the deterministic scanners on every PR, then run the AI triage as an advisory step. The hard gate stays deterministic — the pipeline fails on genuinely new high-severity findings from Semgrep/Trivy, not on the AI’s opinion. Note actions/checkout@v5 (Node 24; v3 runners are end-of-life as of June 2026).

.github/workflows/security-scan.yml
name: Security Scan
on:
pull_request:
schedule:
- cron: '0 */6 * * *'
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Semgrep (hard gate on new high-severity)
uses: semgrep/semgrep-action@v1
with:
config: auto
- name: Dependency audit
run: npm audit --audit-level=high
- name: Secret scan
run: |
pipx install detect-secrets
detect-secrets scan --all-files | tee secrets.json
# Advisory: AI triage summary posted to the PR, not a blocker
- name: AI triage (advisory)
if: github.event_name == 'pull_request'
run: |
semgrep --config=auto --json --output semgrep.json . || true
claude -p "Summarize semgrep.json: rank exploitable findings, list \
likely false positives, suggest the smallest fix for each real one." \
--allowedTools "Read,Grep" --output-format json > triage.json
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}