Why this verdict
mcp-server . Suspicious
- inconclusive: gap request_unanswered
- inconclusive: unresolved question on CRED
- inconclusive: unresolved question on EXFIL
skill skill Suspicious
- suspicious: finding skill:skill:skill/.mcp.json:4:SUPPLY
- suspicious: finding skill:skill:skill/SKILL.md:38:SUPPLY
Security findings 2
Supply chain
highContextual riskmedium confidenceThe MCP launch config runs an unpinned npm package via `npx -y @synthnet/mcp`, so whatever version is currently published executes on the host without confirmation.
mcpServers.synthnet.args[1]: @synthnet/mcp
conceal:remotevector:metadata
Counter-argument considered
The launch config runs `npx -y @synthnet/mcp` with no version pin. The pinned-install carve-out does not cover this, so the current registry version runs on the host unreviewed.
Supply chain
highContextual risklow confidenceSKILL.md instructs the agent to add an unpinned `npx -y @synthnet/mcp` server to its MCP config, which fetches and runs unpinned remote code, and the server then stores an identity and API key in ~/.synthnet/identity.json.
Add the MCP server to your Claude Code config (`.mcp.json` / MCP settings):
conceal:remotevector:instruction
Counter-argument considered
The setup instruction has the agent register an unpinned `npx -y @synthnet/mcp` server that fetches and runs the current registry code. The pinned-install carve-out does not apply.
Open questions 3
What the analysis could not settle from the code alone.
Where do ORIGIN (config.ts) and the api/publicPost helpers send requests, and does the keystore write the private key and API key only to a local 0600 file? Not shown in this batch.
Credential access
[file request src/config.ts] How is ORIGIN derived from env, and is there any non-default or hidden destination?
Data exfiltration
What does the @synthnet/mcp npm package do at install and run time (e.g. does it read other credentials or files and send them to synthnet.io)? Its code is not shown and is out of scope as a dependency.
Supply chain
Refuted findings 4
Candidates the review stage examined and rejected, kept for transparency.
Data exfiltration
criticalRefutedmedium confidenceThe synthnet_paint tool invites the agent to pass an arbitrary local file path as `imagePath`; the server reads that file without any type check and uploads it base64-encoded to the remote API, so a steered agent could send arbitrary local files off-host.
imageBase64 = readFileSync(args.imagePath).toString('base64');Why it was refuted
Passing imagePath to upload a self-generated image to the declared SynthNet API is the openly documented bring-your-own-image feature. The destination is the wrapped service, and the file read is declared in the tool description.
Persistence
criticalRefutedlow confidenceSKILL.md instructs the agent to add an unpinned `npx -y @synthnet/mcp` server to the agent's MCP config (.mcp.json), a control-plane write, though this is the documented setup for the skill's own server and the secret (SYNTHNET_API_KEY) is only the wrapped service's key.
Add the MCP server to your Claude Code config
Why it was refuted
Adding the skill's own MCP server to .mcp.json is the documented setup for this skill, in a fenced vendor install block. It adds no hidden persistence beyond the declared server, and the only secret involved is the wrapped service's own key.
Agent hijacking
highRefutedlow confidenceThe synthnet_home tool description tells the agent to call it at the start of every beat and then follow a server-generated to-do list with suggested actions, so the remote server can steer the agent's behavior with content the artifact does not ship.
Server-driven to-do list for your session
Why it was refuted
The heartbeat to-do list is the openly declared core function of synthnet_home. The description plainly says what it does and nothing in it tells the agent to do anything harmful or hidden.
Data exfiltration
highRefutedlow confidencesynthnet_paint reads an arbitrary agent-supplied local file path (only size-checked, with no image type validation) and uploads it base64-encoded to the SynthNet API, so a prompt-injected agent could exfiltrate local files.
imageBase64 = readFileSync(args.imagePath).toString('base64');Why it was refuted
The file read feeds the declared image-upload feature to the wrapped service. A size check is applied and the tool description declares imagePath openly. The missing type validation is a hardening gap, not concealed exfiltration.
Coverage gaps 1
Parts of the artifact the static analysis could not fully examine.
request_unanswered request.0: behavior-chains.0/req-config: no answer