Back to skill

Security audit

Marktplaats

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for Marktplaats ad publishing, but its probe tool can read authenticated browser forms and forward user-supplied cookies or headers to arbitrary URLs without enough guardrails.

Review before installing. Use this only if you are comfortable with a local agent using your logged-in Safari Marktplaats session and writing ad snapshots/registers. Do not pass cookies, cookie files, Authorization headers, or non-Marktplaats URLs to the probe tool; keep snapshots private and inspect them for sensitive data before sharing logs.

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/marktplaats-place-probe.js:418
Finding

Arbitrary destination requests can forward cookies and attacker-controlled authentication headers

Content
View full analysis
{ const idx = value.indexOf(':'); if (idx === -1) return [value, '']; return [value.slice(0, idx).trim(), value.slice(idx + 1).trim()]; })), }); const cookie = options.cookie ?? loadCookieFile(options.cookieFile); if (cookie) { headers.set('Cookie', cookie); } const response = await fetch(options.url, { redirect: 'follow', headers, }); const body = await response.text(); const isLoginRedirect = /\/identity\/v2\/login\b/.test(response.url) || /Inloggen op uw account/i.test(body); const security = detectSecuritySignals(body, response.url); const bodyLimit = Number.isFinite(options.bodyLimit) ? options.bodyLimit : 0; const bodyPreview = bodyLimit === 0 ? '' : body.slice(0, bodyLimit); return { source: 'curl', url: response.url, status: response.status, ok: response.ok, isLoginRedirect, security, requestHeaders: { 'user-agent': userAgent, accept: headers.get('Accept'), }, headers: Object.fromEntries(response.headers.entries()), body: bodyPreview, bodyTruncated: bodyPreview.length < body.length, bodyLen ...[truncated 2932 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/marktplaats-place-probe.js:113
Finding

Denylist-based form redaction can save or print passwords and other sensitive browser fields

Content
View full analysis
({ name: input?.name ?? '', type: input?.type ?? '', value: redactValue(input?.name, input?.value ?? ''), checked: Boolean(input?.checked), disabled: Boolean(input?.disabled), options: Array.isArray(input?.options) ? input.options.map((option) => ({ value: option?.value ?? '', text: option?.text ?? '', selected: Boolean(option?.selected), })) : undefined, })); } ``` The Safari-side collector reads every form control value: ```js const collectInputs = (root) => Array.from(root.querySelectorAll('input, textarea, select')).map((el) => { const record = { name: el.name || '', type: el.type || el.tagName.toLowerCase(), value: 'value' in el ? el.value : '', checked: Boolean(el.checked), disabled: Boolean(el.disabled), }; if (el.tagName === 'SELECT') { record.options = Array.from(el.options).map((opt) => ({ value: opt.value, text: opt.text, selected: Boolean(opt.selected), })); } return record; }); ``` The normalized result may then be written and printed: ```js if (args.save) { writeFileSync(resolve(args.save), `${JSON.stringify(result, null, 2)}\n`); } if (args.json || args.save) { console.log(JSON.stringify(result, null, 2)); return; } ``` ...[truncated 2880 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/api.js:329
Finding

Listing detail fetch accepts arbitrary HTTP URLs and can probe local or private-network services

Content
View full analysis
} Detailed information parsed from the listing page. */ async function getListingDetails(urlOrPath) { const fullUrl = urlOrPath.startsWith('http') ? urlOrPath : `https://www.marktplaats.nl${urlOrPath}`; const response = await fetch(fullUrl, { headers: { ...DEFAULT_HEADERS, Accept: 'text/html', }, }); if (!response.ok) { throw new Error(`Listing page fetch failed with HTTP ${response.status}: ${response.statusText}`); } ``` ### Technical Analysis Any string beginning with `http` is treated as a complete destination. There is no URL parsing, protocol restriction, hostname allowlist, DNS/IP validation, or redirect validation. The function is reachable from the search CLI through `--details [target]`. Although it does not attach browser cookies, it runs from the user's machine and can therefore contact services unavailable to a remote attacker, including loopback and private-network endpoints. Successful responses are parsed for metadata and JSON-LD, and the returned object includes structured data and response length. Errors expose HTTP status information. This provides a network and content oracle even where the response is not returned in full. This functionality is unnecessary for the declared purpose because listing details should only be retrieved from approved Marktplaats hosts. ### Attack Path 1. An attacker persuades the agent to use a crafted `--details` target while running `marktplaats-search`. 2. The target is an HTTP URL pointing to a loopback service, private-network host, or other sensitive endpoint reachable from the us ...[truncated 967 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose centers on advertisement workflow actions: preparing ads, placing/editing listings, QA, preflight, live verification, and register maintenance. The actual code does none of those tasks. Instead, it implements a command-line categories explorer that calls fetchCategories(), validates an optional numeric category ID, and prints categories and filter facets or raw JSON. This is a materially different primary purpose and introduces undeclared capability around category/search metadata exploration. No evidence in this chunk supports ad creation, editing, QA, verification, or register functions.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description centers on managing one's own Marktplaats advertisements: preparing ads, placing/editing them, copy QA, preflight checks, live verification, and maintaining a register. The supplied code does not implement ad creation, editing, QA, publishing, verification, or registry features. Instead, it parses CLI arguments for a search query, calls searchListings, prints matching marketplace listings, optionally fetches details for a selected listing via getListingDetails, and can dump raw JSON. This is a materially different primary purpose and includes undeclared capabilities related to searching and inspecting marketplace listings.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose centers on advertisement preparation and lifecycle actions: creating/posting ads, editing them, QA/preflight, live verification, and maintaining a register. The supplied code does none of the posting or editing workflow. Instead, it is a search and retrieval library that queries the Marktplaats search API, normalizes search results, fetches category data, and scrapes listing pages for details like description, price, and images. While 'live verification' could loosely relate to fetching listing details, that is only a small subset of the declared scope and not the primary behavior. The main functional mismatch is that the code is read-only market data retrieval, whereas the description claims ad management and QA capabilities.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The provided code does not implement the declared end-to-end Marktplaats ad preparation and management workflow. It merely exports APIs whose names indicate read-oriented capabilities such as searching listings, fetching categories, retrieving listing details, and getting attribute/filter metadata. Based on this chunk, there is no visible functionality for placing or editing advertisements, QA/preflight checks, live verification, or maintaining a register. While this may be only a partial file, the behavior shown is materially narrower and different from the declared purpose.

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

