Back to skill

Security audit

Competitor Change Monitor

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it needs review because it fetches user-configured websites and stores reports without enough validation or disclosure.

Review this skill before installing in a sensitive environment. Use only trusted competitor configurations, restrict allowed domains, run it away from internal networks, sanitize competitor names, cap response sizes, and review any report before sending it to Slack, email, or a custom webhook. Do not enable cron-style continuous monitoring until legal, privacy, and data-retention expectations are clear.

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

Error
Location
scripts/monitor.js:23
Finding

Configuration-Controlled Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/monitor.js, lines 23-30 and 64-67
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: High

Vulnerable Code

javascript
async function fetchWebsite(url) {
  return new Promise((resolve, reject) => {
    https.get(url, { timeout: 10000 }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => resolve(data));
    }).on('error', reject);
  });
}

The unvalidated URL is passed to this function from the configuration:

javascript
async function createSnapshot(competitor) {
  try {
    console.log(`📸 Snapshotting ${competitor.name} (${competitor.url})...`);
    const html = await fetchWebsite(competitor.url);
    const content = extractContent(html);

Technical Analysis

The application reads competitor URLs from competitors.json and supplies them directly to https.get. It does not validate the destination hostname, port, resolved IP address, or network range.

Requiring HTTPS does not prevent SSRF. An attacker who can create or modify the configuration can target HTTPS services bound to loopback addresses, private networks, or other infrastructure reachable from the monitoring host. The fetched response is processed to extract titles, headings, prices, content length, and a content hash, after which those values are written to snapshots and reports.

Attack Path

  1. An attacker obtains the ability to create or modify the configuration supplied to the monitor.
  2. The attacker adds a competitor whose URL points to an internal HTTPS endpoint, such as a loopback or private-network service.
  3. A user or scheduled cron task executes node scripts/monitor.js monitor.
  4. The process requests the internal endpoint using the monitoring host's network access.
  5. Extracted response information is saved under snapshots/ and may subsequently appear in generated ...[truncated 696 chars]
Remediation
View remediation

Remediation Suggestions

  • Parse each URL with the standard URL class and reject malformed URLs.
  • Permit only https: and approved destination ports.
  • Require an explicit allowlist of competitor domains where feasible.
  • Resolve hostnames before making requests and reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges.
  • Protect against DNS rebinding by verifying the address actually used for the connection, not only an earlier DNS lookup.
  • If redirect support is later added, apply the same validation to every redirect target.
  • Run the monitor in a restricted environment without access to sensitive internal networks.
  • Validate the configuration against a strict schema before monitoring begins.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/monitor.js:79
Finding

Path Traversal Through Unsanitized Competitor Names

Content
View full analysis

Vulnerability Details

File Location: scripts/monitor.js, lines 79-80
Vulnerability Type: Path Traversal and Arbitrary File Creation
Risk Level: Medium

Vulnerable Code

javascript
// Save snapshot
const filename = path.join(SNAPSHOTS_DIR, `${competitor.name.toLowerCase()}-${Date.now()}.json`);
fs.writeFileSync(filename, JSON.stringify(snapshot, null, 2));

Technical Analysis

The competitor's display name is incorporated directly into a filesystem path. path.join normalizes traversal components but does not enforce that the result remains beneath SNAPSHOTS_DIR.

A name containing path separators and .. components can therefore cause the final path to escape the intended snapshots/ directory. The operation can create a JSON file anywhere the process has write access, provided the destination's parent directory already exists.

The generated timestamp and .json suffix reduce the attacker's control over the final filename and generally prevent direct replacement of a specifically named existing file. They do not prevent unauthorized file creation outside the snapshot directory.

Attack Path

  1. An attacker supplies or modifies competitors.json.
  2. The attacker sets a competitor name to a traversal value such as ../reports/injected.
  3. The monitor converts the name to lowercase and appends a timestamp and .json.
  4. path.join('./snapshots', '../reports/injected-<timestamp>.json') resolves outside snapshots/.
  5. fs.writeFileSync creates the file in the traversed location if the process can write there and the parent directory exists.

Impact Assessment

Exploitation permits timestamped JSON file creation outside the designated snapshot directory with the privileges of the Node.js process. This can pollute application directories, interfere with software that automatically consumes JSON files, consume disk space, or place attacker-influenced snapshot content in ...[truncated 238 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not derive storage paths from an untrusted display name.
  • Assign each competitor a validated internal identifier and use that identifier as the filename.
  • If names must be used, convert them to a strict slug containing only characters such as a-z, 0-9, _, and -.
  • Reject names containing path separators, traversal components, control characters, or empty normalized values.
  • Resolve both the base directory and final filename with path.resolve.
  • Verify that the final path starts with the resolved snapshot directory followed by the platform path separator before writing.
  • Open files with exclusive-creation semantics where appropriate to prevent accidental replacement.
  • Apply a strict JSON schema to configuration fields, including maximum name lengths.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/monitor.js:23
Finding

Unbounded HTTP Response Buffering Enables Memory Exhaustion

Content
View full analysis

Vulnerability Details

File Location: scripts/monitor.js, lines 23-30
Vulnerability Type: Resource Exhaustion and Denial of Service
Risk Level: Medium

Vulnerable Code

javascript
async function fetchWebsite(url) {
  return new Promise((resolve, reject) => {
    https.get(url, { timeout: 10000 }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => resolve(data));
    }).on('error', reject);
  });
}

Technical Analysis

The downloader concatenates every response chunk into a single in-memory string without enforcing a maximum response size. It also does not validate the HTTP status or content type before buffering the body.

A malicious or compromised monitored server can return an extremely large response, causing increasing memory consumption. String concatenation and subsequent regular-expression processing can require additional allocations, increasing the likelihood that the Node.js process will terminate due to memory exhaustion.

The configured request timeout is not a response-size limit and does not make buffering an arbitrarily large body safe.

Attack Path

  1. An attacker controls a domain present in the monitoring configuration, compromises an existing monitored server, or convinces a user to monitor an attacker-controlled HTTPS endpoint.
  2. The endpoint returns a very large response body.
  3. The monitor appends every chunk to the data string without enforcing a byte limit.
  4. Memory usage grows while the body is buffered.
  5. The completed body is passed through several regular expressions and hashing operations, causing further processing and allocation.
  6. The process becomes unresponsive or terminates from memory exhaustion; scheduled monitoring and report generation fail.

Impact Assessment

Exploitation can deny availability of the monitoring process and disrupt scheduled reports. If the monitor shar ...[truncated 257 chars]

Remediation
View remediation

Remediation Suggestions

  • Enforce a conservative maximum response size based on expected competitor page sizes.
  • Track received bytes and immediately destroy the request if the limit is exceeded.
  • Check Content-Length when present, while retaining streaming enforcement because the header can be absent or false.
  • Reject unexpected status codes and non-HTML content types before buffering.
  • Process responses as streams where practical rather than retaining the complete body.
  • Add explicit connection, inactivity, and total-operation timeouts and abort the request when they expire.
  • Limit the number and size of configured competitor entries.
  • Run the process with operating-system or container memory and CPU limits so one response cannot affect unrelated services.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

The core purpose mostly matches: the code does monitor competitor websites, track some content and pricing changes, and generate reports. It uses HTTPS requests, extracts headings and price-like strings, saves snapshots, and summarizes detected changes. However, the description explicitly says it will 'alert on significant changes,' and there is no alerting mechanism present (no email, webhook, notification, or trigger-based alert output beyond console logs/report files). Additionally, while snapshots are stored historically, the monitor action generates its report only from the snapshots created in that same run, so it does not reliably compare against prior stored snapshots for ongoing monitoring as the description implies. This is a partial but material description-to-behavior mismatch rather than a completely different purpose.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill describes continuous scraping, snapshot retention, LLM analysis, and email/Slack delivery, but does not warn users about the privacy, compliance, and data-handling implications of those actions. In practice, monitored pages may contain personal data, sensitive business information, or terms-of-service restrictions, and external delivery channels increase the risk of unintended disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This markdown file includes a concrete integration that posts report-latest.json to a Slack webhook, which is a network transmission of collected report data. The surrounding documentation does not warn users that report contents will be sent to a third-party service or may contain sensitive competitive intelligence.

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 · references/api-guide.md (reported line 127)May include surrounding context.

Slack integration

bash
# Send report to Slack channel
curl -X POST https://hooks.slack.com/services/YOUR/WEBHOOK/URL \
  -H 'Content-Type: application/json' \
  -d @report-latest.json

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The custom webhook sample uses fetch to send serialized change and competitor data to https://your-api.com/alerts. The guide does not disclose that enabling this integration transmits monitoring data off-system, which is the kind of privacy and data-handling behavior markdown documentation should warn about.

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 · references/api-guide.md (reported line 168)May include surrounding context.

md
// hooks/on-pricing-change.js
module.exports = async (change, competitor) => {
  // Send to your service
  await fetch('https://your-api.com/alerts', {
    method: 'POST',
    body: JSON.stringify({ change, competitor })
  });

Static analysis

No suspicious patterns detected.