Back to skill

Security audit

Local Lead Gen

Security checks for vulnerabilities and agentic risk

Overview

This skill openly performs local lead generation, but it can scrape contact emails and send cold outreach automatically with weak review and suppression controls.

Install only if you intentionally want an automated cold-outreach tool and are prepared to control it closely. Use dry-run first, review every recipient and message before sending, configure lawful unsubscribe and suppression handling outside this script, and avoid running it in an environment with access to sensitive internal network services.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bad-website-hunter.js:90
Finding

Unvalidated Remote URLs Enable Server-Side Request Forgery

Content
View full analysis
{ const u = new URL(url); https.get({ hostname: u.hostname, path: u.pathname + u.search, headers: { 'User-Agent': 'Mozilla/5.0', ...headers } }, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => resolve({ status: res.statusCode, data: d, headers: res.headers })); }).on('error', reject); }); } ``` ```js return (json.web?.results || []).map(r => ({ name: r.title, url: r.url, description: r.description })); ``` ```js async function scoreWebsite(url) { const score = { ssl: 0, mobile: 0, speed: 0, design: 0, content: 0, total: 0, issues: [] }; try { const start = Date.now(); const res = await httpGet(url); ``` ```js async function extractEmail(url) { try { // Try DeepCrawl first if available if (DEEPCRAWL_KEY) { const dcUrl = `https://api.deepcrawl.dev/read?url=${encodeURIComponent(url)}`; const res = await httpGet(dcUrl, { 'Authorization': 'Bearer ' + DEEPCRAWL_KEY }); const emailMatch = res.data.match(/[\w.+-]+@[\w-]+\.[\w.]+/g); if (emailMatch) return emailMatch[0]; // Try /contact page const contactUrl = url.replace(/\/$/, '') + '/contact'; const res2 = await httpGet(`https://api.deepcrawl.dev/read?url=${encodeURIComponent(contactUrl)}`, { 'Authorization': 'Bearer ' + DEEPCRAWL_KEY }); const emailMatch2 = res2.data.match(/[\w.+-]+@[\w-]+\.[\w.]+/g); if (emailMatch2) return emailMatch2[0]; } // Fallback: direct scrape const res = await httpGet(url); ``` ```js for (const biz of businesses) { results.scanned++; const score = await scoreWebsite(biz.url); ``` ### Technical Analysis ...[truncated 2120 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bad-website-hunter.js:45
Finding

Unbounded HTTP Response Buffering and Missing Timeouts Allow Denial of Service

Content
View full analysis
{ const u = new URL(url); https.get({ hostname: u.hostname, path: u.pathname + u.search, headers: { 'User-Agent': 'Mozilla/5.0', ...headers } }, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => resolve({ status: res.statusCode, data: d, headers: res.headers })); }).on('error', reject); }); } function httpPost(url, body, headers = {}) { return new Promise((resolve, reject) => { const u = new URL(url); const data = JSON.stringify(body); const req = https.request({ hostname: u.hostname, path: u.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': data.length, ...headers } }, res => { let d = ''; res.on('data', c => d += c); res.on('end', () => resolve({ status: res.statusCode, data: d })); }); req.on('error', reject); req.write(data); req.end(); }); } ``` ### Technical Analysis Both network helpers concatenate every response chunk into an in-memory string until the remote peer ends the response. Neither helper limits the number of bytes accepted. The requests also lack explicit connection, socket-idle, and total-operation deadlines. A remote server can consequently stream data indefinitely, return an extremely large body, or retain the connection without completing it. Because the main scanning loop awaits each request sequentially, one hostile destination can block the entire pipeline. ### Attack Path 1. An attacker causes an attacker-controlled site to appear in the business search results. 2. The scanner requests the site through `httpGet`. 3. The attacker either continuously streams response data, returns a ver ...[truncated 754 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bad-website-hunter.js:199
Finding

Configured Email Suppression List Is Not Enforced

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (11)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill explicitly automates scraping contact details, enriching lead data, and sending cold emails, yet it provides no guardrails around privacy law, consent, rate limits, robots/compliance, or external transmission of personal/business contact information to third-party services. In this context, the lack of warnings and approval gates is especially dangerous because the skill is designed to operationalize outreach at scale, increasing the likelihood of spam, privacy violations, and improper data sharing.

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 34)May include surrounding context.

md
node scripts/bad-website-hunter.js --niche "restaurants" --city "Austin TX" --limit 20

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/bad-website-hunter.js --niche "restaurants" --city "Austin TX" --limit 20

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill advertises use of environment-backed credentials and executable scripts, but it does not declare an explicit tool scope or permission boundary. That increases the chance an agent can invoke the skill with broader-than-necessary access to secrets or execution capabilities, undermining least-privilege controls.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The description is broad enough to trigger on generic prospecting, outreach, or business-development requests, which could cause an agent to run scraping and emailing workflows in contexts the user did not specifically intend. Because this skill can collect contact data and initiate outbound communication, overbroad routing materially raises the risk of unauthorized or surprising actions.

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 · scripts/bad-website-hunter.js (reported line 74)May include surrounding context.

js
// Step 1: Search businesses via Brave
async function searchBusinesses(niche, city, limit) {
  const query = encodeURIComponent(`${niche} ${city}`);
  const url = `https://api.search.brave.com/res/v1/web/search?q=${query}&count=${Math.min(limit, 20)}`;
  const res = await httpGet(url, { 'X-Subscription-Token': BRAVE_KEY, 'Accept': 'application/json' });
  const json = JSON.parse(res.data);
  return (json.web?.results || []).map(r => ({

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

When DEEPCRAWL_KEY is present, the script transmits target website URLs and contact-page URLs to a third-party crawling service without an explicit user prompt at the point of use. That is a genuine data-handling risk because it leaks user browsing/targeting activity and prospect lists to an external provider, which is more sensitive in a lead-generation tool that profiles businesses for outreach.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

This call sends a target URL to DeepCrawl, which exposes prospecting targets to a third party and expands the data-sharing surface beyond what is necessary for local processing. In context, the skill is built for lead harvesting and outreach, so silent enrichment through an external crawler increases privacy, confidentiality, and vendor-trust risk.

Content

Scanner excerpt · scripts/bad-website-hunter.js (reported line 136)May include surrounding context.

js
try {
    // Try DeepCrawl first if available
    if (DEEPCRAWL_KEY) {
      const dcUrl = `https://api.deepcrawl.dev/read?url=${encodeURIComponent(url)}`;
      const res = await httpGet(dcUrl, { 'Authorization': 'Bearer ' + DEEPCRAWL_KEY });
      const emailMatch = res.data.match(/[\w.+-]+@[\w-]+\.[\w.]+/g);
      if (emailMatch) return emailMatch[0];

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

The secondary DeepCrawl request transmits an inferred /contact page URL to a third party, further disclosing navigation and contact-discovery behavior about targets. This compounds the same privacy and operational exposure as the first call, especially because the tool is automating business profiling for unsolicited outreach.

Content

Scanner excerpt · scripts/bad-website-hunter.js (reported line 142)May include surrounding context.

js
if (emailMatch) return emailMatch[0];
      // Try /contact page
      const contactUrl = url.replace(/\/$/, '') + '/contact';
      const res2 = await httpGet(`https://api.deepcrawl.dev/read?url=${encodeURIComponent(contactUrl)}`,
        { 'Authorization': 'Bearer ' + DEEPCRAWL_KEY });
      const emailMatch2 = res2.data.match(/[\w.+-]+@[\w-]+\.[\w.]+/g);
      if (emailMatch2) return emailMatch2[0];

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script automatically sends cold outreach emails to addresses harvested from scanned websites, with no explicit confirmation step, compliance checks, or contextual warning at the point of execution. In this skill context, that creates real abuse and legal/compliance risk because running the tool can immediately contact third parties using scraped contact data.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

This request transmits scraped recipient email addresses and generated outreach content to Resend for delivery, enabling immediate unsolicited contact with third parties. In a lead-gen automation context, that is more dangerous than a generic email integration because it operationalizes harvested data into outbound messages without strong safety gates or review.

Content

Scanner excerpt · scripts/bad-website-hunter.js (reported line 172)May include surrounding context.

js
return true;
  }

  const res = await httpPost('https://api.resend.com/emails', {
    from: `${FROM_NAME} <${FROM_EMAIL}>`,
    to: [to],
    subject,

Static analysis

No suspicious patterns detected.