md
node ./scripts/marktplaats-copy-qa.js --self-test

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node ./scripts/marktplaats-ad-preflight.js --self-test

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node ./scripts/marktplaats-live-verify.js --self-test

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node ./scripts/marktplaats-register-update.js --self-test

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill declares no explicit tool scope while its instructions clearly require shell execution and network/browser access. In an agent environment, missing permissions metadata weakens policy enforcement and can let the skill run with broader capabilities than reviewers or orchestrators expect.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instruction 'Schrijf een feitelijke Nederlandse tekst' mandates Dutch-language content. Under the policy criteria, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly justified as region-specific; this file does not clearly provide that justification or opt-in path.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This markdown file presents all operational instructions in Dutch and does not indicate that the user can choose another language. That can violate a language/locale policy when skills are expected to avoid forcing a specific language without explicit opt-in or documented justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code hard-codes 'nl-NL' in text normalization for all inputs, which enforces a specific locale behavior. This is a natural-language policy concern because the script does not offer user opt-in or configuration for other languages/locales, and no justification is documented in the file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The Safari browser-fetch path performs same-origin requests with the browser's authenticated session (withCredentials = true) and is explicitly designed to inspect logged-in Marktplaats pages. Although this appears operational rather than malicious, it silently leverages ambient browser authentication and can expose account-scoped page contents, form metadata, and security state without any user-facing disclosure or consent prompt.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Curl mode accepts raw cookies from --cookie or --cookie-file and transmits them directly as a Cookie header to an arbitrary --url. This creates a credential-handling and exfiltration risk: a user or higher-level agent can accidentally send active session cookies to the wrong endpoint, particularly because there is no host validation, warning, or redaction at transmission time.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file implements search and scraping-style retrieval of marketplace data, which materially diverges from the declared publisher-oriented skill purpose. This kind of capability mismatch is dangerous because it can mislead reviewers and users about what the skill actually does, expanding data-access behavior beyond expected scope and potentially enabling unintended collection of listing content from arbitrary URLs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest description is written entirely in Dutch: "Maak en controleer Marktplaats-advertenties...". For a general package manifest, this can amount to a language/locale constraint without any explicit opt-in or statement that the skill is intended only for Dutch-speaking or Netherlands-specific users.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The script can save probe output to disk, and while some form fields are redacted in normalized snapshots, the saved data still includes sensitive page metadata such as URLs, category selections, request/response headers, challenge status, and potentially response body previews in curl mode. On shared systems or in agent workflows, these artifacts can persist longer than intended and leak account-related or operational details.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The module docstring explicitly describes a search and detail-fetch client, which contradicts the publisher-focused manifest and reinforces that the code's actual behavior is outside declared scope. While not directly exploitable on its own, deceptive or inaccurate documentation increases the risk of unsafe deployment by obscuring real network and data-handling functionality.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.