DEV Community

correctover
correctover

Posted on

AI Agent Security Audit Outreach: CVE Verification Before Cold-Emailing Maintainers

AI Agent Security Audit Outreach: CVE Verification Before Cold-Emailing Maintainers

A cold email to a project maintainer is a one-shot credibility test. In security research outreach, the fastest way to fail it is to cite a CVE that does not apply — a wrong ID, a wrong severity, or a fix version that never shipped. This field note documents how we built a repeatable method for reaching AI/Agent projects (MCP servers, agent frameworks, LLM gateways, vector databases) and why CVE pre-verification is the lifeblood of the whole process.

Everything below comes from a real outreach campaign run in August 2026. Where a number or CVE appears, it is drawn from the campaign's own scorecards and send logs — no figure here is invented.

AI agent security audit targets: score, don't guess

We scored AI/Agent ecosystem projects on six dimensions — activity, commercialization, security responsiveness, AI relevance, reachability, and "big-tech" status — with security responsiveness and reachability weighted highest (25% each). The round that this article documents focused on the top 10 projects by that score:

Project Score Category
promptfoo 95 LLM evaluation / red-teaming
langfuse 93 LLM observability
ollama 91 Local LLM runtime
weaviate 89 Vector database
lancedb 88 Vector database
Composio 88 Agent tool platform
dify 86 LLM application platform
qdrant 86 Vector database
Guardrails AI 84 Output validation layer
mem0 83 Agent memory layer

Source: scorecards/round2_top10_20260813.md (campaign scorecard, 2026-08-13).

Email reachability without guessing: 10/10

Every address used came from a public page — we never guessed or inferred a format. Three channels covered all 10 targets:

  1. SECURITY.md — the project's own vulnerability-reporting file: promptfoo, langfuse, ollama, mem0.
  2. security.txt (RFC 9116) — the domain's published contact: lancedb.
  3. GitHub org API — the org's public email field: weaviate, Composio, dify, qdrant, Guardrails AI.

