Skip to content
Development
Agent

security-data-exfil

Load when reviewing code that handles URL fetching, file paths, raw SQL, XML parsing, response serialization, debug modes, or error handling that may expose internal data.

From plugin
vexjoy-agent
449198 skills198 agents12 commands83 hooks

Claude Code

$ npx -y skills add notque/vexjoy-agent --agent claude-code

Agent definition

security-data-exfil.md

Data Exfiltration Patterns

Load when reviewing code that handles URL fetching, file paths, raw SQL, XML parsing, response serialization, debug modes, or error handling that may expose internal data.

Data exfiltration: data crosses a trust boundary it should not. Validate inputs at the boundary, expose only fields the caller needs.

---

Validate URLs at the IP Layer Before Fetching

When fetching user-provided URLs (webhooks, image proxies, import-from-URL), validate the resolved IP against a blocklist of internal ranges. String-based URL checks fail because DNS rebinding, IP encoding, and redirects bypass hostname validation.

Correct Pattern

**Python:**

import ipaddress, socket
from urllib.parse import urlparse

BLOCKED_RANGES = [
    ipaddress.ip_network('127.0.0.0/8'),
    ipaddress.ip_network('10.0.0.0/8'),
    ipaddress.ip_network('172.16.0.0/12'),
    ipaddress.ip_network('192.168.0.0/16'),
    ipaddress.ip_network('169.254.0.0/16'),
]

def safe_fetch(user_url: str) -> bytes:
    parsed = urlparse(user_url)
    if parsed.scheme not in ('http', 'https'):
        raise ValueError('scheme not allowed')
    addr = socket.getaddrinfo(parsed.hostname, parsed.port or 443)[0][4][0]
    ip = ipaddress.ip_address(addr)
    if any(ip in net for net in BLOCKED_RANGES):
        raise ValueError('internal IP not allowed')
    resp = requests.get(user_url, allow_redirects=False, timeout=10)
    return resp.content

**TypeScript:**

import { lookup } from 'dns/promises';
import ipaddr from 'ipaddr.js';

const BLOCKED_RANGES = ['private', 'linkLocal', 'loopback', 'uniqueLocal', 'unspecified'];

async function safeFetch(userUrl: string): Promise<Response> {
  const parsed = new URL(userUrl);
  if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error('scheme not allowed');
  const { address } = await lookup(parsed.hostname);
  const ip = ipaddr.parse(address);
  if (BLOCKED_RANGES.includes(ip.range())) throw new Error('internal IP not allowed');
  return fetch(parsed.href, { redirect: 'manual' });
}

Why This Matters

SSRF reaches internal services, cloud metadata (169.254.169.254 returns IAM credentials).

**CVEs:** Capital One 2019, CVE-2021-26855 (Exchange ProxyLogon), CVE-2024-34351 (Next.js Server Actions SSRF), CVE-2020-28168 (axios redirect bypass), CVE-2024-21893 (Ivanti SAML SSRF).

Detection

rg -n 'requests\.(get|post|put)\(|urlopen\(|urllib\.request' --type py
rg -n 'fetch\(|axios\.(get|post)\(|got\(' --type ts --type js
rg -n 'callback_url|webhook_url|target_url|redirect_url' --type py --type ts
rg -n 'allow_redirects=True' --type py

---

Contain File Paths with Realpath Validation

Resolve the full path and verify it stays within the intended base directory.

Correct Pattern

**Python:**

from pathlib import Path
from flask import abort, send_file

def serve_export(name: str):
    base = Path("/var/app/exports").resolve()
    target = (base / name).resolve()
    if not target.is_relative_to(base):
        abort(403)
    return send_file(target)

**TypeScript:**

import path from 'path';

function serveFile(name: string, res: Response) {
  const base = path.resolve('/var/app/exports');
  const target = path.resolve(base, name);
  if (!target.startsWith(base + path.sep)) return res.sendStatus(403);
  res.sendFile(target);
}

**Go:**

func serveExport(w http.ResponseWriter, r *http.Request) {
    base := "/var/app/exports"
    name := filepath.Base(r.URL.Query().Get("name"))
    target := filepath.Join(base, name)
    clean, err := filepath.EvalSymlinks(target)
    if err != nil || !strings.HasPrefix(clean, base) {
        http.Error(w, "forbidden", http.StatusForbidden)
        return
    }
    http.ServeFile(w, r, clean)
}

