Security operations automation pairs deterministic scanners — Semgrep, npm audit, detect-secrets, Trivy — with an AI agent in Cursor, Claude Code, or Codex that reads their output, separates exploitable findings from false positives, opens the fix as a reviewed pull request, gates CI on fixable HIGH and CRITICAL vulnerabilities, and drafts SOC 2 and GDPR evidence citing real artifacts.
Your scanner just flagged 412 “critical” findings across 50 container images and 14 dependency CVEs. Eleven of the CVEs are in dev-only packages that never ship, two are unreachable from any code path you call, and the one that matters is buried at the bottom of the report. Meanwhile the on-call channel is on fire and your SOC 2 auditor wants access-review evidence by Friday.
The promise of “AI security” is rarely a new scanner. It is an assistant that reads scanner output the way a senior engineer would — correlating a finding with your code, ruling out what is unreachable, and turning what is left into a reviewed pull request instead of a 200-line JSON dump nobody opens.
What you’ll walk away with on security operations automation
A triage prompt for each scanner class: Semgrep SAST findings, npm audit CVEs, Trivy container images, and a live secret found in history
The per-tool mechanics for running the loop in Cursor (agent mode + MCP), Claude Code (headless -p in CI + hooks), and Codex (sandboxed CLI + Cloud automation)
A CI workflow that runs the scanners as a hard gate and the AI triage as an advisory step, plus a Trivy job that blocks merges on fixable HIGH/CRITICAL
Verified MCP setups for AWS and Azure security data, the GitHub MCP server for opening remediation PRs, and the one MCP-hygiene scanner you should actually run
Kubernetes hardening that uses current APIs (Pod Security Admission, not the removed PodSecurityPolicy)
A clear-eyed failure-mode list so you don’t ship false confidence
The mistake most “AI SecOps” writeups make is asking the model to be the scanner. It isn’t. Semgrep, CodeQL, and Trivy are deterministic, fast, and auditable — keep them. 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.
Run the deterministic scanners and capture machine-readable output. These are real commands, not prompts — paste them into a terminal:
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.
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.
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 scanner commands in step 1 are identical everywhere. The mechanics of steps 2 and 3 differ per tool.
Run the scanners so semgrep.json / audit.json / trivy.json exist in the workspace, then drop into Agent mode (Cmd/Ctrl+I). Agent mode reads the JSON directly and edits the flagged source files in place, showing each change as a checkpoint you accept or reject. With the @aws-security MCP server connected, add @aws-security to the prompt so it can check whether an affected service is actually internet-exposed.
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:
Now the agent reads semgrep.json, patches the two real findings, and calls the GitHub MCP create_pull_request tool — all from one prompt.
Claude Code shines for the headless, in-CI half of this loop. Run the scanners, then pipe the triage prompt through -p (print mode) with the JSON on disk. Claude Code can run grep/rg itself to confirm reachability, which makes its prioritization concrete rather than guessed:
Terminal window
semgrep--config=auto--json--outputsemgrep.json.
claude-p"Read semgrep.json. For each finding, classify exploitable vs \
false-positive and output JSON: {file, line, verdict, reason, patch}. \
Treat anything under test/ or scripts/ as non-shipping."\
The same shape works for container findings, where the useful output is a priority ranking rather than a patch set:
Terminal window
claude-p"Read trivy.json (Trivy JSON). For each HIGH/CRITICAL: name the CVE and package, use Grep to check if the vulnerable path is reachable in our source, and assign P0/P1/P2 with a one-line justification. Output a markdown table sorted by priority."--output-formatjson>container-triage.json
Add the GitHub MCP server with the documented HTTP transport so Claude Code can open the PR locally or in CI:
A PreToolUse hook is the right place to enforce the “no auto-merge of security fixes” rule — block any Bash(gh pr merge*) on a branch named security-* and require human review.
Codex (GPT-5.6 Sol across App/CLI/IDE/Cloud) is strongest for the scheduled, hands-off variant, and its sandbox defaults make it a safe place to review each command before it runs:
Terminal window
semgrep--config=auto--json--outputsemgrep.json.
codexexec"Read semgrep.json and rank findings by real exploitability; \
propose a minimal patch for each genuinely exploitable one."\
--ask-for-approvalon-request
For an interactive pass with per-action review, launch codex -a untrusted (it asks before each action) and paste:
Read trivy.json. For each HIGH/CRITICAL finding, check whether the vulnerable
package is actually imported and reachable in this repo, classify it P0/P1/P2
by real exploitability, and propose the smallest dependency bump that resolves
the P0s. Show me the diff before applying anything.
For the recurring scan, use Codex Cloud with its GitHub integration: point a Cloud task at the repo on a schedule, let it run the scanners and triage, and have it open the remediation PR through the connected GitHub app. Keep --ask-for-approval on-request (not never) so a destructive remediation still pauses for a human.
These are the recipes. They assume you have already captured the scanner JSON above. Each targets a different input, and the difference matters: Semgrep findings need taint reachability, CVEs need call-site reachability, container findings need exposure, and a leaked secret needs rotation before anything else.
A real, reachable finding rather than a fabricated one:
P0 — CVE-2026-42945 (nginx, ngx_http_rewrite_module heap overflow, CVSS 9.2).
Affected: nginx:1.30.0 base image in 3 services. Reachable: yes — all three use rewrite directives. Internet-exposed: yes (edge ingress).
Fix: rebuild on nginx:1.31.0 (or 1.30.1), which ships the patch. Patch within 24h.
That CVE is real (introduced in 2008, disclosed 2026, fixed in nginx 1.31.0 / 1.30.1) — use verifiable findings in security work; never let the agent present an invented CVE number as fact.
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 or 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
For images, the highest-leverage move is a scanner in front of deploys so a fixable critical blocks the merge automatically. Trivy ships a maintained GitHub Action; the agent’s job is to wire it into your pipeline and set the gate correctly.
A correct gate looks roughly like this — note that it scans the built image and fails the build, rather than just reporting:
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: myorg/api:${{ github.sha }}
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: '1'# fail the build on a fixable HIGH/CRITICAL
format: sarif
output: trivy.sarif
The pattern generalizes: build-time scanning in CI, continuous registry scanning for images already deployed, and an admission policy at the cluster so a vulnerable image can’t be scheduled even if it slips past CI.
The challenge: your team deploys 50+ images weekly. Manual review is a bottleneck; unscanned images are a risk. The fix is layered, automated gates — and an admission policy that uses current Kubernetes APIs.
Use the agent to:
Wire Trivy into CI as a blocking gate (prompt above).
Enable continuous registry scanning for already-deployed images.
Enforce Pod Security Standards at the cluster via Pod Security Admission (the built-in successor to the removed PodSecurityPolicy), or a policy engine like Kyverno / OPA Gatekeeper for richer rules.
Audit live workloads against the CIS Kubernetes Benchmark.
PodSecurityPolicy (policy/v1beta1) was removed in Kubernetes 1.25 and does not exist on any supported cluster — any guide still telling you to “implement pod security policies” is out of date. The current built-in mechanism is Pod Security Admission via namespace labels, enforcing the three Pod Security Standards (privileged / baseline / restricted), paired with a NetworkPolicy for traffic isolation.
# namespace with Pod Security Standards enforced (replaces PodSecurityPolicy)
# 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 }
Two prompts cover the two situations you will actually be in. The first is for one namespace whose traffic you already know — it produces manifests you can apply today. The second is the cluster-wide plan, including the RBAC and policy-engine decision the first one skips.
A few security data sources are worth wiring in as MCP servers so the agent can query live cloud posture instead of working from a stale export. The server (command, args, env) is the same everywhere — only the config file and its format differ: Cursor and Claude Code (.mcp.json) use the JSON mcpServers shape, while Codex (~/.codex/config.toml) expresses the same fields as a TOML [mcp_servers.<name>] table.
AWS security posture — the aws-security-mcp package (real, on npm) exposes Security Hub, GuardDuty, and IAM findings:
{
"mcpServers": {
"aws-security": {
"command": "npx",
"args": ["-y", "aws-security-mcp"],
"env": {
"AWS_REGION": "us-east-1"
}
}
}
}
The same server in Codex’s ~/.codex/config.toml — identical fields, TOML syntax:
[mcp_servers.aws-security]
command = "npx"
args = ["-y", "aws-security-mcp"]
[mcp_servers.aws-security.env]
AWS_REGION = "us-east-1"
Do not hardcode AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY as literals in a config you might commit. Let the server pick up credentials from the standard AWS chain — an assumed IAM role, SSO, or ~/.aws/credentials — the same way the AWS CLI does.
Azure security posture — Microsoft’s first-party @azure/mcp server covers Defender for Cloud and resource queries:
GitHub MCP server (github/github-mcp-server, remote at https://api.githubcopilot.com/mcp/) is the persistent connection for the remediation half: it lets the agent search code across the org, read Dependabot and secret-scanning alerts, and open the PR. Use it when the agent needs to act on GitHub repeatedly. There is no @anthropic/* package and no one-string new MCPClient('github') convenience layer — MCP is connect(transport) then callTool({ name, arguments }), and if you script directly against the SDK the package is @modelcontextprotocol/sdk with the client at @modelcontextprotocol/sdk/client/index.js.
On the skills side, 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.
If you’re adding MCP servers to a security workflow, audit them. snyk-agent-scan (run via uvx) statically inspects your installed MCP servers for prompt injection, tool poisoning, and rug-pull attacks — the supply-chain risks specific to MCP. This is Invariant Labs’ former mcp-scan, renamed after Snyk acquired Invariant Labs in June 2025; the old mcp-scan package on PyPI is now just a redirect shim that installs and forwards to snyk-agent-scan, so use the canonical name:
Terminal window
# Scan all installed MCP server configs for malicious tool descriptions
uvxsnyk-agent-scan@latestscan
# Inspect the exact tool descriptions your model is being fed
uvxsnyk-agent-scan@latestinspect
This is a real, current command — and a genuinely good habit once you depend on third-party MCP servers for security data.
Compliance is mostly evidence assembly, and that is exactly the kind of structured-collation work an agent is good at — provided you point it at real artifacts (access logs, IaC, config exports) rather than asking it to assert compliance from nothing.
Drop your access-review export and IaC into context, connect @aws-security for live config, and ask for an evidence package mapped to specific controls.
Using `access-review.csv` (our quarterly IAM export) and the Terraform in
`infra/`, draft a SOC 2 evidence package for controls CC6.1 (logical access),
CC6.3 (network access), and CC8.1 (change management). For each control: cite
the specific file/line or log entry that demonstrates it, and flag any control
where the evidence is missing or weak. Do NOT claim a control is satisfied
without pointing to the artifact that proves it.
Claude Code can read the repo, run git log for change-management evidence, and assemble the package in one pass.
Map our personal-data flows for a GDPR Article 30 record. Search the codebase
and `infra/` for: where PII is collected, which datastores hold it (grep for
schema/migrations), where it crosses a service or third-party boundary (Stripe,
SendGrid, analytics), and where retention/deletion is implemented. Output a
table of data category -> store -> lawful basis (mark "UNKNOWN — needs legal
review" if not evident) -> retention. Be explicit about gaps; do not invent a
lawful basis.
Use Codex with read-only sandboxing for an evidence pass — it should read and report, not modify infrastructure.
Terminal window
codex--sandboxread-only"Assemble SOC 2 CC6.1 access-control evidence from access-review.csv and infra/"
Then ask for the gap report, which is the part an auditor actually acts on:
Read `access-review.csv` and `infra/`. For SOC 2 CC6.1/CC6.3/CC8.1, produce a
gap report: control, current evidence (with file/log citation), and the single
most important gap to close. Rank gaps by audit risk. Flag any control with no
supporting artifact as a blocker.
The discipline that makes this trustworthy: every “satisfied” claim must cite a real artifact. An evidence package the agent fabricated will fail the moment an auditor asks to see the underlying log — so prompt explicitly for citations and gap-flagging, as above.