Result: 10/10 targets had a verified, publicly-sourced email address. Each entry in the scorecard carries the source URL used to verify it (e.g. https://raw.githubusercontent.com/ollama/ollama/main/SECURITY.md, https://lancedb.com/.well-known/security.txt). Source: scorecards/round2_top10_20260813.md, "邮箱核实明细" section.

The rule that kept us honest: if no public email could be found, we did not send. Projects that only run a GitHub Security Advisory channel (vLLM, fastmcp, Skyvern, beeai) were excluded from this round rather than contacted through a guessed address.

CVE verification: the four-gate check in an AI agent security audit

Before any email that carried a CVE, each candidate CVE had to pass a four-gate check, with the verification method recorded for every entry:

  1. It exists (real CVE/GHSA, not a hallucinated ID).
  2. It is public (disclosed, not embargoed).
  3. It has a fix version (a patch shipped).
  4. The version range is correct (the affected/fixed range matches what we would claim).

Any gate failing → we did not cite that CVE in the cold email, and switched to a preventive-audit pitch instead.

The gate is easy to encode and rerun per candidate, which is what makes the audit reproducible rather than vibes-based:

def passes_cve_gate(cve_id, advisory_url, fixed_versions):
    exists   = advisory_accessible(advisory_url)          # gate 1: real
    is_public = advisory_disclosed(advisory_url)          # gate 2: public
    has_fix  = bool(fixed_versions)                       # gate 3: fix shipped
    correct  = verify_range(fixed_versions, target_range) # gate 4: range right
    return all([exists, is_public, has_fix, correct])
Enter fullscreen mode Exit fullscreen mode

What the check actually caught: three real corrections

The scorecard initially listed a "score" for weaviate and a fix version for Composio that did not survive the four-gate check:

  • weaviate CVE-2025-67818 / CVE-2025-67819 — the draft card said "CVSS 8.7". Verification showed the actual scores are 7.2 (67818) and 4.9 (67819). We cited the CVE IDs in the email but deliberately did not cite the scores. Source: weaviate.io security release (Nov 2025) and GHSA-hmmh-292h-3364; documented in the scorecard appendix.
  • Composio CVE-2024-8953 / CVE-2024-8958 — the draft card said "fixed in 0.6.9+". Verification showed the actual fix shipped in composio-core 0.5.43. We corrected the version before sending. Source: Snyk advisory SNYK-PYTHON-COMPOSIOCORE-9637814 and GitLab gemnasium-db.
  • mem0 CVE-2026-31241 / CVE-2026-31245 — real and public, but the official advisories show no fix version (fixed_versions: [] on the GitHub Advisory). Under gate 3 (must have a fix), we did not cite the CVE numbers and switched to a preventive pitch referencing the vulnerability class (unsafe pickle deserialization + unauthenticated memory endpoints) instead. Source: GHSA-gq6f-qwv9-rf4j and dependabot.ecosyste.ms.

The six CVEs that made it into the emails

After the gate, six projects carried a verified CVE payload (each with a source URL in the scorecard appendix):

Project CVE Class Verified fix
promptfoo CVE-2025-64756 glob CLI command injection (7.5) glob 10.5.0/11.1.0; promptfoo 0.34.0
langfuse CVE-2025-9799 webhook SSRF 3.89.0 (≤3.88.0 affected)
ollama CVE-2024-37032 path traversal → RCE (9.8) 0.1.34
weaviate CVE-2025-67818 / 67819 backup ZipSlip + shard read (IDs only, no score) 1.33.4
Composio CVE-2024-8953 / 8958 eval/file-path RCE (9.8) composio-core ≥0.5.43
Guardrails AI CVE-2024-45858 (GHSA-w392-75q8-vr67) XML validation eval / arbitrary code execution 0.5.10

Source: scorecards/round2_top10_20260813.md, "第五波 CVE 前置核验附录" (2026-08-13), with per-CVE source URLs.

Channel discipline: when a CVE must NOT go in the email

Verification is not enough — the channel matters. Two of our targets (dify and qdrant) officially handle vulnerabilities through GitHub Security Advisories. Cold-emailing a CVE list to a team that runs a formal advisory process reads as process ignorance. For those two, we sent no CVE payload and used a preventive-audit pitch instead. lancedb has no CVE history at all (clean record per Sonatype/safetycli), so it also got a preventive "first-disclosure" offer.

Final payload split across the 10 emails: 6 with verified CVE citations, 2 with preventive pitches (lancedb, mem0), 2 with no CVE by channel rule (dify, qdrant).

What a pre-verified send looks like

The emails followed a tight format: 2–4 word lowercase subject, 95–112 word body, a single call-to-action ("reply 'interested'"), and an ASCII signature. All 10 were sent and logged with per-recipient Message-IDs. 10/10 sent OK (SMTP accepted; delivery confirmed via the send log's receipt tracking). Source: SENT_LOG_20260813.md (campaign send log, 2026-08-13).

The reusable checklist

  1. Reachability before payload: SECURITY.md, then security.txt (RFC 9116), then the GitHub org API. No public source → no email.
  2. Four-gate every CVE: exists, public, has fix, version range correct. One failure → drop the ID, keep the vulnerability class as the pitch.
  3. Fix the card before you send: verify the actual CVSS and the actual patched version against the advisory, not against your own draft.
  4. Match the channel: if a project runs GitHub Security Advisories, don't cold-mail a CVE list — send a preventive pitch.

Conclusion: get your AI agent security audited

This method is the backbone of our own audit work at Correctover. CCS is the security validation layer for AI applications: it statically scans AI/LLM code for SSRF, prompt-injection, and unsafe file access in CI (16 systemic vulnerability patterns), and enforces agent tool-call selection, arguments, and permissions at runtime before a call executes. We run this exact verification discipline on every engagement — the same four-gate CVE check above, applied to your stack.

If you are building an AI agent, an MCP server, or an LLM gateway and want a free static scan pass of your codebase, reach us at correctover.com.

Top comments (0)