**Archive extraction (Python):**

import tarfile

def safe_extract(archive_path: str, dest: str):
    with tarfile.open(archive_path) as tar:
        tar.extractall(path=dest, filter="data")  # Python 3.12+

Why This Matters

Path traversal reads arbitrary files. Archive extraction compounds risk (zip-slip).

**CVEs:** CVE-2007-4559 (Python tarfile), CVE-2022-48285 (jszip), CVE-2023-26111 (node-static).

Detection

rg -n 'os\.path\.join\(|Path\(' --type py | rg -v 'resolve\(\)|realpath'
rg -n 'extractall\(' --type py | rg -v 'filter='
rg -n 'sendFile\(|readFile\(|readFileSync\(' --type ts
rg -n 'open\(.*request\.|open\(.*user_' --type py

---

Use Parameterized Queries for All Database Access

Pass user values as parameters, never interpolated strings.

Correct Pattern

**Python (Django):**

invoices = Invoice.objects.filter(customer_id=request.GET["cid"])

# When raw SQL unavoidable:
Invoice.objects.extra(where=["customer_id = %s"], params=[request.GET["cid"]])

**TypeScript (Prisma):**

const users = await prisma.$queryRaw`SELECT * FROM users WHERE name = ${name}`;
// NEVER use $queryRawUnsafe with user input

**Go:**

row := db.QueryRowContext(ctx,
    "SELECT * FROM orders WHERE id = $1 AND user_id = $2",
    orderID, userID,
)

Why This Matters

SQL injection via string interpolation remains one of the most exploited vulnerability classes. ORM escape hatches (`.raw()`, `.extra()`, `$queryRawUnsafe`) bypass parameterization.

**CVEs:** CVE-2023-25813 (Sequelize `literal()`), CVE-2025-23061 (Mongoose `populate({match: userObj})`).

Detection

rg -n '\.raw\(f"|\.extra\(where=\[f"|cursor\.execute\(f"|RawSQL\(f"' --type py
rg -n 'text\(f"|session\.execute\(f"' --type py
rg -n '\$queryRawUnsafe|\$executeRawUnsafe' --type ts
rg -n 'literal\(|sequelize\.query\(' --type ts --type js
rg -n 'fmt\.Sprintf.*SELECT|fmt\.Sprintf.*INSERT|fmt\.Sprintf.*UPDATE' --type go

---

Disable External Entity Resolution in XML Parsers

Configure XML parsers to reject DTDs and external entity references before parsing untrusted XML.

Correct Pattern

**Python:**

from defusedxml im
Read more
@notque
Ships withvexjoy-agent

Essays and writing behind this toolkit live at vexjoy.com. VexJoy Agent connects plain-English requests to specialist agents, skills, and workflows. /do selects the knowledge and tools needed for your task.

44964 skills198 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
449
Star history of notque/vexjoy-agent on GitHub, a chart by star-history.com

Other agents on vexjoy-agent.

modules

modules

Agent

**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes…

@notque@notqueInstall Agent
testing

testing

Agent

**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**:…

@notque@notqueInstall Agent
communication-patterns

communication-patterns

Agent

**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what…

@notque@notqueInstall Agent

More agents from other plugins.

a11y-architect
Auto-invoked

a11y-architect

Agent

Accessibility Architect specializing in WCAG 2.2 compliance for Web and Native platforms. Use PROACTIVELY when designing UI…

ecc13d293k
@affaan-m@affaan-mInstall Agent
analyze-pass-impact

analyze-pass-impact

Agent

Analyzes how a specific topic affects a group of compiler passes. Used by the /plan-update skill to parallelize research across…

react2d252k
@facebook@facebookInstall Agent
orchestrator

orchestrator

Agent

- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as…

codex2d135k
@openai@openaiInstall Agent
cavecrew-builder

cavecrew-builder

Agent

Surgical 1-2 file edit. Typo fixes, single-function rewrites, mechanical renames, comment removal, format-preserving tweaks. Hard…

caveman3d119k
@juliusbrussee@juliusbrusseeInstall Agent

Questions about security-data-exfil.

Which coding agents can use security-data-exfil?
security-data-exfil installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.