Back to skill

Security audit

Agent Email Setup

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Resend email setup guide, but its reference inbox API and automation design can expose or process sensitive emails without enough access control or guardrails.

Install only after adding authentication to all inbox endpoints, making the webhook secret mandatory, treating all email content as untrusted data, requiring owner approval for replies or verification actions, and separating one-time full-permission Resend setup credentials from lower-privilege runtime keys.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/inboundEmail.js:103
Finding

Mailbox APIs Expose Sensitive Email Without Authentication

Content
View full analysis
{ try { const limit = parseInt(req.query.limit) || 50; const files = fs.readdirSync(INBOX_DIR) .filter(f => f.endsWith('.json') && !f.includes('.processed') && f !== '.lastcheck') .sort().reverse().slice(0, limit); const emails = files.map(f => { try { return JSON.parse(fs.readFileSync(path.join(INBOX_DIR, f), 'utf8')); } catch { return null; } }).filter(Boolean); res.json({ emails, count: emails.length }); } catch (err) { res.status(500).json({ error: 'Failed to read inbox' }); } }); // GET /:id — Read specific email (marks as read) router.get('/:id', async (req, res) => { try { const filePath = path.join(INBOX_DIR, `${req.params.id}.json`); if (!fs.existsSync(filePath)) return res.status(404).json({ error: 'Email not found' }); const email = JSON.parse(fs.readFileSync(filePath, 'utf8')); email.read = true; fs.writeFileSync(filePath, JSON.stringify(email, null, 2)); res.json(email); } catch (err) { res.status(500).json({ error: 'Failed to read email' }); } }); // POST /:id/ack — Mark email as processed router.post('/:id/ack', async (req, res) => { try { const filePath = path.join(INBOX_DIR, `${req.params.id}.json`); if (!fs.existsSync(filePath)) return res.status(404).json({ error: 'Email not found' }); const email = JSON.parse(fs.readFileSync(filePath, 'utf8')); email.processed = true; fs.writeFileSync(filePath, JSON.stringify(email, null, 2)); // Rename to .processed to hide from listing fs.renameSync(filePath, `${filePath}.processed`); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: 'Failed to ack email' }); } } ...[truncated 1605 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
references/inboundEmail.js:52
Finding

Webhook Verification Fails Open When the Signing Secret Is Missing

