Back to skill

Security audit

amazon-shopper

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly transparent and purpose-aligned, but its optional off-Amazon research path can fetch arbitrary HTTPS search-result URLs without the same public-network guard used elsewhere.

Install only if you are comfortable with its network and local subprocess behavior. Keep AMAZON_SHOPPER_ALLOW_WEB_RESEARCH unset unless you accept third-party lookups, and prefer running it in a constrained environment with a trusted PATH and no access to sensitive internal network services. Runtime tests could not be run here because the available Node version was v20.20.0 and the package expects Node 22.5+ with node:sqlite.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch.mjs:62
Finding

Opt-in web research can issue requests to private or internal network destinations

Content
View full analysis
= 300 && status < 400 && location) { return { status, headers: res.headers, body: "", redirectTo: new URL(location, url).toString() }; } const body = await res.text(); return { status, headers: res.headers, body }; } async function fetchFollowing(url, maxHops = 3) { let current = url; for (let hop = 0; hop <= maxHops; hop++) { const r = await rawGet(current); if (!r.redirectTo) return { ...r, finalUrl: current }; current = r.redirectTo; // Each hop is a fresh request, so it observes the same pacing. await sleep(intervalMs); } throw new Error(`too many redirects starting at ...[truncated 3938 chars]
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Warning
Location
scripts/llm.mjs:20
Finding

Allowlisted subprocesses can be replaced through inherited PATH resolution

Content
View full analysis
{ const child = spawn(cmd, args, { stdio: ["pipe", "pipe", "pipe"] }); let out = "", err = ""; child.stdout.on("data", d => out += d.toString()); child.stderr.on("data", d => err += d.toString()); child.on("error", reject); child.on("close", code => { if (code === 0) resolve(out); else reject(new Error(`${cmd} exited ${code}: ${err.slice(0, 500)}`)); }); if (stdinPayload) child.stdin.write(stdinPayload); child.stdin.end(); }); } ``` The ImageMagick path additionally trusts a PATH-resolved `which` command and then executes the returned path: ```js const ALLOWED_CONVERT_BINS = ["magick", "convert"]; let resolvedBin; function resolveConvertBin() { if (resolvedBin !== undefined) return resolvedBin; resolvedBin = null; for (const bin of ALLOWED_CONVERT_BINS) { // A bare name only: `which` resolves it from PATH, and a name containing a // path separ ...[truncated 3735 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (70)

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

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

md
---
name: amazon-shopper
version: 1.2.1
description: "Amazon shopping that ends in a decision, not a page of links. It sweeps a dozen query phrasings instead of trusting one keyword, decodes the spec markings a listing hides (memory-card A2/V30 classes, active-ingredient concentration), and ranks on the metric that actually decides the buy — €/kg of active, €/GB, €/kg of protein — so the cheapest sticker price stops winning by default. Availability is a hard gate: a listing that still renders is not a listing you can buy. It reads amazon.es ANONYMOUSLY — there is no login, no cookie capture, no browser tab to share and n

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| The `new Function` metric evaluator in `rank.mjs` | Compiled a `formula_js` string into a function and ran it per product row | Nothing. It only ever compiled

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
the anonymous Node fetcher, with no subprocess and no `--session` flag. `tests/package-surface.test.mjs`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
the anonymous Node fetcher, with no subprocess and no `--session` flag. `tests/package-surface.test.mjs`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Removed. The test seam is a JSON fixture map that is parsed, never executed | `scripts/shopper.mjs` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Removed. The test seam is a JSON fixture map that is parsed, never executed | `scripts/shopper.mjs` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Removed. The test seam is a JSON fixture map that is parsed, never executed | `scripts/shopper.mjs` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
horization: Bearer`, and the token is scrubbed from any reflected error body | `scripts/apify.mjs` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
horization: Bearer`, and the token is scrubbed from any reflected error body | `scripts/apify.mjs` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
SHOPPER_ALLOW_WEB_RESEARCH=1`, and it runs on a separate cookie-free fetcher | `scripts/research.mjs` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
n the code had only read a dataset field | Wording matches what was verified | `scripts/sdcard-chart.mjs` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
n the code had only read a dataset field | Wording matches what was verified | `scripts/sdcard-chart.mjs` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
2. **Brand table** (`scripts/brand-concentrations.mjs`) — known brands (CTX, Bayrol, AstralPool,

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
2. **Brand table** (`scripts/brand-concentrations.mjs`) — known brands (CTX, Bayrol, AstralPool,

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- **Max 2 qualifying questions per task** — enforced in `categorize.mjs`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
`scripts/sdcard-spec.mjs` decodes the markings (`A1/A2`, `U1/U3`, `V10…V90`, `C10`, bus, pack count)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
When all three are present, `start` and `rank` route through `scripts/creators-api.mjs` instead of HTML

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node --experimental-sqlite scripts/dev-capture-fixtures.mjs

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The manifest states the skill 'searches amazon.es and nothing else' and that earlier classifieds scrapers are gone. However, this base adapter describes a generic per-store abstraction and explicitly cites non-Amazon identifiers such as 'milanuncios id', showing the codebase is designed to support other stores/sources beyond Amazon.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code can send user-derived shopping queries and brand information to third-party sites (producer domains and DuckDuckGo) when opt-in is enabled, despite the skill metadata claiming it searches only amazon.es. This creates a scope-expansion and privacy/transparency problem: users or host systems may rely on the manifest to make trust decisions, but the implementation can disclose shopping intent off-platform.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code path explicitly resolves any adapter name passed via --store and then uses adapter.search()/adapter.detailFetch() for non-Amazon stores, contradicting the skill metadata claim that it searches amazon.es and nothing else. This is dangerous because users and upstream agents may rely on the declared data-scope boundary for privacy, compliance, or policy reasons, but the implementation permits access to arbitrary registered stores and their network behavior.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · tests/cli.test.mjs (reported line 15)May include surrounding context.

js
function runCli(args, env = {}) {
  const r = spawnSync("node", ["--experimental-sqlite", SHOPPER, ...args], {
    env: { ...process.env, ...env },
    encoding: "utf8",
  });
  return { code: r.status, stdout: r.stdout, stderr: r.stderr };

Obfuscated Code

High
Category
Supply Chain
Confidence
50% confidence
Finding

Code contains obfuscation (base64, hex encoding with execution). This is often used to hide malicious functionality.

Content

Scanner excerpt · tests/fixtures/BLOCKED-search-1779809435497.html (reported line 15)May include surrounding context.

html
<script type="text/javascript">
    window.awsWafCookieDomainList = [];
    window.gokuProps = {
"key":"AQIDAHjcYu/GjX+QlghicBgQ/7bFaQZ+m5FKCMDnO+vTbNg96AHPa6bn+5rzsd4+1fQjaoabAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMmJz4/5YeXMT5Fmo4AgEQgDthZ5K+p1YxskARFVn/0mUMur4oZzZNu1enq1RdNf4USptF83P6RJM+ehGAHyG/Z+SYtoE05balFrPY1g==",
          "iv":"D545dwDpqgAAAD2s",
          "context":"nmg+tz3zmoH1RwyRBA9WsJZc+02UbvWXbpOlkcV4eVZteTu2XjYon4x40j5sBg6zYyNNIghas/Z8wOfrCJEygFiaoCALPe8M429LfHemrwa7o5sRt/CavM22lFRnpNoFB42LV/OyXxd+Q4x4o6ajieeJm5L7/4mRJcmFrfZlhRmr6WnK8m0IIH08uYcB6i3eno4wBSoflSSDzSI4+v34UX5GdoNpUA4oxu/3XfKJBP3xBpeQ/hjjfJE2sIkxOvu+IJbKHOa22otuH6DebiHuZIH3ql7yvSBrL5voZzq7wsJKfy+kDaXgjo/9ZxuHilb7Ci5y3fmC1SzMmt+wA+xQug2g67bab0cr4R3VJvzWn5UW7brkYcWK5X6uQqE/33tDsBvrSTp5"
};

Obfuscated Code

High
Category
Supply Chain
Confidence
50% confidence
Finding

Code contains obfuscation (base64, hex encoding with execution). This is often used to hide malicious functionality.

Content

Scanner excerpt · tests/fixtures/BLOCKED-search-1779809435497.html (reported line 17)May include surrounding context.

html
window.gokuProps = {
"key":"AQIDAHjcYu/GjX+QlghicBgQ/7bFaQZ+m5FKCMDnO+vTbNg96AHPa6bn+5rzsd4+1fQjaoabAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMmJz4/5YeXMT5Fmo4AgEQgDthZ5K+p1YxskARFVn/0mUMur4oZzZNu1enq1RdNf4USptF83P6RJM+ehGAHyG/Z+SYtoE05balFrPY1g==",
          "iv":"D545dwDpqgAAAD2s",
          "context":"nmg+tz3zmoH1RwyRBA9WsJZc+02UbvWXbpOlkcV4eVZteTu2XjYon4x40j5sBg6zYyNNIghas/Z8wOfrCJEygFiaoCALPe8M429LfHemrwa7o5sRt/CavM22lFRnpNoFB42LV/OyXxd+Q4x4o6ajieeJm5L7/4mRJcmFrfZlhRmr6WnK8m0IIH08uYcB6i3eno4wBSoflSSDzSI4+v34UX5GdoNpUA4oxu/3XfKJBP3xBpeQ/hjjfJE2sIkxOvu+IJbKHOa22otuH6DebiHuZIH3ql7yvSBrL5voZzq7wsJKfy+kDaXgjo/9ZxuHilb7Ci5y3fmC1SzMmt+wA+xQug2g67bab0cr4R3VJvzWn5UW7brkYcWK5X6uQqE/33tDsBvrSTp5"
};
    </script>
    <script src="https://1c5c1ecf7303.7a63328c.eu-central-1.token.awswaf.com/1c5c1ecf7303/be01910c327c/6c185aecd8cc/challenge.js"></script>

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · tests/fixtures/search-microsd-en.html (reported line 9)May include surrounding context.

html
<div data-component-type="s-impression-counter" data-component-props="{&quot;presenceCounterName&quot;:&quot;sp_delivered&quot;,&quot;hiddenCounterName&quot;:&quot;sp_hidden&quot;,&quot;testElementSelector&quot;:&quot;.s-image&quot;}" class="rush-component s-featured-result-item s-expand-height" data-component-id="119">
    <span class="a-declarative" data-version-id="vu7iu4fntu1xk260xz2x41rg0u" data-render-id="r3snxz1ymkuzv2v2nx27vmihon" data-action="puis-card-container-declarative" data-csa-c-func-deps="aui-da-puis-card-container-declarative" data-csa-c-item-id="amzn1.asin.B07N73LB4T" data-csa-c-posx="2" data-csa-c-type="item" data-csa-c-owner="puis" data-csa-c-id="gycboz-v24eo2-ozyopc-krvc7i"><div class="puis-card-container puis-overflow-hidden s-card-container aok-relative puis-expand-height puis-include-content-margin puis puis-vu7iu4fntu1xk260xz2x41rg0u s-latency-cf-section puis-card-border" data-cy="asin-faceout-container"><div class="a-section a-spacing-base desktop-grid-content-view"><div class="s-product-image-container aok-relative s-text-center s-image-overlay-grey puis-image-overlay-grey s-padding-left-small s-padding-right-small puis-spacing-small s-height-equalized puis puis-vu7iu4fntu1xk260xz2x41rg0u" data-cy="image-container" style="padding-top: 24px !important;"><span data-component-type="s-product-image" class="rush-component" data-version-id="vu7iu4fntu1xk260xz2x41rg0u" data-render-id="r3snxz1ymkuzv2v2nx27vmihon"><a aria-hidden="true" class="a-link-normal s-no-outline" tabindex="-1" href="/-/en/sspa/click?ie=UTF8&amp;spc=MTo4MjE0NjQ0NDE0Njk2MTc0OjE3ODYwNDc3NjM6c3BfYXRmOjIwMDUzOTc2ODc2MjA0OjowOjo&amp;url=%2FGIGASTONE-32GB-Compatible-Nintendo-Recording%2Fdp%2FB07N73LB4T%2Fref%3Dsr_1_2_sspa%3Fcrid%3DTJM9L2LHIARJ%26dd%3DlSPGTPQNXLEBhnkQQjAudg%252C%252C%26dib%3DeyJ2IjoiMSJ9.n4R-Dt4oBYQRkM6m0S8mH9KrxROsgLOaK-SjKfwLAqTK-6tClumI9JTaISPI1v2MRD7DPrQj-dSfF8KOEd1CfaWPsTMVxHGvm4kDB3hxB1YP9qpmsnK1U98R2_Dv3NwR-uIVmvqSOiTSJRX6GFxBUYjVM3SpUMlbGESwXJ0s47dxpdkBtwMx
...[truncated 27 chars]