Skip to content
Development
Agent

security-pii

Load when reviewing code that handles personal data in logs, test fixtures, error responses, serialized output, URLs, telemetry, or git history.

From plugin
vexjoy-agent
449198 skills198 agents12 commands83 hooks

Claude Code

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

Agent definition

security-pii.md

PII Exposure Patterns

Load when reviewing code that handles personal data in logs, test fixtures, error responses, serialized output, URLs, telemetry, or git history.

PII exposure: personally identifiable information appears in a context with broader visibility, longer retention, or lower trust than intended. Use synthetic data in tests, structured identifiers in logs, explicit field selection in API responses.

---

Use Synthetic Data in Test Fixtures

Tests, fixtures, snapshots, cassettes, and seed files must use obviously synthetic identifiers.

Correct Pattern

**Python:**

fixture = {
    "email": "user@example.com",
    "org_slug": "org-slug",
    "name": "Jane Doe",
    "ip": "198.51.100.23",       # RFC 5737
    "arr_usd": 120000,
    "renewal_date": "2026-01-01",
}

**TypeScript:**

const fixture = {
  email: 'user@example.com',
  orgSlug: 'org-slug',
  name: 'John Doe',
  ip: '203.0.113.42',  // RFC 5737 TEST-NET-3
  monthlySpend: 1000,
  seatCount: 25,
};

Safe Domains and IP Ranges

| Type | Safe Values | Standard | |------|-------------|----------| | Email domains | `example.com`, `example.org`, `example.net`, `.invalid` | RFC 2606 | | IPv4 | `192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24` | RFC 5737 | | IPv6 | `2001:db8::/32` | RFC 3849 | | Names | `Jane Doe`, `John Doe`, `Alice`, `Bob`, `Acme Corp` | Convention |

Why This Matters

Real data in git history cannot be fully purged. A single real email is a GDPR data subject access request waiting to happen.

Detection

rg -n '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' tests/ fixtures/ --type py --type ts | \
  rg -v 'example\.(com|org|net)|noreply|test@|foo@|user@'

rg -n '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' tests/ fixtures/ | \
  rg -v '192\.0\.2\.|198\.51\.100\.|203\.0\.113\.|127\.0\.0\.|0\.0\.0\.0|10\.\|172\.(1[6-9]|2|3[01])\.|192\.168\.'

rg -n 'customer|account.*slug|org.*slug|tenant' tests/ fixtures/ --type py --type ts

---

Log Structured Identifiers Instead of Raw PII

Log internal ID, salted hash, or correlation ID — never raw email, IP, phone, or request body.

Correct Pattern

**Python:**

import hashlib

def hash_email(email: str) -> str:
    return hashlib.sha256(f"salt:{email}".encode()).hexdigest()[:12]

logger.warning(
    "identity lookup failed",
    extra={"user_id": user.id, "email_hash": hash_email(user.email)},
)

**TypeScript:**

logger.warn('signup failed', {
  userId: user.id,
  reason: 'validation_failed',
});

**Go:**

slog.Warn("identity lookup failed",
    "user_id", user.ID,
    "error", err,
)

Why This Matters

PII in logs creates a secondary data store bypassing access controls. Log aggregators retain data for months, index for full-text search, and expose to broad teams.

Detection

rg -n 'logger\.\w+\(.*email|logger\.\w+\(.*REMOTE_ADDR|logger\.\w+\(.*request\.(body|data|META)' --type py
rg -n 'logger\.\w+\(.*email|logger\.\w+\(.*req\.(body|ip)|console\.\w+\(.*email' --type ts
rg -n 'set_user\(|set_tag\(.*email|set_extra\(.*email' --type py
rg -n 'slog\.\w+\(.*email|log\.\w+\(.*email|zap\.\w+\(.*email' --type go

---

Keep PII Out of URL Query Strings and Path Segments

Emails, phone numbers, names in URLs appear in browser history, server logs, CDN logs, referrer headers, analytics.

Correct Pattern

**Python:**

request.session["login_error"] = "invalid_magic_code"
return redirect("/login/error")

**TypeScript:**

return redirect('/oauth/error?reason=invalid_code');

Detection

rg -n 'redirect.*email=|redirect.*user=|redirect.*phone=' --type py --type ts
rg -n 'f".*\?.*email|`.*\?.*email|\?.*email=' --type py --type ts

---

Exclude PII Fields from Serialized API Responses

Declare explicit field lists excluding PII not required by the caller.

Correct Pattern

**Python (DRF):**

class UserPublicSerializer(ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'display_name', 'avatar_url']

**TypeScript (Prisma):**

const user = await prisma.user.findUnique({
  where: { id },
  select: { id: true, displayName: true, avatarUrl: true },
});
return res.json(user);

**GraphQL:**

type UserPublic {
  id: ID!
  displayName: String!
  avatarUrl: String
}

Why This Matters

`fields = '__all__'` exposes every column including fields added by future migrations. Full-row Prisma queries return password hashes, 2FA secrets.

Detection

rg -n "fields = '__all__'" --type py
rg -n 'email|phone|ipAddress|password' --type graphql --type ts | rg 'type |interface '
rg -n 'findUnique\(|findFirst\(' --type ts | rg -v 'select:'

---

Scrub PII from Error Responses and Exception Handlers

Error responses: generic message + correlation ID. Stack traces, SQL, request bodies stay server-side.

Correct Pattern

**Python:**

import uuid

@app.errorhandler(Exception)
def handle_error(error):
    correlation_id = str(uuid.uuid4())
    app.logger.error("unhandled exception",
        extra={"correlation_id": correlation_id, "error": str(error)}, exc_info=True)
    return {"error": "internal server error", "reference": correlation_id}, 500

**TypeScript:**

app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  const correlationId = crypto.randomUUID();
  console.error({ correlationId, error: err.stack });
  res.status(500).json({ error: 'internal server error', reference: correlationId });
});

Detection

rg -n 'err\.stack|error\.stack|traceback|exc_info' --type py --type ts | rg -v 'logger\.|console\.'
rg -n 'request\.(body|data|form)|req\.body' --type py --type ts | rg 'return |res\.(json|send)'
rg -n 'set_user\(|set_context\(|set_extra\(' --type py | rg 'email|phone|ip|address'

---

Detect Common PII Patterns in Code

# Email addresses (excluding safe domains)
rg -n '[a-zA-Z0-9._%+-]+@
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-pii.

Which coding agents can use security-pii?
security-pii 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.