Content
View full analysis
{ try { // Verify webhook signature const signature = req.headers['x-resend-signature'] || req.headers['resend-signature']; const secret = process.env.RESEND_WEBHOOK_SECRET; if (secret && signature) { if (!verifySignature(req.body.toString(), signature, secret)) { console.warn('[INBOUND EMAIL] Invalid signature'); return res.status(401).json({ error: 'Invalid signature' }); } } else if (secret) { console.warn('[INBOUND EMAIL] No signature header, rejecting'); return res.status(401).json({ error: 'Missing signature' }); } const body = JSON.parse(req.body.toString()); ``` ### Technical Analysis Signature verification is enforced only when `RESEND_WEBHOOK_SECRET` is configured. When the secret is missing, neither branch rejects the request, and processing continues with attacker-controlled JSON. This converts a deployment or secrets-management error into complete loss of webhook authentication. An external party able to reach the endpoint can impersonate Resend and inject arbitrary sender, recipient, subject, text, HTML, and header data into the stored inbox. ### Attack Path 1. The application is deployed without `RESEND_WEBHOOK_SECRET`, or the environment variable is accidentally removed. 2. An attacker sends an unsigned JSON request to `POST /api/inbound-email`. 3. The conditional verification block does not reject the request because `secret` is falsy. 4. The payload is parsed and stored as a legitimate inbox message. 5. The monitoring workflow later reads and processes the injected content. 6. If autonomous actions are enabled, the injected message may influence downstream agent behavior. ### Impact Assessment An ...[truncated 293 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
references/inboundEmail.js:36
Finding

Webhook Signature Verification Does Not Prevent Replay Attacks

Content
View full analysis
s.trim()); const timestamp = parts.find(p => p.startsWith('t=')); const hash = parts.find(p => !p.startsWith('t=')); if (!timestamp || !hash) return false; const signedPayload = `${timestamp.slice(2)}.${rawBody}`; const expected = crypto.createHmac('sha256', secret) .update(signedPayload).digest('hex'); return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(expected)); } catch { return false; } } ``` ### Technical Analysis The timestamp is included in the HMAC calculation, but the code never parses it as a time value or verifies that it falls within an acceptable age window. The handler also generates a new local identifier for every delivery and does not deduplicate requests using a provider event identifier. Consequently, a previously valid signed webhook remains valid when submitted again. Signature verification proves that the captured request was originally authentic, but it does not prove that the current delivery is fresh. ### Attack Path 1. An attacker captures or otherwise obtains a valid signed webhook request, including its body and signature header. 2. The attacker resends the same request to the webhook endpoint at a later time. 3. The HMAC remains valid because the signed timestamp and body are unchanged. 4. No timestamp-age check or event deduplication rejects the replay. 5. The server assigns a new local email ID and stores another copy. 6. The agent may process the duplicated message as a new event. ### Impact Assessment Replay can cause duplicate notifications, repeated processing, storage consumption, or repeated downstream actions. The practical severity depends on wheth ...[truncated 122 chars]
Remediation
View remediation

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:24
Finding

Untrusted Email Content Is Routed Into Autonomous Agent Processing

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:10
Finding

Full-Permission Resend Credential Exceeds Runtime Least Privilege

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The code is narrowly focused on inbound email receipt and local inbox management. It supports part of the declared description—receiving emails via Resend webhook and storing them—but it does not perform the broader setup/configuration tasks claimed in the description. There is no code to provision an email identity, configure outbound sending, register webhook endpoints with Resend, or autonomously process verification flows. This is a material description-behavior mismatch because the declared purpose presents a fuller email setup/automation capability than the code actually provides.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 63)May include surrounding context.

md
Create `routes/inboundEmail.js` in your Express backend (see `references/inboundEmail.js` for full implementation). Key requirements:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill clearly expects access to environment secrets and external network connectivity, but it declares no explicit tool scope or permission boundaries. That creates a governance gap where an agent may use broader capabilities than a reviewer expects, especially given the skill handles API keys, webhook setup, and inbox operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill promotes autonomous inbox monitoring, reading, summarization, and reporting of received emails without clear warnings about privacy, consent, retention, or sensitive-data handling. Because email may contain credentials, personal data, and verification links, silent automated processing increases the risk of over-collection, unintended disclosure, and policy violations.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 41)May include surrounding context.

md
RESEND_KEY="re_xxxxx"

# Add domain
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/domains \
  -d '{"name": "yourdomain.com", "region": "us-east-1"}'

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 43)May include surrounding context.

md
# Add domain
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/domains \
  -d '{"name": "yourdomain.com", "region": "us-east-1"}'

# Get DNS records to configure

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 48)May include surrounding context.

md
# Add domain
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/domains \
  -d '{"name": "yourdomain.com", "region": "us-east-1"}'

# Get DNS records to configure

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 56)May include surrounding context.

md
# Add domain
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/domains \
  -d '{"name": "yourdomain.com", "region": "us-east-1"}'

# Get DNS records to configure

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 82)May include surrounding context.

md
# Add domain
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/domains \
  -d '{"name": "yourdomain.com", "region": "us-east-1"}'

# Get DNS records to configure

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 127)May include surrounding context.

md
# Add domain
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/domains \
  -d '{"name": "yourdomain.com", "region": "us-east-1"}'

# Get DNS records to configure

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 150)May include surrounding context.

md
# Add domain
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/domains \
  -d '{"name": "yourdomain.com", "region": "us-east-1"}'

# Get DNS records to configure

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 80)May include surrounding context.

Step 3: Register Webhook on Resend

bash
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/webhooks \
  -d '{

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

The cron-based design creates persistent automated processing of inbox contents and marks messages as processed over time, which establishes durable state and recurring access to potentially sensitive email. Without retention, access control, and audit guidance, this can cause unnoticed long-term collection and handling of confidential data.

Content

Scanner excerpt · SKILL.md (reported line 113)May include surrounding context.

Step 6: Set Up Inbox Monitoring Cron

Create a cron job that checks for new emails every 5 minutes and notifies the agent:

text
Every 5 min → Check mail/inbox/ for new .json files

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 125)May include surrounding context.

Send a test email:

bash
curl -s -X POST -H "Authorization: Bearer $RESEND_KEY" \
  -H "Content-Type: application/json" \
  https://api.resend.com/emails \
  -d '{

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 150)May include surrounding context.

js
async function sendEmail(to, subject, text, html) {
  const res = await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${RESEND_API_KEY}`,

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · SKILL.md (reported line 172)May include surrounding context.

js
async function getInbox() {
  const res = await fetch('http://localhost:PORT/api/inbound-email');
  const data = await res.json();
  return data.emails;
}

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
references/directorMail.js:20