wonderwhy-er/ DesktopCommanderMCP
AgentThis is MCP server for Claude that gives it terminal control, file system search and diff file editing capabilities
Trust score
Scanned Oct. 22nd, 2026 at 51c7fbf against 34 rules · How scoring works
What pulled the score down
- HighTool Definition Integrity & Versioning (ETDI-style) ATR-M0001
- HighSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull) ATR-P0007
- HighSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull) ATR-P0007
- HighSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull) ATR-P0007
- HighStatic Secrets ATR-C0002
- MediumDoS Protections: Timeouts, Concurrency, Streaming Limits ATR-C0004
- MediumLogging Hygiene (No Secrets/PII/Tokens) & Audit Trail ATR-C0005
- MediumContainer/Runtime Hardening ATR-C0006
- HighDependency Risk & Supply Chain ATR-C0007
- HighTool Output Sanitization & Injection Containment ATR-C0013
At a glance
- Repository-wide8 of 15
- mcp-server (MCP server)2 of 11
- skills-plugins (skill)1 of 8
- skills-plugins/plugins/claude (skill)1 of 8
- skills-plugins/plugins/cursor (skill)1 of 8
What to fix first
13 issues, most severe first. Each one points to the exact code, with the recommended fix and a prompt you can paste into your coding agent.
- 1HighStatic SecretsRepository-wide · ATR-C0002The set_config_value tool handler (src/tools/config.ts) emits the entire unparsed argument object to stderr before parsing, and the MCP's audit-log sink (trackToolCall) persists raw tool arguments, including commands and file contents, without redaction. Although the historical Google-Analytics keys and gitleaks hits found in earlier commits are no longer present in the current HEAD, runtime-supplied credentials passed as tool arguments can still be echoed to stderr or written to the audit log in plaintext, which this rule's scope over logged/echoed secrets also covers.
Code not shown: this line may hold a secret.
Show 2 more locations
Code not shown: this line may hold a secret.
Code not shown: this line may hold a secret.
+8 more locations in the finding JSON
Recommended fixRemove or redact the raw-argument stderr echo in set_config_value's handler before logging, and route all tool-argument logging (including trackToolCall's audit log) through the existing redact() secret-scrubbing function so runtime-supplied credentials are never persisted or printed in plaintext.
- 2HighRuntime Command Injection via Process ExecutionDeclared behaviorRepository-wide · ATR-C0003Desktop Commander's declared, advertised purpose is to let the LLM run arbitrary shell commands (
start_process,interact_with_process) -- this is the product's core feature, so within_declared_contract=true. The repo does implement a real, non-trivial mitigation (CommandManager blocklist with chained/piped/subshell-aware parsing to resist blocklist bypass), so mitigation_present=true -- but it is a denylist of command *names*, not a sandbox or allowlist: any command name not on the configured blocklist runs with the full OS privileges of the MCP server process via a real spawned shell. This is reported as VULNERABLE per the rule's own evaluation method (a real source-to-sink process-execution path exists and the only gate is a bypassable denylist), while within_declared_contract and mitigation_present are both set true so the finding is weighted as a declared, partially-mitigated risk rather than an undisclosed defect.// Terminal tools case "start_process": result = await handlers.handleStartProcess(args); break;Show 2 more locations
const isAllowed = await commandManager.validateCommand(parsed.data.command); if (!isAllowed) { return { content: [{ type: "text", text: `Error: Command not allowed: ${parsed.data.command}` }], isError: true, structuredContent: { blocked: true, command: parsed.data.command }, }; } const commandToRun = parsed.data.command;const result = await terminalManager.executeCommand( commandToRun, parsed.data.timeout_ms, shellUsed, parsed.data.verbose_timing || false, maxWaitMs );+4 more locations in the finding JSON
Recommended fixThis is a fundamental, declared capability of the product (an agentic shell-access tool) rather than a defect to 'fix' outright; standard hardening options are: run inside a container/VM/sandbox with least-privilege OS permissions, default to a stricter allowlist mode for unattended/non-interactive use, keep the command blocklist updated and default-deny unknown commands in high-trust deployments, and ensure user-confirmation gating is enabled by default for risky operations (the project's own SKILL.md already recommends confirming high-risk commands with the user).
- 3HighDependency Risk & Supply ChainRepository-wide · ATR-C0007Trivy fs scan of package-lock.json identifies markdown-it@14.3.2 (a direct production dependency, imported by the file-preview/markdown rendering pipeline) as vulnerable to CVE-2026-111758 (GHSA-gfr2-rv44-r23c, HIGH, CVSS 7.5) -- an O(n^2) DoS via unclosed link reference definitions in default parsing options, fixed in 14.3.3/15.0.3 -- and this HIGH-severity DoS path remains reachable regardless of the renderer's configuration. A second, LOW-severity CVE (CVE-2026-111766) in the same package is HTML-markup-specific; because createMarkdownIt disables raw-HTML passthrough (html:false), that second CVE does not apply to this parser's actual usage path, so only the HIGH unclosed-reference-definition issue represents a live, unaddressed risk here. The project otherwise pins dependencies via package-lock.json and avoids postinstall curl|bash patterns, but the HIGH CVE remains unaddressed in the committed lockfile.
"node_modules/markdown-it": { "version": "14.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.2.tgz", "integrity": "sha512-sHHjZ5fJKlgrG4qns2YwVcdNep35h5fERrfkD2YNsb9UFk0UIHarbiTaHKVMlPuWAoiilyK8Fv/jAm11slsY7Q==",Show 2 more locations
// markdown-it is intentionally typed locally here to avoid maintaining ambient module declarations. import MarkdownIt from 'markdown-it';
"markdown-it": "^14.1.0",
+2 more locations in the finding JSON
Recommended fixRun
npm update markdown-it(or pin to >=14.3.3 / >=15.0.3) to remediate the HIGH unclosed-link-reference-definition DoS (CVE-2026-111758), which is reachable regardless of the html:false configuration, and commit the regenerated package-lock.json; add Trivy/npm auditto CI to catch future regressions. - 4HighTool Output Sanitization & Injection ContainmentRepository-wide · ATR-C0013Across every content-returning tool handler (read_file including isUrl=true fetched web content, read_process_output terminal/REPL output, start_search / get_more_search_results results, read_multiple_files), the MCP returns raw external text straight into the content:[{type:'text', text:<raw>}] array with no structural envelope, no stripping/escaping of control characters, ANSI escape sequences, zero-width/bidi Unicode, or fake role/system markers, and no untrustedReturn-equivalent annotation in the tool schemas/manifest. The repository supplies MCP protocol-typed results to a calling host, not a host LLM context-concatenation implementation itself, so this finding establishes that content returned by this MCP lacks any sanitization or untrusted-content signal before leaving the server -- it does not establish how, or whether, any particular downstream host concatenates that content into a model prompt.
let textContent = typeof fileResult.content === 'string' ? fileResult.content : fileResult.content.toString('utf8'); const fileType = fileResult.metadata?.isDirectory ? 'directory' as const : resolvePreviewFileType(resolvedFilePath); ... return { content: [{ type: "text", text: textContent }],Show 2 more locations
export async function readFileFromUrl(url: string, svgAsImage = false): Promise<FileResult> { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), FILE_OPERATION_TIMEOUTS.URL_FETCH); try { const response = await fetch(url, { signal: controller.signal });When reading from the file system, only works within allowed directories. Can fetch content from URLs when isUrl parameter is set to true (URLs are always read in full regardless of offset/length).
Recommended fixWrap every tool's model-facing text payload in a host-enforced structural envelope (e.g. <tool_output name="read_file" src="...">...</tool_output>), strip/neutralize ANSI escapes, zero-width/bidi Unicode, and fake role markers before returning content, and add an untrustedReturn:true (or equivalent) annotation to read_file (especially isUrl mode), start_search, and read_process_output so that any host consuming this MCP's output can apply stricter downstream handling.
- 5HighTool Definition Integrity & Versioning (ETDI-style)mcp-server (MCP server) · ATR-M0001Every shipped launch configuration for this server (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json) invokes it via
npx -y @wonderwhy-er/desktop-commander@latest. Tool definitions (names/descriptions/schemas) are compiled into the package and are not signed; because launch is unpinned, each MCP client start can silently fetch a newer release whose tool JSON differs from what the user last reviewed, with no hash/signature check and no re-approval step before the new definitions take effect.{ "mcpServers": { "desktop-commander": { "command": "npx", "args": ["-y", "@wonderwhy-er/desktop-commander@latest"] } } }Show 2 more locations
"mcpServers": { "desktop-commander": { "command": "npx", "args": ["-y", "@wonderwhy-er/desktop-commander@latest"] } }server.setRequestHandler(ListToolsRequestSchema, async () => { try { const showMcpUiPreviews = await shouldShowMcpUiPreviews(); const allTools = [ { name: "get_config",Recommended fixPin the launch config to an exact version (e.g. @wonderwhy-er/desktop-commander@0.3.0) instead of @latest, and adopt the ETDI pattern of signed tool manifests plus an explicit re-approval prompt on tool definition changes.
Show the other 8 issues
- 6HighSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)Declared behaviorskills-plugins (skill) · ATR-P0007The repo's shipped plugin/MCP manifests launch the Desktop Commander MCP server with an unpinned package reference: the root .mcp.json (also referenced by the root plugin.json), the Claude plugin manifest, and the Cursor plugin manifest all run
npx -y @wonderwhy-er/desktop-commander@latest, and gemini-extension.json omits any version at all. npx may reuse a cached resolution rather than always fetching fresh, but the launcher is still unpinned to an exact version or digest, so a later publisher-controlled or compromised publish can still be resolved and run without any re-consent from the user. No digest is recorded at approval time and no re-consent mechanism exists on change.{ "mcpServers": { "desktop-commander": { "command": "npx", "args": ["-y", "@wonderwhy-er/desktop-commander@latest"] } } }Show 2 more locations
"skills": "./skills/", "mcpServers": { "desktop-commander": { "command": "npx", "args": ["-y", "@wonderwhy-er/desktop-commander@latest"] } }"skills": "./skills/", "rules": "./rules/", "mcpServers": { "desktop-commander": { "command": "npx", "args": ["-y", "@wonderwhy-er/desktop-commander@latest"] } }+1 more location in the finding JSON
Recommended fixPin every shipped mcpServers launch entry (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact published version or digest (e.g. @wonderwhy-er/desktop-commander@0.3.0) instead of @latest or an unversioned reference, and bump the pin (with a changelog/re-consent prompt) on each intentional upgrade so a compromised or unexpected publish cannot silently change what every installed client launches.
- 7HighSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)skills-plugins/plugins/claude (skill) · ATR-P0007The plugin's own shipped launch entry starts the Desktop Commander MCP server via
npx -y @wonderwhy-er/desktop-commander@latest-- an unpinned package reference resolved at run time with no exact version or digest. This matches the rule's explicit positive indicator: an unpinned launch command the bundle ships for the host to run on every start, so each start runs whatever version the registry serves that day with no re-consent and no diff."mcpServers": { "desktop-commander": { "command": "npx", "args": ["-y", "@wonderwhy-er/desktop-commander@latest"] } }Recommended fixPin the launch command to an exact version or digest, e.g.
npx -y @wonderwhy-er/desktop-commander@0.3.0(matching the current release tag), and bump it deliberately with each reviewed release rather than floating on @latest. - 8HighSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)Declared behaviorskills-plugins/plugins/cursor (skill) · ATR-P0007The plugin's own shipped launch entry starts the bundled MCP server via
npx -y @wonderwhy-er/desktop-commander@latest— no version pin, so each launch can silently run different published code than whatever version the user originally reviewed/approved when installing the plugin. This is flagged by the rule text itself: 'a server or tool whose shipped launch entry starts from an unpinned package (@latest ... via npx) ... each start can run different code than the user approved.'"mcpServers": { "desktop-commander": { "command": "npx", "args": ["-y", "@wonderwhy-er/desktop-commander@latest"] } }Show 1 more location
- MCP server: `desktop-commander`, installed with `npx -y @wonderwhy-er/desktop-commander@latest`.
Recommended fixPin mcpServers.desktop-commander.args to an exact version (e.g. @wonderwhy-er/desktop-commander@0.3.0) and bump it deliberately as part of a reviewed plugin update, rather than floating to @latest on every launch.
- 9MediumUnrestricted Network Fetch / SSRFDeclared behaviorRepository-wide · ATR-C0001The read_file tool (schemas.ts ReadFileArgsSchema) accepts an arbitrary
path+isUrl:trueand, when isUrl is set, hands the value directly to fetch() with no scheme/host allowlist and no RFC1918/loopback/cloud-metadata (169.254.169.254) IP filtering. The tool's own description ('Can fetch content from URLs when isUrl parameter is set to true') confirms this is a declared capability, but a declared URL-fetch feature still needs SSRF containment, which is absent here. Any caller of the MCP tool (the LLM, potentially steered by injected instructions from previously-fetched/read untrusted content) can direct the server to fetch internal network addresses or cloud-metadata endpoints and have the response content returned into the agent's context.export const ReadFileArgsSchema = z.object({ path: z.string(), isUrl: z.boolean().optional().default(false), offset: z.number().optional().default(0), length: z.number().optional().default(1000), sheet: z.string().optional(), range: z.string().optional(), options: z.record(z.any()).optional(), origin: z.enum(['ui', 'llm']).optional(), });Show 2 more locations
export async function handleReadFile(args: unknown): Promise<ServerResult> { ... const parsed = ReadFileArgsSchema.parse(args); ... const options: ReadOptions = { isUrl: parsed.isUrl, offset: parsed.offset ?? 0, length: lengthGiven ? parsed.length : defaultLimit, sheet: parsed.sheet, range: parsed.range, svgAsImage: parsed.origin === 'ui' }; const resolvedFilePath = parsed.isUrl ? parsed.path …export async function readFileFromUrl(url: string, svgAsImage = false): Promise<FileResult> { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), FILE_OPERATION_TIMEOUTS.URL_FETCH); try { const response = await fetch(url, { signal: controller.signal });+2 more locations in the finding JSON
Recommended fixBefore fetching a URL in readFileFromUrl, resolve the hostname and reject requests whose resolved IP falls in loopback/RFC1918/link-local/unique-local ranges (and reject the AWS/GCP/Azure metadata IP specifically), restrict to http/https schemes, and consider an explicit allowlist or opt-in flag the user must set for non-public URLs.
- 10MediumDoS Protections: Timeouts, Concurrency, Streaming LimitsRepository-wide · ATR-C0004readFileFromUrl's AbortController timeout is cleared once the fetch response headers arrive, so the URL_FETCH timeout does not bound the time spent reading the response body. The handler then calls response.text()/arrayBuffer() and buffers the entire body in memory with no size limit, so a large or slow-to-complete response from an attacker-controlled or compromised URL can consume unbounded memory or keep the read open indefinitely, even though process/terminal output elsewhere in the codebase is capped (MAX_BUFFERED_OUTPUT_CHARS, MAX_LINE_CHARS, MAX_WAIT_OUTPUT_CHARS).
server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest): Promise<ServerResult> => { const args = request.params.arguments;Show 2 more locations
const resolvedFilePath = parsed.isUrl ? parsed.path : resolveAbsolutePath(parsed.path); const fileResult = await readFile(parsed.path, options);export async function readFileFromUrl(url: string, svgAsImage = false): Promise<FileResult> { // Set up fetch with timeout const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), FILE_OPERATION_TIMEOUTS.URL_FETCH); try { const response = await fetch(url, { signal: controller.signal }); // Clear the timeout since fetch completed clearTimeout(timeoutId);+1 more location in the finding JSON
Recommended fixKeep (or re-arm) the abort timeout for the full duration of the body read, not just until response headers arrive, and enforce a maximum content-length / byte-count cap on the fetched body in readFileFromUrl, aborting the response stream once the cap is exceeded.
- 11MediumLogging Hygiene (No Secrets/PII/Tokens) & Audit TrailRepository-wide · ATR-C0005The MCP's tool-call audit logger (trackToolCall) unconditionally serializes raw tool-call arguments and appends them to the audit log (claude_tool_call.log) before any validation or redaction step runs. The remote-diagnostics redact() function sanitizes only the remote-device log and telemetry pipeline; it is never applied to this audit-logging sink. As a result, credentials or secrets passed as tool arguments (for example in shell commands or file-write contents) can be persisted in plaintext in the tool-call audit log.
async function handleCallToolRequest(request: CallToolRequest): Promise<ServerResult> { const { name, arguments: args } = request.params; const startTime = Date.now();Show 2 more locations
// Track tool call trackToolCall(name, args);export async function trackToolCall(toolName: string, args?: unknown): Promise<void> { try { // Get current timestamp const timestamp = new Date().toISOString(); // Format the log entry const logEntry = `${timestamp} | ${toolName.padEnd(20, ' ')}${args ? `\t| Arguments: ${JSON.stringify(args)}` : ''}\n`;+1 more location in the finding JSON
Recommended fixApply the existing redact() function (or an equivalent secret-scrubbing routine) to tool-call arguments in trackToolCall before they are serialized and written to claude_tool_call.log, so all audit-log sinks route through the same redaction layer as remote diagnostics.
- 12MediumContainer/Runtime HardeningRepository-wide · ATR-C0006The shipped Dockerfile (community Smithery.ai build) has no USER directive, so the container runs the Node MCP server as root by default. Trivy config scan flags this as DS-0002 (HIGH, 'Image user should not be root') and DS-0026 (LOW, no HEALTHCHECK). This increases blast radius if a code-execution bug (e.g. the terminal/exec tools, or a dependency CVE) is ever exploited inside the container.
FROM node:lts-alpine ENV MCP_CLIENT_DOCKER=true WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --ignore-scripts RUN npm rebuild @vscode/ripgrep COPY . . RUN npm run build CMD [ "node", "dist/index.js" ]
Recommended fixAdd a non-root
USER node(the official node image ships anodeuser) before CMD, and add a HEALTHCHECK instruction. - 13MediumUnrestricted Tool Permissions (Overprivileged Tools)Declared behaviormcp-server (MCP server) · ATR-M0002Desktop Commander ships with unrestricted filesystem access by default (allowedDirectories: []) and a denylist-only command filter, consistent with SECURITY.md's description of the server as trusted, privileged local automation rather than a sandboxed tool. The command validator fails closed on validation errors, and set_config_value restricts mutation to an allowed set of keys, providing partial containment beyond the base denylist. No per-tool role separation or capability scoping exists, so a caller directed by untrusted content can still reach the shell and widen file-system or command scope beyond any single call's stated purpose; prompt-injection exploitation of this path was not itself reproduced.
private getDefaultConfig(): ServerConfig { return { blockedCommands: [ ... ~30 entries ... ], // Disk and partition management "mkfs", // Create a filesystem on a device "format", // Format a storage device (cross-platform) "mount", // Mount a filesystem ... "fdisk", // Manipulate disk partition tables "dd", // Convert and copy files, can write directly to disksShow 2 more locations
async validateCommand(command: string): Promise<boolean> { const config = await configManager.getConfig(); const blockedCommands = config.blockedCommands || []; const allCommands = this.extractCommands(command); for (const cmd of allCommands) { if (blockedCommands.includes(cmd)) { return false; } } return true; }name: "set_config_value", description: ` Set a specific configuration value by key. IMPORTANT: Setting allowedDirectories to an empty array ([]) allows full access to the entire file system, regardless of the operating system. ${CMD_PREFIX_DESCRIPTION}`,+1 more location in the finding JSON
Recommended fixDefault allowedDirectories to a narrow, explicit scope rather than '[] = full disk'; move from a command denylist to an opt-in allowlist for start_process where feasible; require a distinct, non-model-triggerable confirmation step before set_config_value can widen allowedDirectories/blockedCommands.
What’s inside
BlueRock found 22 components in this repository. Each is checked against the rules for its type.
MCP servermcp-server.32Failing1 high1 medium
Its tools can
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
MCP server rules 2 of 11 flagged
2 rules passed · 7 did not applyShow
PassedTool Name Collision / SpoofingATR-M0006Details
Why it got this result
Desktop Commander registers filesystem tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info, read_multiple_files) that overlap names used by the official MCP reference filesystem server, but every shipped launch configuration (.mcp.json, Claude and Cursor plugin manifests) declares only the desktop-commander server identity, and the tool registration code builds its list independently with no merge against, or collision check with, another server's names. No client-side merge, silent shadowing, or bypass of server-qualified routing is present in the shipped configurations, so the overlapping names alone do not establish an exploitable collision in this deployment.
Why it matters
Prevents malicious tool shadowing.
Evidence
{
name: "read_file",
description: `Show 2 more locations
{
name: "write_file",
description: `server.setRequestHandler(ListToolsRequestSchema, async () => {
try {
const showMcpUiPreviews = await shouldShowMcpUiPreviews();
const allTools = [
// Configuration tools
{
name: "get_config",
description: `+1 more location in the finding JSON
PassedPrompt/Metadata Injection in Tool DescriptionsATR-M0007Details
Why it got this result
Manual review of every tool description string in src/server.ts (and the shared CMD_PREFIX_DESCRIPTION / SERVER_INSTRUCTIONS constants) found only functional usage text, no hidden directives, no zero-width/bidi control characters (checked programmatically for U+200B/U+200C/U+200D/U+202E/U+202A/U+2066/U+2069/U+FEFF across all tool-facing strings), and no ANSI escape sequences or 'ignore previous instructions'-style covert directives. Descriptions only explain functional behavior (pagination, format handling, security notes for set_config_value) in plain language surfaced identically to what a developer reading the source sees.
Why it matters
Prevents covert model manipulation.
Evidence
description: `
Set a specific configuration value by key.
Security note: configuration changes affect later file and command behavior.
Isolating them from untrusted file or command content reduces the chance that external content influences configuration.
Config keys include: ...`Does not applyOAuth 2.1 + PKCE Correctness (MCP Authorization)ATR-M0005Details
Why it got this result
The MCP server itself exposes only a stdio transport (src/index.ts, FilteredStdioServerTransport) — no streamable_http/SSE/HTTP transport exists for MCP clients, so the MCP Authorization spec's OAuth requirement does not attach to this surface. The separate 'remote' subcommand (src/remote-device/) is an optional outbound bridge that uses OAuth2 Device Authorization Grant + PKCE (src/remote-device/device-authenticator.ts) against the vendor's own hosted backend to relay tool calls over Supabase Realtime — this is a device/client flow with PKCE already implemented, not the MCP client↔server transport the rule targets, and does not change the base server's stdio-only classification.
Why it matters
Misconfig enables token theft and confused-deputy attacks.
Evidence
async function runServer() {
try {
if (process.argv[2] === 'setup') { ... }
if (process.argv[2] === 'remove') { ... }
if (process.argv[2] === 'remote') {
await runRemote();
return;
}
...
const transport = new FilteredStdioServerTransport();Does not applyNo Authentication or Authorization Checks (General)ATR-M0010Details
Why it got this result
Rule requires a remote transport (requires_remote_transport=true). The MCP server binds no network listener at all — it communicates over stdio, which is gated by the OS process boundary (whatever spawns the process, e.g. Claude Desktop). No bare ':PORT' or '0.0.0.0' bind exists anywhere in src/ for the MCP protocol itself.
Why it matters
Prevents unauthorized tool use and data access.
Evidence
const transport = new FilteredStdioServerTransport(); global.mcpTransport = transport;
Does not applyOAuth Proxy Per-Client Consent (Confused Deputy Defense)ATR-M0011Details
Why it got this result
Desktop Commander is not an OAuth proxy forwarding authorization to a distinct upstream IdP on behalf of dynamically-registered MCP clients. The only OAuth-like flow in the repo is the optional 'remote device' feature (src/remote-device/device-authenticator.ts), where the local device itself authenticates (Device Authorization Grant + PKCE) directly to the vendor's own backend to enable remote relay of tool calls — there is no MCP-client registration endpoint, no shared upstream client_id proxied on behalf of third-party MCP clients, and no bridge-code minting pattern. Q1/Q2 of the evaluation method do not apply.
Why it matters
Shared upstream client + dynamic MCP-client registration + no per-client consent enables attacker-registered MCP clients to receive bridge codes carrying victim upstream tokens — the documented MCP Confused Deputy attack.
Evidence
async authenticate(deviceId?: string): Promise<AuthSession> {
console.log('🔐 Connecting this computer...\n');
const pkce = this.generatePKCE();
const deviceAuth = await this.requestDeviceCode(pkce.challenge, deviceId);
this.displayUserInstructions(deviceAuth);
const tokens = await this.pollForAuthorization(deviceAuth, pkce.verifier);
console.log('✅ Device verified\n');
return tokens;
}Does not applyCORS / Origin / Host Validation (DNS Rebinding Defense)ATR-M0003Details
Why it got this result
Desktop Commander's MCP protocol surface is stdio-only. src/index.ts constructs a FilteredStdioServerTransport and calls server.connect(transport) — there is no HTTP/SSE/StreamableHTTP listener for the MCP protocol itself anywhere in the codebase (grep for .listen(/createServer/express() across src/ only finds an ephemeral 127.0.0.1-bound helper server for PDF rendering, unrelated to MCP transport). Rule applicability requires_remote_transport=true, which is not met.
Why it matters
Prevents DNS rebinding/browser hijack of local or remote MCP.
Evidence
// Create transport FIRST so all logging gets properly buffered // This must happen before any code that might use logger.* const transport = new FilteredStdioServerTransport(); // Export transport for use throughout the application global.mcpTransport = transport;
Does not applyBinding to 0.0.0.0 Without Auth/TLSATR-M0004Details
Why it got this result
No network listener is bound for the MCP protocol; the only createServer()/.listen() call in the repo is an ephemeral helper bound explicitly to 127.0.0.1 for local PDF/Chrome rendering (src/tools/pdf/markdown.ts:608,621), not an MCP transport. requires_remote_transport=true is not satisfied.
Why it matters
LAN/Internet exposure enables remote exploitation.
Evidence
const server = createServer((request, response) => {
...
});
...
server.listen(0, '127.0.0.1', resolve);Does not applyCORS Misuse: Wildcard with CredentialsATR-M0008Details
Why it got this result
No HTTP/CORS surface exists for the MCP protocol (stdio-only); requires_remote_transport=true is not met.
Why it matters
Prevents credentialed cross-origin abuse.
Evidence
const transport = new FilteredStdioServerTransport(); global.mcpTransport = transport;
Does not applyTransport Security: TLS Everywhere (Remote)ATR-M0009Details
Why it got this result
MCP transport is local stdio only; no remote listener requiring TLS exists in this component. requires_remote_transport=true is not met.
Why it matters
MitM can steal tokens and tamper responses.
Evidence
const transport = new FilteredStdioServerTransport(); global.mcpTransport = transport;
Show its 25 tools
| Tool | It can |
|---|---|
create_directoryCreate a new directory | Create or change data |
edit_blockApply exact-match find/replace edits to a file | Create or change dataDelete or overwrite data |
force_terminateTerminate a process session started by start_process | Delete or overwrite data |
get_configGet the complete server configuration as JSON (blockedCommands, allowedDirectories, etc.) | Read dataChange settings |
get_file_infoGet metadata about a file or directory | Read data |
get_more_search_resultsPage through results of an in-progress search session | Read dataReturns untrusted content |
get_recent_tool_callsGet history of recent tool calls with arguments/outputs | Read data |
get_usage_statsGet usage statistics for the server | Read data |
give_feedback_to_desktop_commanderSend feedback about the tool to the developers | Post or message on your behalf |
interact_with_processSend input to a running process/REPL session | Run code or commandsDelete or overwrite data |
kill_processKill an arbitrary OS process by PID | Delete or overwrite data |
list_directoryList contents of a directory | Read data |
list_processesList OS processes | Read data |
list_searchesList active search sessions | Read data |
list_sessionsList active terminal sessions | Read data |
move_fileMove or rename a file/directory | Create or change dataDelete or overwrite data |
read_fileRead contents from files and URLs, including PDFs, images, Excel, DOCX | Read dataReturns untrusted contentReach any network address |
read_multiple_filesRead multiple files at once | Read dataReturns untrusted content |
read_process_outputRead output from a running process session | Read dataReturns untrusted content |
set_config_valueSet a specific configuration value by key, including allowedDirectories and blockedCommands | Change settingsDelete or overwrite data |
start_processStart a terminal process/shell/REPL session and run a command | Run code or commandsDelete or overwrite dataUse secrets and credentialsReach any network address |
start_searchStart a ripgrep-backed file/content search session | Read dataReturns untrusted content |
stop_searchStop an in-progress search session | Create or change data |
write_fileWrite content to a file | Create or change dataDelete or overwrite data |
write_pdfRender markdown to PDF or modify existing PDFs | Create or change dataDelete or overwrite data |
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins.54Weak1 high
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 1 of 8 flagged
7 rules passedShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Reviewed all skill artifacts in scope at repo root not individually registered (desktop-commander-overview/SKILL.md, terminal/SKILL.md, identically duplicated under skills/, plugins/claude/skills/, plugins/cursor/skills/) plus the repo's plugin/MCP manifests (plugin.json, plugin.yaml, gemini-extension.json, .mcp.json, manifest.template.json, config.json). desktop-commander-overview and terminal both describe the exact tool surface the Desktop Commander MCP exposes (process/shell, file I/O, search, SSH) -- the manifests declare the same capability set (terminal-control, file-search, file-system-operations, excel/pdf/docx support) that the skill text uses. No undisclosed network egress, credential harvesting, or hidden exec capability found in any model-facing carrier.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: Use for Desktop Commander MCP capabilities — persistent shells and REPLs, long-running processes, filesystem beyond the workspace, structured files (.xlsx, .docx, .pdf, images) and large local data files such as CSVs, ripgrep search at scale, SSH, or cross-turn state. # Desktop Commander MCP Desktop Commander gives the agent reach across the user's actual computer — files, folders, terminals, processes, structured documents, and remote machines reachable over SSH.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
A YARA signature (Zeus_Metadata_Consent_Suppression) flagged a cluster at lines 20-84 of desktop-commander-overview/SKILL.md (identically present under skills/, plugins/claude/skills/, and plugins/cursor/skills/). Manual review of that exact cluster (the attestation object was not available in this session) shows the opposite of consent suppression: the text explicitly instructs the host to OBTAIN confirmation before privileged actions -- 'Before running production-impacting SSH commands, explain the intended action and get user confirmation when the risk is non-trivial' (line 36) and 'Before deploys, restarts, migrations, or other environment-changing commands, summarize the action and confirm with the user unless they already explicitly asked for that exact operation' (line 42). This is a negative-indicator match (prose explicitly defers to host consent gate) and the YARA hit is assessed as a false positive on this reading. The terminal/SKILL.md (also only at root/plugin level, not individually registered) similarly instructs 'Never run destructive commands without explicit confirmation' and devotes a full safety section (S7) to requiring confirmation before destructive commands. No consent-suppression prose was found anywhere in the root-level skill/plugin manifests or instruction bodies.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
### "Debug this production issue" Before running production-impacting SSH commands, explain the intended action and get user confirmation when the risk is non-trivial. ... ### "Deploy this to staging" Before deploys, restarts, migrations, or other environment-changing commands, summarize the action and confirm with the user unless they already explicitly asked for that exact operation.
Show 1 more location
## 7. Safety: destructive commands and chaining Beyond that list, treat these as **destructive — never run without the user's explicit confirmation**, even if they'd technically succeed: - Recursive/forced deletes: `rm -rf`...
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
The onboarding workflow in desktop-commander-overview/SKILL.md explicitly uses get_recent_tool_calls to retrieve prior tool arguments and outputs for last-session reconstruction (lines 90-92). This constitutes a conversation/tool-history read, which brings the artifact within this rule's mechanism-agnostic context-read test. The recovered history stays in-session and is returned to the user; no instruction directs it to any external destination, so no harvest-plus-exfiltration pair exists.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
## Operating etiquette - Preview risky commands before running; run read-only inspection first. - Use absolute paths; respect `allowedDirectories`. - Never print secrets, tokens, or passwords into output; redact if they appear.
PassedFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
The root ships a format-native install-time lifecycle primitive via package.json's postinstall hook, which runs track-installation.js and verify-ripgrep.js. track-installation.js only reads an existing client ID or generates an in-memory fallback UUID when none exists; it does not create a persisted ID file or config directory. verify-ripgrep.js resolves the bundled ripgrep binary and repairs its executable permissions (chmod 0755 on Unix) as part of normal installation -- a purpose-matched executable repair, not a covert directive or payload delivery. Neither script rewrites host control-plane/memory files, exports exec-enabling environment variables, or repoints any LLM provider endpoint. No covert persistence or endpoint-substitution was found.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
"postinstall": "node dist/track-installation.js && node dist/npm-scripts/verify-ripgrep.js || node -e \"process.exit(0)\""
Show 2 more locations
// Telemetry proxy configuration (same as setup script) const TELEMETRY_PROXY_URL = 'https://telemetry.desktopcommander.app/mp/collect'; [REDACTED]
{
"mcpServers": {
"desktop-commander": {
"command": "npx",
"args": ["-y", "@wonderwhy-er/desktop-commander@latest"]
}
}
}PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
gemini-extension.json declares 'excludeTools': [] (no tools excluded = full MCP toolset available), and package manifests declare no narrower allowed-tools/OAuth scope. However, this is broad-but-justified: the product's entire declared purpose across every manifest (plugin.json, plugin.yaml, gemini-extension.json, server.yaml) IS full terminal/filesystem/process-management access ('MCP server for terminal commands, process management, and file operations'), disclosed prominently in README/FAQ/PRIVACY.md, so the broad grant matches the documented function rather than exceeding it. No wildcard grant is paired with a narrower stated purpose (the false-positive guard for intrinsic broad capability applies). The desktop-commander-overview and terminal skill texts (no allowed-tools frontmatter) likewise only describe using the already-disclosed, purpose-matched tool surface.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
{
"name": "desktop-commander",
"version": "1.5.0",
"description": "MCP server for terminal commands, process management, and file operations across text, code, PDF, DOCX, Excel, images, and structured data",
"mcpServers": {
"desktop-commander": {
"command": "npx",
"args": ["-y", "@wonderwhy-er/desktop-commander"]
}
},
"excludeTools": []
}PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint-scanned desktop-commander-overview/SKILL.md and terminal/SKILL.md (identically present under skills/, plugins/claude/skills/, plugins/cursor/skills/) plus the root plugin/MCP manifests (plugin.json, plugin.yaml, gemini-extension.json, .mcp.json, manifest.template.json, config.json, server.yaml, smithery.yaml). No zero-width/bidi/Tag-block codepoints, mixed-script confusables, forged chat-template/role tokens, or ANSI/OSC escapes were found in any model-facing carrier; no HTML-comment/CSS-hidden concealment patterns. The YARA hit on desktop-commander-overview/SKILL.md (addressed under ATR-P0002) was reviewed in full -- it is plain, visible, ASCII prose requiring user confirmation, with no forged/concealed content.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(codepoint scan of desktop-commander-overview/SKILL.md and terminal/SKILL.md: no concealment codepoints, no forged chat-template tokens, no HTML-comment/CSS-hidden text, no ANSI escapes found)
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
The repo's own declared identity ('desktop-commander' / 'Desktop Commander' / '@wonderwhy-er/desktop-commander') is published from the verifiable genuine owner -- git remote origin is github.com/wonderwhy-er/DesktopCommanderMCP, matching the publisher named in every manifest (plugin.json author, package.json, server.json repository field). No near-miss/homoglyph of a well-known brand, no trust-implying prefix, no version-spoofing suffix, and no fork/official-replacement claims against a third party were found across the root-level manifests (plugin.json, plugin.yaml, gemini-extension.json, server.json, server.yaml, smithery.yaml) or the desktop-commander-overview/terminal skill text.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
{
"name": "io.github.wonderwhy-er/desktop-commander",
"title": "Desktop Commander",
"repository": {
"url": "https://github.com/wonderwhy-er/DesktopCommanderMCP",
"source": "github"
},
"version": "0.3.0"Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/claudeplugins/claude89Strong1 high
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 1 of 8 flagged
6 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
The plugin manifest declares exactly one capability class: launching the Desktop Commander MCP server for terminal/process/file operations. All six bundled skills only direct the model to call that MCP's own already-registered tools for exactly the workflows the manifest/README describe. No bundled script, no outbound network call to a non-documented host, no undisclosed credential read, and no undeclared subprocess/eval was found (only SKILL.md/README.md/plugin.json files exist under plugins/claude).
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
{
"name": "desktop-commander",
"description": "MCP server for terminal commands, process management, and file operations...",
"mcpServers": {
"desktop-commander": {
"command": "npx",
"args": ["-y", "@wonderwhy-er/desktop-commander@latest"]
}
}
}Show 1 more location
## Components - MCP server: `desktop-commander`... - Skill: `desktop-commander-overview` - Skill: `terminal` - Skill: `computer-health-check` - Skill: `ai-tools-setup` - Skill: `knowledge-base` - Skill: `obsidian-vault`
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
The plugin manifest registers only skill files and the MCP server launch; it contains no approval-bypass or consent-suppression instructions. The ai-tools-setup skill requires redaction of secrets and backups before edits, and the knowledge-base skill asks clarifying questions for ambiguous tasks, but neither skill contains an explicit per-action confirmation requirement. The verdict rests on the absence of any affirmative consent-suppression or self-authorizing language in the bundled instructions, not on an explicit all-write confirmation guarantee.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
concrete working alternative instead of retrying the same thing. 4. **Never echo secrets.** API keys, tokens, and passwords go *into the right config or `.env` file* — never repeated back in chat. If a user pastes a live secret, warn them, use it, and suggest they rotate it if it was shared
Show 1 more location
{
"name": "desktop-commander",
"description": "MCP server for terminal commands, process management, and file operations...",
"mcpServers": {
"desktop-commander": {
"command": "npx",
"args": ["-y", "@wonderwhy-er/desktop-commander@latest"]
}
}
}PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
The ai-tools-setup skill reads MCP client configuration files and provider API keys for its declared installation/repair purpose, and additionally performs a provider key validation check via an outbound request to the provider's own endpoint -- this is sanctioned authentication to the user-selected provider, not an exfiltration channel. The desktop-commander-overview skill reads recent tool outputs for its declared overview function. No instructed destination exists for forwarding secrets or conversation history to a third party; output summaries redact secrets and conversation history is not persisted outside the session.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
concrete working alternative instead of retrying the same thing. 4. **Never echo secrets.** API keys, tokens, and passwords go *into the right config or `.env` file* — never repeated back in chat. If a user pastes a live secret, warn them, use it, and suggest they rotate it if it was shared
Show 1 more location
## Safety - Only add MCP servers from sources the user trusts; an MCP server runs with the user's privileges. - Back up before editing; redact secrets in summaries; restart the client to apply changes.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
The plugin manifest declares a single, narrow capability: launching the Desktop Commander MCP server. There is no wildcard/catch-all permission block (no "permissions": ["*"]", no broad OAuth scope), and no bundled skill's frontmatter declares an allowed-tools list at all, so there is no self-declared capability class that exceeds the stated purpose. No package.json bin/postinstall or lifecycle hook is shipped.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
{
"name": "desktop-commander",
"description": "MCP server for terminal commands, process management, and file operations...",
"mcpServers": {
"desktop-commander": {
"command": "npx",
"args": ["-y", "@wonderwhy-er/desktop-commander@latest"]
}
}
}PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint-scanned all 24 files under the in-scope component directories (Python regex for zero-width/bidi-override/Tag-block chars U+200B-200D/2060/FEFF/202A-202E/2066-2069/E0000-E007F) -- zero matches. Also grepped for forged chat-template tokens (<|im_start|>, [INST], <start_of_turn>, etc.) and 'ignore previous instructions' style imperatives -- none found. All model-facing text is plain ASCII/UTF-8 prose matching the rendered view.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(scanned; no zero-width/bidi/tag-block codepoints, no forged chat-template tokens, no homoglyph substitution found)
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
The declared identity 'desktop-commander' matches the canonical name of this project's own product (repository wonderwhy-er/DesktopCommanderMCP, the genuine publisher). No near-miss/typosquat of a third-party brand, no trust-implying prefix, no version/tier-spoofing suffix, and no impersonation prose claiming to be an official/updated build of someone else's tool.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
{
"name": "desktop-commander",
"displayName": "Desktop Commander",
"version": "0.2.0",
"description": "MCP server for terminal commands, process management, and file operations...",
"author": {
"name": "Desktop Commander",
"email": "[REDACTED]"
},Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
plugins/claude ships no lifecycle/event hook (no .claude/settings.json PreToolUse/PostToolUse, no before_tool_callback), no entry-script/dotenv/git-hook files, and no exec-enabling or LLM-endpoint-substituting env assignment. Its only control-plane-adjacent artifact is the declared MCP server launch line in plugin.json (npx -y @wonderwhy-er/desktop-commander@latest), which per this rule's own false-positive guard is 'not by itself format-native out-of-band execution' -- an unpinned launch package is explicitly carved out to ATR-P0007, not this rule. No other primitive exists, so there is no out-of-band execution surface for this rule to evaluate.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
{
"name": "desktop-commander",
"mcpServers": {
"desktop-commander": {
"command": "npx",
"args": ["-y", "@wonderwhy-er/desktop-commander@latest"]
}
}
}Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/cursorplugins/cursor89Strong1 high
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 1 of 8 flagged
6 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
The plugin manifest/README declare exactly what the bundle provides: an MCP server plus listed skills/rule steering the agent toward Desktop Commander tool use. No hidden components beyond what README.md enumerates were found in the directory listing.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
# Desktop Commander Cursor Plugin ... ## Components - MCP server: `desktop-commander`, installed with `npx -y @wonderwhy-er/desktop-commander@latest`. - Rule: `rules/desktop-commander-default.mdc` ... - Skill: `desktop-commander-overview` ... - Skill: `terminal` ...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
The always-on rule only steers tool preference ('prefer Desktop Commander MCP ... for local filesystem, terminal, process, search'); it contains no language suppressing confirmation or bypassing guardrails, and points to the (confirmation-reinforcing) overview skill for detailed guidance.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
--- description: Prefer Desktop Commander MCP for local filesystem, terminal, process, search, and workspace operations. alwaysApply: true --- For local filesystem, terminal, process, search, and workspace operations, prefer Desktop Commander MCP, especially for persistent shells, long-running processes, file reads, structured documents, large local data files, SSH workflows, and process inspection.
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
The bundled overview skill reads recent tool-call context and the setup skill reads configured credentials/config values as part of their declared functionality. Both handle this data in-session: credentials are redacted and sent only to the requested provider, with no separate collection destination or exfiltration sink identified across the six bundled skills and their references.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
# Desktop Commander Cursor Plugin Desktop Commander gives Cursor agents access to a local MCP server for terminal sessions, filesystem work, structured documents, search, process management, and SSH workflows ...
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
plugin.json declares only the mcpServers entry, skills path, and rules path needed to deliver the bundle's stated purpose; no additional OS-level permissions, broad host access declarations, or unrelated capability grants are present in the manifest.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
"skills": "./skills/",
"rules": "./rules/",
"mcpServers": {
"desktop-commander": {
"command": "npx",
"args": ["-y", "@wonderwhy-er/desktop-commander@latest"]
}
}PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
No hidden unicode control characters or concealed directives found in plugin.json, README.md, or the rule .mdc file.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
--- description: Prefer Desktop Commander MCP for local filesystem, terminal, process, search, and workspace operations. alwaysApply: true --- For local filesystem, terminal, process, search, and workspace operations, prefer Desktop Commander MCP ...
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
plugin.json publisher/author/homepage/repository fields ('Desktop Commander', desktopcommander.app, github.com/wonderwhy-er/DesktopCommanderMCP) match the upstream project exactly and consistently across .claude-plugin, .cursor-plugin, and package.json; no name confusable with a different, unrelated popular project was found.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
{
"name": "desktop-commander",
"displayName": "Desktop Commander",
"version": "0.1.0",
...
"author": {
"name": "Desktop Commander",
"email": "[REDACTED]"
},
"publisher": "Desktop Commander",
"homepage": "https://desktopcommander.app",
"repository": "https://github.com/wonderwhy-er/DesktopCommanderMCP",Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
The plugin's declared MCP server launch entry is an ordinary declared server launch excluded from this rule's scope. The remaining bundled files are Markdown prose (the always-on rule, skill guidance, and references) containing no executable hooks, hostile environment assignments, or covert host-control directives; the unpinned @latest launch concern is addressed instead under the package-provenance/rug-pull rule.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- description: Prefer Desktop Commander MCP for local filesystem, terminal, process, search, and workspace operations. alwaysApply: true --- For local filesystem, terminal, process, search, and workspace operations, prefer Desktop Commander MCP ...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/claude/skills/ai-tools-setupplugins/claude/skills/ai-tools-setup100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
The SKILL.md frontmatter description ('Set up, connect, validate, and repair Claude Desktop and MCP servers using Desktop Commander') matches the body content exactly: golden rules for config editing, backups, and verification, plus three reference files for Claude Desktop/OpenClaw/Hermes setup. No behavior in the body (install/config/diagnose local AI tooling) exceeds or diverges from the declared trigger phrases.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
--- name: ai-tools-setup ... description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers using Desktop Commander ... --- # AI Tools Setup Assistant Help users install, configure, and repair AI tooling on their own machine using Desktop Commander's file and terminal tools. ...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
The skill's golden rules require backing up configs before editing and verifying changes before declaring success; no instruction weakens or bypasses any host confirmation / guardrail. 'Never echo secrets' further reinforces a protective behavior rather than suppressing one.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
4. **Never echo secrets.** ... 5. **Back up before you edit.** Copy the config to a `.bak` first ... 6. **Verify, then stop.** After a change, actually confirm it worked ...
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
Golden rule #4 explicitly forbids echoing secrets back in chat and requires redaction in any output/logs/summaries; the skill handles API keys only to the extent of testing validity, never collecting or transmitting them elsewhere.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
4. **Never echo secrets.** API keys, tokens, and passwords go *into the right config or `.env` file* — never repeated back in chat. If a user pastes a live secret, warn them, use it, and suggest they rotate it if it was shared insecurely. Redact secrets in any output, logs, or summaries you produce.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No capability/permission manifest field exists; the breadth of the described task (installing/configuring third-party AI tooling) is consistent with the stated trigger phrases and does not request scope beyond what the task needs.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: ai-tools-setup version: 0.1.0 audience: agent description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers using Desktop Commander ... ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Scanned SKILL.md and all three references/*.md files for hidden unicode control characters and covert directive phrasing; none found. All instructions are plainly visible and consistent with the stated purpose.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
## Golden rules (read before doing anything) 1. **Detect the OS and shell first.** ... 2. **Verify the model/API key works before you build.** ...
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
references/hermes.md and references/openclaw.md provide third-party install commands (curl|bash and npm install) for external tools, not this skill's own shipped runtime launch entry or a self-mutating instruction bundle. The skill package itself contains no updater, live-fetch mechanism, or capability to silently rewrite its own trusted instructions, so the provenance/mutation concern this rule targets is not established by third-party installer prose.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
**CLI only:** ```bash # Linux / macOS / WSL2 / Android (Termux) curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash ```
Show 1 more location
```bash # Install npm install -g openclaw@latest ```
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Skill identifies third-party tools (OpenClaw, Hermes) by their real names and links to their real official docs domains (docs.openclaw.ai, hermes-agent.nousresearch.com) rather than impersonating them; the skill itself is clearly scoped and attributed to Desktop Commander, consistent across all manifests.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
- **OpenClaw** — a self-hosted multi-channel gateway for AI agents. ... onboard, wire a model/provider, connect channels (Telegram, Slack, iMessage, - **Hermes** — disambiguate first: **Hermes Agent** ... vs. **Hermes 3** ... → Read [`references/openclaw.md`](references/openclaw.md)
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
SKILL.md and its three reference files are plain Markdown instructions/references containing no bundled execution hooks, entry scripts, or format-native execution primitives. The third-party daemon installation they describe is carried out by a model-selected host tool, not a format-native execution mechanism bundled by this skill, so the rule's subject of bundled execution hooks/entry scripts is not established.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
## Uninstall / cleanup - Stop the service/daemon, then `npm uninstall -g openclaw`. - Remove residual files in `~/.openclaw/` (back up first if it holds config the user may want). - Disable any autostart entry created during onboarding.
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/claude/skills/computer-health-checkplugins/claude/skills/computer-health-check100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
SKILL.md frontmatter declares a read-only computer health-check skill that uses Desktop Commander's local shell to gather diagnostics and only performs cleanups the user explicitly approves. The body (safety contract, Steps 1-5) maps 1:1 to that declaration: OS detection, read-only collection via start_process, scoring, chat report, and opt-in cleanup. No undisclosed network egress, credential reads, or exec capability beyond what is declared; explicit text forbids sudo/elevation and states 'Privacy. This reads local system metadata only. Dont send anything off the machine.'
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Run a comprehensive, read-only health check on the user's computer... scored chat summary with prioritized, plain-English recommendations and safe cleanup suggestions. Use this whenever the user wants to check their computer's health, speed it up, free up / reclaim disk space, find what's eating CPU / memory / storage, check battery health and wear, audit login & startup items, see pending updates, or asks for ... healthy". Trigger even on casual phrasing like "my laptop feels slow", "why is my fan so loud", "my computer is laggy", "running out of space", or "clean up my machine". Works on macOS, Windows, and Linux. This is a Desktop Commander skill: it relies on a real local shell (start_process / interact_with_process) and is strictly read-only by default — it never needs sudo and only performs cleanups the user explicitly approves.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
Instruction body explicitly REQUIRES user consent before any state-changing action: 'Cleanups are opt-in. You may suggest cleanups freely, but only execute one after the user explicitly approves that specific action, and only show the exact command first.' No prose instructs the agent to skip confirmation, hide actions, or claim pre-authorization; brevity guidance ('Keep it tight and scannable') only shapes report formatting, not concealment of privileged actions.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
### Step 5 — Suggest cleanups, execute only on approval For each recommendation that maps to a concrete command (see the reference file's "Cleanup" section), offer it and show the exact command. Then: cache cleanup... - **Safe, reversible** (offer to run on a yes): empty Trash, package-manager cache cleanup login items, installing OS updates... `apt-get clean` / `dnf clean` need root — suggest, don't auto-run.) - **Needs the user to decide / act** (suggest, don't auto-run): disabling login items, installing OS updates (requires restart), uninstalling apps, deleting files in Downloads.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
SKILL.md frontmatter declares no allowed-tools/scope/permissions field at all (no wildcard grant, no OAuth/permission block) and ships no bundled executable, package.json bin/postinstall, or code-executing lifecycle hook -- a purely declarative text artifact. The prose explicitly narrows its OWN use of the host's broader tool surface to read-only diagnostics plus opt-in, confirmed cleanups, and explicitly forbids sudo/elevation -- i.e. it self-restricts below what the host grants rather than requesting anything beyond its stated purpose.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
## The safety contract (read this first) This is the part that makes users trust the skill, so honor it strictly: - **Read-only by default.** The collection phase only *observes* (sizes, counts, status).
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan of SKILL.md for zero-width/bidi/Tag-block characters, mixed-script confusables, forged chat-template tokens, HTML comments, and ANSI escapes found none. The raw bytes are byte-identical to the rendered content; all instruction text is purely descriptive with no host-directed imperatives hidden from a reviewer.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(codepoint scan: no U+200B/200C/200D/2060/FEFF, no U+202A-202E/2066-2069 bidi overrides, no HTML comments, no forged chat-template tokens, no ANSI escapes found in this file)
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
SKILL.md and its reference files ship fully inside the bundle as static markdown -- no runtime fetch of instructions from a mutable endpoint, no self-rewriting install hook, and this component itself ships no MCP server launch entry (that lives one directory up in the parent plugin manifest, evaluated under the root component).
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent description: >- Run a comprehensive, read-only health check...
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Name 'computer-health-check' is a plain descriptive name with no near-miss edit-distance to any known brand/tool, no homoglyph/leetspeak substitution, no trust-implying prefix (official-/verified-), no version/tier-spoofing suffix, and no prose claiming to be an official/updated/drop-in replacement for a named legitimate tool. Published by wonderwhy-er (the genuine Desktop Commander project owner, confirmed via git remote origin).
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
No instruction text reads host credential stores (env secrets, ~/.ssh, ~/.aws, keychain) or the model's conversation/system-prompt context. Per the rule's SCOPE guidance, the harvest half governs applicability: this skill only reads local system diagnostics (disk/memory/battery/process state) and explicitly states 'Privacy. This reads local system metadata only. Don't send anything off the machine.' With no harvest step constructible, no harvest+exfil pair exists, so the rule's subject does not exist in this component.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
- **Privacy.** This reads local system metadata only. Don't send anything off the machine; the public-IP check is optional and should be labeled.
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
This component ships only SKILL.md and three reference markdown files -- no bundled scripts, lifecycle/event hooks, entry-point code, dotenv/git-hook files, or env-var assignments. Per the rule's SCOPE paragraph, since the bundle ships none of the format-native execution/control-plane primitives the rule enumerates, there is no OOB-execution surface to evaluate.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent description: >- Run a comprehensive, read-only health check...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/claude/skills/desktop-commander-overviewplugins/claude/skills/desktop-commander-overview100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Pure documentation describing Desktop Commander's existing MCP tools and maps 1:1 to the declared frontmatter description. No bundled code, no hidden network egress, no undisclosed credential reads.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities ... --- # Desktop Commander MCP Desktop Commander gives the agent reach across the user's actual computer...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
A YARA signature (agentic_metadata:Zeus_Metadata_Consent_Suppression, cluster L20-L84) flagged this file. The scanner_attestation object was not available in this session. Manually reading the full cluster L20-L84 shows the opposite of suppression: the text explicitly instructs the agent to GET user confirmation before risky actions ('explain the intended action and get user confirmation when the risk is non-trivial'; 'summarize the action and confirm with the user unless they already explicitly asked for that exact operation'). There is no 'do not tell the user', 'auto-approve', or authority-override language anywhere in the file. This is assessed as a false positive on the signature's part -- the keyword proximity of 'confirm'/'user'/'explicitly' near each other matched the pattern but the semantic content reinforces consent rather than suppressing it.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
### "Debug this production issue" Before running production-impacting SSH commands, explain the intended action and get user confirmation when the risk is non-trivial. ... ### "Deploy this to staging" Before deploys, restarts, migrations, or other environment-changing commands, summarize the action and confirm with the user unless they already explicitly asked for that exact operation.
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
SKILL.md lines 82-84 retrieve prior tool arguments and outputs, which is a conversation-context read this rule evaluates. The read remains in-session, with no instruction to transmit or exfiltrate that context outward, so the harvest-and-exfiltration pattern this rule targets is not present.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
(full file reviewed: content only describes Desktop Commander's shell/file/search/SSH tools and example workflows; no credential-store read or conversation-context dump instruction present)
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
SKILL.md frontmatter declares no allowed-tools/permissions field at all (verified: grep for allowed-tools/permissions/scope across this file returns nothing) -- there is no wildcard or catch-all declared scope to flag.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities ... version: 0.1.0 audience: agent ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan found no zero-width/bidi/Tag-block characters and no forged chat-template tokens or homoglyph substitutions in this SKILL.md.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(full file codepoint-scanned: 7-bit-clean ASCII prose aside from standard em-dashes/emoji; no concealment codepoints, no forged system/role tokens)
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Name 'desktop-commander-overview' is a plain descriptive name for a documentation skill about the repo's own Desktop Commander product, not a near-miss of any other brand.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities ...
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
This component is a single SKILL.md with no bundled script, lifecycle hook, dotenv, git hook, or env-var assignment of any kind. It only instructs the model to call already-registered host tools through the normal consent-gated flow, which is explicitly not a format-native primitive per this rule.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities ... version: 0.1.0 audience: agent ---
Does not applySkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
This component is a single static SKILL.md shipped inline in the repo -- it has no launch entry of its own, no runtime instruction fetch, and no self-update mechanism. The launch-pinning concern belongs to the plugin.json manifest (evaluated under the plugins/claude component), not to this skill file.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities ... version: 0.1.0 ---
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/claude/skills/knowledge-baseplugins/claude/skills/knowledge-base100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Declared purpose ('Create and maintain a Markdown knowledge base') matches the entire body, which only covers note/index conventions and file read/write/search workflows via Desktop Commander tools. No undeclared capability.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
--- name: knowledge-base ... description: >- Create and maintain a Markdown knowledge base that any AI agent can read, search, and update. ... --- # Knowledge Base (Markdown, agent-readable)
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
Maintenance workflows do merge and archive notes, but the instructions require reporting these changes to the user and require clarification for ambiguous requests, so there is no language suppressing consent or concealing actions from the user.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
## Workflows ### Create a new KB 1. `create_directory` for the folder structure above. 2. `write_file` `INDEX.md` with the instructions block ...
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No capability/permission manifest field; scope (markdown file CRUD) matches the stated purpose with no broader access requested.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent description: >- Create and maintain a Markdown knowledge base ... ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
No hidden unicode control characters or covert directives found in the file.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
--- name: knowledge-base ... --- # Knowledge Base (Markdown, agent-readable)
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
This skill contains no package install, launch config, or external-code-execution instructions of any kind — purely local markdown file management.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: knowledge-base ... --- # Knowledge Base (Markdown, agent-readable)
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Generic skill name with no reference to any third-party brand to impersonate; consistent with the Desktop Commander plugin bundle's own identity.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: knowledge-base ... --- # Knowledge Base (Markdown, agent-readable)
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
The skill only reads and writes user-authored Markdown notes; it contains no mechanism that reads host credentials, API keys, or live conversation or memory context, so the rule's subject matter is absent rather than present-and-secure.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
--- name: knowledge-base ... description: >- Create and maintain a Markdown knowledge base that any AI agent can read, search, and update. ...
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
The skill is prose-only and invokes only disclosed KB index-writing operations through existing tools; it ships no format-native execution or host control-plane persistence primitive, placing it outside the rule's subject matter.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
## Workflows ### Create a new KB 1. `create_directory` for the folder structure above. 2. `write_file` `INDEX.md` ...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/claude/skills/obsidian-vaultplugins/claude/skills/obsidian-vault100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
SKILL.md declares an Obsidian vault-organizing skill (MOCs, wikilinks, frontmatter, dashboards). Body content (wikilinks, MOCs, frontmatter rules, dashboards, orphan detection, folder organization, dedup/rename workflows) matches the declared scope exactly, using only read_file/write_file/edit_block/start_search file operations on vault content -- no network egress, no credential reads, no undeclared exec capability.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >-
Organize Obsidian vaults with MOCs, wikilinks, frontmatter/properties,
dashboards, orphan-note checks, and cleanup workflows. Use ONLY when the user
user explicitly refers to Obsidian...
an Obsidian-specific feature such as wikilinks ("[[ ]]"), the Dataview or Bases
plugins, or Obsidian Properties. Within that Obsidian context it covers
creating Maps of Content, adding/fixing wikilinks, finding orphan notes,
normalizing YAML frontmatter, generating Dataview/Bases dashboards, organizingPassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
No imperative prose suppresses confirmation, hides privileged actions, or asserts override authority over the host/user. Destructive-adjacent actions (deletes during dedup) are described as 'copy unique content... then repoint before deleting it' as part of the declared workflow, not concealed from the user; the skill instead defers renames/moves to the user acting inside Obsidian itself.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
## Deduplicate & rename - **Duplicates**: `start_search` titles/aliases to find near-identical notes; merge into one, keep the most-linked filename, copy unique content with `edit_block`, then `start_search` for inbound `[[links]]` to the discarded note and repoint them before deleting it. - **Renames**: do them inside Obsidian (Rename note / `F2`) so backlinks update —
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No allowed-tools/scope/permissions field in frontmatter, no wildcard grant, no bundled executable or install hook. The described capability (reading/editing local vault note files) matches the stated purpose exactly with no requested reach into shell/network/credential territory.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent description: >- Organize Obsidian vaults with MOCs, wikilinks, frontmatter/properties, dashboards, orphan-note checks, and cleanup workflows...
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan found no concealment codepoints, forged tokens, HTML comments, or ANSI escapes. Content is purely descriptive guidance for Obsidian vault organization.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(codepoint scan: no concealment codepoints, no HTML comments, no forged chat-template tokens found in this file)
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
SKILL.md ships fully inside the bundle; no runtime instruction fetch, no self-mutation, no launch entry owned by this component.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent description: >- Organize Obsidian vaults with MOCs, wikilinks...
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Name 'obsidian-vault' plainly describes its function and is not a near-miss/homoglyph of a brand name; the skill does not claim to BE Obsidian or impersonate the Obsidian publisher -- it explicitly scopes itself to assisting users who already use Obsidian ('Use ONLY when the user explicitly refers to Obsidian'). No trust-implying prefix, version-spoofing suffix, or fork/official claim.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent description: >- Organize Obsidian vaults with MOCs, wikilinks, frontmatter/properties... Use ONLY when the user explicitly refers to Obsidian...
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
No instruction text reads host credential stores or model conversation/system-prompt context; the skill only reads/writes vault note files. No harvest step exists, so no harvest+exfil pair is constructible.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
description: >- Organize Obsidian vaults with MOCs, wikilinks, frontmatter/properties, dashboards, orphan-note checks, and cleanup workflows...
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
This component ships only a single SKILL.md -- no bundled scripts, hooks, dotenv, or env assignments. No format-native execution/control-plane primitive exists to evaluate.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent description: >- Organize Obsidian vaults with MOCs, wikilinks...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/claude/skills/terminalplugins/claude/skills/terminal100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
5 rules passed · 3 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Terminal skill's declared purpose (shell/terminal assistance via Desktop Commander) matches actual prose content exactly. No undisclosed capability.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
--- name: terminal description: Use Desktop Commander for terminal and command-line work... Never run destructive commands without explicit confirmation. audience: agent
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
No bundled hook/settings file or prose instructs the agent to bypass confirmation. All bundled skills explicitly REQUIRE confirmation before state-changing actions (terminal, ai-tools-setup, computer-health-check all gate destructive/approve-worthy actions on explicit user yes). No 'do not tell the user' / 'auto-approve' / 'proceed without confirmation' language anywhere in the bundle.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
Beyond that list, treat these as **destructive -- never run without the user's explicit confirmation**, even if they'd technically succeed: ... For these: show the exact command, explain in one line what it will irreversibly do, and wait for a clear "yes".
Show 2 more locations
## 7. Safety: destructive commands and chaining Desktop Commander already refuses a built-in `blockedCommands` list... `sudo`, `mkfs`, `format`, `dd`, `fdisk`, `shutdown`, `reboot`, `diskpart`, explicit confirmation**...
- **Cleanups are opt-in.** You may *suggest* cleanups freely, but only *execute* one after the user explicitly approves that specific action, and only show the exact command first.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No allowed-tools/permissions frontmatter field declared; nothing to flag as over-broad.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: terminal version: 0.1.0 audience: agent description: >- Use Desktop Commander for terminal and command-line work... ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan found no concealment characters or forged chat-template tokens.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(full file codepoint-scanned: no concealment codepoints or forged tokens found)
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Generic name 'terminal' with no brand resemblance or impersonation prose.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: terminal version: 0.1.0 audience: agent
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
No instruction directs reading credential stores or conversation context; the skill only advises NOT to echo secrets into terminal output. No harvest step exists.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
## Operating etiquette - Preview risky commands before running; run read-only inspection first. - Use absolute paths; respect `allowedDirectories`. - Never print secrets, tokens, or passwords into output; redact if they appear.
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
Single SKILL.md, no bundled scripts/hooks/env assignments; only directs the model to call already-registered host tools.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: terminal version: 0.1.0 audience: agent description: >- Use Desktop Commander for terminal and command-line work... ---
Does not applySkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
Static, bundled SKILL.md with no launch entry, no runtime fetch, no self-update mechanism of its own.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: terminal version: 0.1.0 audience: agent ---
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/cursor/skills/ai-tools-setupplugins/cursor/skills/ai-tools-setup100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
SKILL.md declares a skill to install/configure/validate Claude Desktop, MCP servers, OpenClaw, and Hermes using Desktop Commander's file/terminal tools. The body and the three reference files (claude-desktop-mcp.md, openclaw.md, hermes.md) describe exactly that: reading/editing config files, starting local processes, verifying installs. All network calls (npm install of named third-party packages, curl install scripts documented upstream) are disclosed, functionally tied to the stated install/configure purpose, and no credential exfiltration or hidden exec capability is present -- the skill explicitly instructs never to echo secrets.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers using Desktop Commander... Use when a user wants to install, configure, connect, getting MCP servers to connect, and diagnosing local AI tooling. Use when a user wants to install, configure, connect, validate, or fix Claude Desktop, an MCP server, an MCP config file, or a local AI agent/gateway. Trigger on the symptoms that actually bring people in: "Claude can't see my tools", "my MCP server isn't showing up", "tools aren't loading in Claude", "add an MCP
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
Body includes explicit transparency/disclosure rules rather than suppression: 'Translate errors, don't dump them' (formatting, not concealment of a privileged action), 'Never echo secrets... Redact secrets in any output, logs, or summaries you produce' (protects secrets from leaking, does not hide a state-changing action from the user), and 'Verify, then stop... Don't declare success on a guess.' No directive tells the agent to bypass confirmation or hide an action from the user.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
3. **Translate errors, don't dump them.** When a tool returns a raw JSON/HTTP error, explain in one line what it means... missing, deprecated, rate-limited, or doesn't support tool-calling, suggest a config or .env file* — never repeated back in chat... 5. **Back up before you edit.** Copy the config to a .bak first... 6. **Verify, then stop.** After a change, actually confirm it worked...
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
Credential handling here is applicable, not out of scope: the skill directs use of a user's already-configured provider API key for a single same-provider validation call, with explicit instructions never to echo or log the secret. The validation call and the key stay within the same provider relationship, so there is no third-party harvest-and-exfiltration pair, and the handling practices -- redaction and same-provider routing -- qualify as safe use of the credential.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
2. **Verify the model/API key works before you build.** The most common reason these sessions dead-end is a provider auth or billing error discovered *after* a lot of setup work. Make one tiny test call with `start_process` (a quick curl to the provider) early, and tell the user plainly if the key is invalid or out of credit.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No allowed-tools/scope/permissions field, no wildcard, no bundled executable or install-lifecycle hook. The skill's use of shell/process tools (start_process for curl test calls, config edits) is intrinsic and proportionate to its declared purpose of installing/configuring/repairing local AI tooling.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: ai-tools-setup version: 0.1.0 audience: agent description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers...
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan of SKILL.md and its three reference files found no concealment codepoints, forged chat-template tokens, HTML comments, or ANSI escapes. All content is descriptive setup/troubleshooting guidance.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(codepoint scan: no concealment codepoints, no forged chat-template tokens, no HTML comments found in this file)
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
SKILL.md and its reference files ship fully inside the bundle; no runtime fetch of the skill's own instructions, no self-mutation, no launch entry owned by this component.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: ai-tools-setup version: 0.1.0 audience: agent description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers...
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Name 'ai-tools-setup' is generic/descriptive, not a near-miss of any specific brand. Reference docs name real third-party tools (Claude Desktop, OpenClaw, Hermes) accurately and link to their genuine official docs (docs.openclaw.ai, hermes-agent.nousresearch.com) rather than impersonating them or pointing at look-alike domains; install commands target the real published packages (npm install -g openclaw, the official Hermes installer URL).
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
- **OpenClaw** — a self-hosted multi-channel gateway for AI agents... → Read `references/openclaw.md` - **Hermes** — disambiguate first: **Hermes Agent** (the Nous Research self-improving agent framework)...
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
This component ships only SKILL.md plus three reference markdown files (install documentation) -- no lifecycle hooks, entry-point code, dotenv/git-hooks, or exec-enabling / endpoint-substituting env assignments shipped by the skill itself. The documented third-party install commands (npm install -g openclaw, curl install.sh | bash) are install instructions for SEPARATE external tools the user runs manually, not a format-native primitive this artifact itself ships or auto-executes.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: ai-tools-setup version: 0.1.0 audience: agent description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/cursor/skills/computer-health-checkplugins/cursor/skills/computer-health-check100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
5 rules passed · 3 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Declared purpose is a read-only health check; actual content (collection scripts in references/*.md) are exclusively read-only diagnostics (df, ps, vm_stat, pmset, etc.) with cleanup commands gated on explicit user approval. No undisclosed network egress or credential access.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Run a comprehensive, read-only health check on the user's computer... it never needs sudo and only performs cleanups the user explicitly approves. ---
Show 1 more location
zsh -c ' echo "== SYSTEM =="; sw_vers; echo "uptime:"; uptime ... echo "== DONE =="'
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
The 'safety contract' section explicitly requires opt-in consent for every cleanup action ('only *execute* one after the user explicitly approves that specific action'). No disclosure-suppression or auto-approve language.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
## The safety contract (read this first) ... - **Cleanups are opt-in.** You may *suggest* cleanups freely, but only *execute* one after the user explicitly approves that specific action, and only show the exact command first.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No declared allowed-tools/permissions scope in frontmatter; the skill is explicitly scoped to read-only diagnostics with opt-in cleanup, matching its stated purpose with no broader grant.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent description: >- Run a comprehensive, read-only health check... ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan of SKILL.md and all 3 references/*.md found no concealment characters or forged tokens.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(full file + references/{macos,windows,linux}.md codepoint-scanned: no concealment codepoints or forged tokens found)PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Generic descriptive name 'computer-health-check', no brand resemblance or impersonation claims.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
The skill reads only local system metadata (disk, memory, battery, startup items), never credential stores or conversation context, and explicitly states nothing is sent off the machine. No harvest step exists.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
- **Privacy.** This reads local system metadata only. Don't send anything off the machine; the public-IP check is optional and should be labeled.
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
Only SKILL.md + references/*.md reference docs; the inline shell diagnostics are run through start_process (the host's normal post-decision tool invocation), not at load time, and no lifecycle hook or env-hijack exists.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
## Collection script (run as one batched process) Run this with `start_process` (timeout ~30s). It prints labeled sections you can parse.
Does not applySkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
Static, bundled SKILL.md + reference docs; no launch entry of its own (the plugins/cursor plugin manifest that carries the npx launch line is a sibling directory not part of this component's path and is outside the 7 components in scope for this scan).
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent ---
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/cursor/skills/desktop-commander-overviewplugins/cursor/skills/desktop-commander-overview100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 0 flagged
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/cursor/skills/knowledge-baseplugins/cursor/skills/knowledge-base100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
SKILL.md declares a Markdown knowledge-base creation/maintenance skill. Body content (index-first navigation, atomic notes, frontmatter schema, workflows using create_directory/write_file/edit_block/start_search) matches the declared scope exactly -- purely local file organization with no network egress, credential access, or undisclosed exec capability.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Create and maintain a Markdown knowledge base that any AI agent can read, search, and update... update notes, organize docs/notes for an agent or LLM to consume, build an index of notes, or run a cleanup/maintenance pass on an existing MD knowledge agent can navigate it without a vector database...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
No prose instructs the agent to skip confirmation, hide privileged steps, or assert authority over host/user policy. All file creates/edits are described as direct declared actions of the skill's core function (writing notes/index), not actions concealed from the user.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
## Workflows ### Create a new KB - **Normalize metadata**: read_multiple_files to audit properties... 2. `write_file` `INDEX.md` with the instructions block, an empty controlled-tag list, and an empty registry.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No allowed-tools/scope/permissions field, no wildcard, no bundled executable or install hook. Declared capability (local Markdown file organization) matches stated purpose exactly.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent description: >- Create and maintain a Markdown knowledge base...
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
One HTML comment exists ('<!-- extend deliberately -->') but it sits inside an illustrative fenced code-block example of a controlled-tag list, is purely a human-readable annotation with no host-directed imperative, and is not concealed from the rendered view (it is a normal visible markdown comment in example text, not a raw-vs-rendered mismatch). No zero-width/bidi codepoints, forged chat-template tokens, or ANSI escapes found.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
## Controlled tags `auth`, `security`, `billing`, `api`, `ops` <!-- extend deliberately -->
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
SKILL.md ships fully inside the bundle; no runtime instruction fetch, no self-mutation, no launch entry owned by this component.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent description: >- Create and maintain a Markdown knowledge base...
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Name 'knowledge-base' is a generic common term, not a near-miss of any specific branded product. No trust-implying prefix, version-spoofing suffix, or impersonation prose.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
No instruction text reads host credential stores or model conversation/system-prompt context; the skill only reads/writes local Markdown note files with no outbound channel at all. No harvest step exists.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
description: >- Create and maintain a Markdown knowledge base that any AI agent can read, search, and update...
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
This component ships only a single SKILL.md -- no bundled scripts, hooks, dotenv, or env assignments. No format-native execution/control-plane primitive exists to evaluate.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent description: >- Create and maintain a Markdown knowledge base...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/cursor/skills/obsidian-vaultplugins/cursor/skills/obsidian-vault100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
5 rules passed · 3 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Declared purpose (Obsidian vault organization) matches actual prose content exactly; only file read/edit ops via Desktop Commander's already-declared tools, scoped to the user's vault.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Organize Obsidian vaults with MOCs, wikilinks, frontmatter/properties, dashboards, orphan-note checks, and cleanup workflows. Use ONLY when the user explicitly refers to Obsidian...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
The skill's workflow includes state-changing edits and deduplication of vault notes, not merely passive organizational guidance. It remains SECURE because no instruction suppresses confirmation, hides changes, or directs the host to bypass approval before acting.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
(full file reviewed: no imperative directs the host to skip confirmation, hide an action from the user, or assert authority over host instructions)
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No declared allowed-tools/permissions scope; narrow documented purpose (Obsidian vault organization).
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent description: >- Organize Obsidian vaults with MOCs, wikilinks... ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan found no concealment characters or forged tokens.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(full file codepoint-scanned: no concealment codepoints or forged tokens found)
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Name 'obsidian-vault' legitimately refers to the real Obsidian product as a compatibility descriptor (a vault-organizing helper that triggers only when the user explicitly says 'Obsidian'), not a claim to BE Obsidian or an official Obsidian extension -- no impersonation prose, no trust-implying prefix, no version-spoofing suffix, and no install-instruction that replaces or spoofs the real Obsidian distribution channel.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: obsidian-vault description: >- Organize Obsidian vaults with MOCs, wikilinks, frontmatter/properties, dashboards, orphan-note checks, and cleanup workflows. Use ONLY when the user explicitly refers to Obsidian... ---
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
The skill only reads/edits the user's Obsidian vault notes; no credential store or conversation-context read, no outbound channel. No harvest step exists.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
(full file reviewed: no credential-store or conversation-context read instruction anywhere)
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
Single SKILL.md with no bundled executable, hook, or env assignment.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent description: >- Organize Obsidian vaults with MOCs, wikilinks... ---
Does not applySkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
Static, bundled SKILL.md; no launch entry of its own.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent ---
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/plugins/cursor/skills/terminalplugins/cursor/skills/terminal100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 0 flagged
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/skills/ai-tools-setupskills/ai-tools-setup100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Byte-identical in substance to plugins/cursor/skills/ai-tools-setup (only a markdown link-syntax difference). Same analysis applies: declared install/configure-AI-tooling purpose matches actual instruction content with no undisclosed network/credential/exec capability.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers using Desktop Commander — inspecting and fixing mcp.json / claude_desktop_config.json, getting MCP servers to connect, and diagnosing local AI tooling.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
Same content as plugins/cursor/skills/ai-tools-setup (only markdown link-syntax differs); same analysis: no consent-suppression or oversight-override prose present.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
4. **Never echo secrets.** API keys, tokens, and passwords go *into the right config or `.env` file* — never repeated back in chat. If a user pastes a live live secret, warn them, use it, and suggest they rotate it... 5. **Back up before you edit.** Copy the config to a `.bak` first...
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
The skill instructs a brief provider test call using the user's own supplied API key to confirm it works and has credit, which is a user-supplied credential read within this rule's scope. No credential-store harvesting occurs and no third-party exfiltration channel is instructed; the key is used only within the user's own existing provider relationship.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
2. **Verify the model/API key works before you build.** ... Make one tiny test call with `start_process` (a quick curl to the provider) early, and tell the user plainly if the key is invalid or out of credit.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
Same manifest/content as plugins/cursor/skills/ai-tools-setup; no wildcard/scope grant, no bundled executable or install hook; capability matches declared purpose.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: ai-tools-setup version: 0.1.0 audience: agent description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers...
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Same content as plugins/cursor/skills/ai-tools-setup; codepoint scan found no concealment codepoints, forged tokens, HTML comments, or ANSI escapes.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(codepoint scan: no concealment codepoints, no forged chat-template tokens, no HTML comments found in this file)
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
Same as plugins/cursor/skills/ai-tools-setup: ships fully inside the bundle, no runtime fetch, no self-mutation, no launch entry owned by this component.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: ai-tools-setup version: 0.1.0 audience: agent description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers...
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Identical to plugins/cursor/skills/ai-tools-setup: generic name, accurate references to real third-party tools with genuine official links/install commands, no impersonation.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: ai-tools-setup version: 0.1.0 audience: agent
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
Same as plugins/cursor/skills/ai-tools-setup: no bundled scripts/hooks/dotenv/env assignments shipped by this skill; documented third-party install commands are manual instructions, not an auto-executed primitive.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: ai-tools-setup version: 0.1.0 audience: agent description: >- Set up, connect, validate, and repair Claude Desktop and MCP servers...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/skills/computer-health-checkskills/computer-health-check100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
5 rules passed · 3 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Identical content to plugins/cursor and plugins/claude variants -- read-only diagnostics matching declared purpose, cleanup gated on consent.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Run a comprehensive, read-only health check on the user's computer... it never needs sudo and only performs cleanups the user explicitly approves. ---
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
Same safety contract as plugins/cursor variant: explicit opt-in approval required for every cleanup action.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
## The safety contract (read this first) ... - **Cleanups are opt-in.** You may *suggest* cleanups freely, but only *execute* one after the user explicitly approves that specific action...
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No declared allowed-tools/permissions scope; scoped to read-only diagnostics matching purpose.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent description: >- Run a comprehensive, read-only health check... ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan of SKILL.md and all 3 references/*.md found no concealment characters or forged tokens.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(full file + references/{macos,windows,linux}.md codepoint-scanned: no concealment codepoints or forged tokens found)PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Generic descriptive name, no brand resemblance.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
Identical content to the plugins/cursor variant: reads only local system metadata, explicitly no off-machine sends. No harvest step exists.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
- **Privacy.** This reads local system metadata only. Don't send anything off the machine; the public-IP check is optional and should be labeled.
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
Same as the plugins/cursor variant: diagnostics run via start_process (post-decision tool call), no lifecycle hook/env-hijack present.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
## Collection script (run as one batched process) Run this with `start_process` (timeout ~30s). It prints labeled sections you can parse.
Does not applySkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
Static, bundled SKILL.md + reference docs at repo root skills/ directory; no launch entry, no runtime fetch.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: computer-health-check version: 0.1.0 audience: agent ---
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/skills/desktop-commander-overviewskills/desktop-commander-overview100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Body content (workflow examples, tool inventory, conventions) is a straightforward elaboration of the declared description; no capability outside the stated MCP tool-usage guidance.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities ... --- # Desktop Commander MCP Desktop Commander gives the agent reach across the user's actual computer ...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
The workflow examples repeatedly instruct the agent to seek confirmation before risky actions ('Before running production-impacting SSH commands, explain the intended action and get user confirmation when the risk is non-trivial', 'Before deploys, restarts, migrations... summarize the action and confirm with the user', 'Preview the move plan before executing destructive ops'). This reinforces human-in-the-loop gates rather than weakening, skipping, or socially engineering around them. No instruction anywhere tells the agent to proceed without confirmation, hide an action from the user, or bypass a host guardrail.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
### "Debug this production issue"
Before running production-impacting SSH commands, explain the intended action and get user confirmation when the risk is non-trivial.
`start_process("ssh [REDACTED]", timeout_ms=...)` opens a long-lived SSH session ...
### "Deploy this to staging"
Before deploys, restarts, migrations, or other environment-changing commands, summarize the action and confirm with the user unless they already explicitly asked for that exact operation.PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
SKILL.md lines 82-84 use get_recent_tool_calls to retrieve recent tool-call arguments and outputs, so this rule is applicable to that behavior. The retrieved history remains in-session and no instruction pairs it with an outbound channel such as SSH or another network sink to export credentials or conversation context, so no exfiltration occurs.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
## Core tool inventory - **Process / shell:** `start_process`, `interact_with_process`, `read_process_output`, `list_processes`, `list_sessions`, `kill_process`, `force_terminate` - **Files (read/write):** `read_file`, `read_multiple_files`, `write_file`, `edit_block`, `write_pdf`
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
The SKILL.md frontmatter has no capability/permission manifest fields (name/description/version/audience only) — there is nothing to over-declare. The body's described capability breadth (filesystem, process, search) matches exactly the MCP tools it documents and is gated by the MCP server's own allowedDirectories/blockedCommands configuration, not by anything this skill grants itself.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities ... version: 0.1.0 audience: agent ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Programmatic scan of the full file for zero-width/bidi control characters (U+200B, U+200C, U+200D, U+202A, U+202E, U+2066, U+2069, U+FEFF) found none. Manual read of the full text found only plain-language workflow guidance, no hidden directives, ANSI escapes, or 'ignore previous instructions'-style covert text.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities — persistent shells and REPLs, long-running processes, filesystem beyond the workspace, structured files (.xlsx, .docx, .pdf, images) and large local data files such as CSVs, ripgrep search at scale, SSH, or cross-turn state. version: 0.1.0 audience: agent ---
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
This skill directory ships only a SKILL.md with no launch configuration or package-install instructions of its own; it references the MCP's tools by name only. The repo-level unpinned-launch concern (root .mcp.json) is evaluated separately against the mcp-server and plugins/cursor components that actually ship that manifest.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
## Core tool inventory Grouped index of the tools an agent reaches for most often. ...
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Skill name/branding ('desktop-commander-overview') matches the project's own product name consistently with package.json (@wonderwhy-er/desktop-commander), the repo README, and all plugin manifests (publisher 'Desktop Commander', homepage desktopcommander.app, repo wonderwhy-er/DesktopCommanderMCP). No typosquatting of a third-party name or spoofed publisher identity.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: desktop-commander-overview description: Use for Desktop Commander MCP capabilities ... ---
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
This skill ships only a SKILL.md file; its persistent shell sessions are ordinary composed tool calls, not format-native out-of-band execution, entry-point hooks, or durable control-plane persistence mechanisms, so the subject matter of this rule is not present here.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
## Example workflows Each example names the actual tool sequence. ...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/skills/knowledge-baseskills/knowledge-base100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Identical to plugins/cursor/skills/knowledge-base (diff -rq confirms byte-identical directory contents). Declared knowledge-base maintenance purpose matches actual content with no undisclosed capability.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Create and maintain a Markdown knowledge base that any AI agent can read, search, and update. Use when the user wants to start a knowledge base...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
Identical content to plugins/cursor/skills/knowledge-base; no consent-suppression prose present.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
## Workflows ### Create a new KB - **Normalize metadata**: `read_multiple_files` to audit properties...
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
Identical to plugins/cursor/skills/knowledge-base; no wildcard/scope grant, no bundled executable or install hook; capability matches declared purpose.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent description: >- Create and maintain a Markdown knowledge base...
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Identical to plugins/cursor/skills/knowledge-base: the one HTML comment present is a benign, visible example annotation ('<!-- extend deliberately -->') inside a fenced controlled-tag-list example, not a concealed directive. No concealment codepoints, forged tokens, or ANSI escapes found.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
## Controlled tags `auth`, `security`, `billing`, `api`, `ops` <!-- extend deliberately -->
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
Identical to plugins/cursor/skills/knowledge-base: ships fully inside the bundle, no runtime fetch, no self-mutation, no launch entry owned by this component.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent description: >- Create and maintain a Markdown knowledge base...
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Identical to plugins/cursor/skills/knowledge-base: generic name, no impersonation.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
No instruction text reads host credential stores or conversation/system-prompt context; purely local note-file organization with no outbound channel. No harvest step exists.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
description: >- Create and maintain a Markdown knowledge base that any AI agent can read, search, and update...
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
This component ships only a single SKILL.md -- no bundled scripts, hooks, dotenv, or env assignments. No format-native execution/control-plane primitive exists to evaluate.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: knowledge-base version: 0.1.0 audience: agent description: >- Create and maintain a Markdown knowledge base...
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/skills/obsidian-vaultskills/obsidian-vault100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Declared purpose matches actual content -- Obsidian vault organization workflows only, no undisclosed capability.
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
description: >- Organize Obsidian vaults with MOCs, wikilinks, frontmatter/properties...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
Purely organizational guidance; no confirmation flow to suppress and no authority-override prose.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
(full file reviewed: no imperative directs the host to skip confirmation, hide an action from the user, or assert authority over host instructions)
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No declared allowed-tools/permissions scope; narrow documented purpose.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent description: >- Organize Obsidian vaults with MOCs, wikilinks... ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Codepoint scan found no concealment characters or forged tokens.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
(full file codepoint-scanned: no concealment codepoints or forged tokens found)
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
The skill consists solely of a bundled SKILL.md describing the model-facing instruction trust surface, with no live-fetch of instructions, launcher, or self-mutation capability present in the file, consistent with a declarative artifact that does not fetch or mutate its own instructions.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent ---
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Same as plugins/cursor variant: legitimate compatibility descriptor for the real Obsidian product, no impersonation claim or spoofed install channel.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: obsidian-vault description: >- Organize Obsidian vaults with MOCs, wikilinks, frontmatter/properties... ---
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
Only reads/edits the user's Obsidian vault notes; no credential or conversation-context harvest, no outbound channel.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
(full file reviewed: no credential-store or conversation-context read instruction anywhere)
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
Single SKILL.md with no bundled executable, hook, or env assignment.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
--- name: obsidian-vault version: 0.1.0 audience: agent description: >- Organize Obsidian vaults with MOCs, wikilinks... ---
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Skillskills-plugins/skills/terminalskills/terminal100StrongNone flagged
What stood out
- start_process/interact_with_process spawn a real OS shell for agent command strings with only a configurable command-name denylist (CommandManager) as a gate; default config grants unrestricted filesystem access (allowedDirectories: []) and the denylist covers ~30 names leaving arbitrary interpreters/binaries reachable -- SECURITY.md documents this as intentional but it is not a hard security boundary (ATR-C0003/ATR-M0002, CRITICAL).
- Unrestricted URL fetch in the read_file tool (isUrl=true) reaches fetch() with no RFC1918/loopback/link-local/cloud-metadata IP filtering -- classic SSRF surface reachable by any MCP client or prompt-injected agent (ATR-C0001, HIGH).
- No host-enforced envelope, control-character/ANSI/zero-width sanitization, or untrustedReturn annotation on tools returning externally-sourced text (read_file incl. URL fetch, read_process_output, start_search) -- prompt-injection containment gap (ATR-C0013, HIGH).
- Known HIGH-severity DoS CVE (CVE-2026-111758) in pinned markdown-it@14.3.2 production dependency used by the shipped file-preview markdown renderer (ATR-C0007, HIGH).
- All shipped MCP launch configurations (root .mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) launch Desktop Commander via unpinned
npx -y @wonderwhy-er/desktop-commander@latestor with no version at all -- a post-approval rug-pull surface where a compromised npm publish silently changes what every installed client launches with no re-consent (ATR-P0007/ATR-M0001, HIGH). - The ai-tools-setup skill's bundled reference docs instruct piping a remote install script directly into bash (curl | bash) and installing an unpinned npm package (@latest) for third-party agent tooling (Hermes, OpenClaw) with no integrity verification step (ATR-P0007, HIGH).
- Desktop Commander registers common unprefixed tool names (read_file, write_file, create_directory, list_directory, move_file, get_file_info) that collide exactly with the official MCP reference filesystem server's tool names with no namespace/disambiguation guard (ATR-M0006, MEDIUM).
- Dockerfile has no USER directive (container runs as root) and no HEALTHCHECK when deployed via the Smithery/Docker path (ATR-C0006, MEDIUM).
Skill rules 0 of 8 flagged
6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details
Why it got this result
Body is a terminal-usage guide consistent with the declared description; no undeclared capability (e.g. no hidden network exfil or persistence instructions).
Why it matters
Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.
Evidence
--- name: terminal ... description: >- Use Desktop Commander for terminal and command-line work ... --- # Terminal Command Assistant
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details
Why it got this result
Section 7 ('Safety: destructive commands and chaining') explicitly instructs the agent to treat a long list of operations as 'destructive — never run without the user's explicit confirmation', to show the exact command and wait for a clear 'yes', and to avoid chaining a destructive step so a failure can't cascade past confirmation. No suppression language found.
Why it matters
Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.
Evidence
## 7. Safety: destructive commands and chaining Desktop Commander already refuses a built-in `blockedCommands` list ... Beyond that list, treat these as **destructive — never run without the user's explicit confirmation**, even if they'd technically succeed: ... For these: show the exact command, explain in one line what it will irreversibly do, and wait for a clear "yes".
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details
Why it got this result
No permission/capability manifest field exists in frontmatter; scope described in the body (terminal command usage) matches the declared description exactly.
Why it matters
An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.
Evidence
--- name: terminal version: 0.1.0 audience: agent description: >- Use Desktop Commander for terminal and command-line work ... ---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details
Why it got this result
Programmatic scan for hidden/bidi unicode control characters found none; no concealed directives in the visible text.
Why it matters
A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.
Evidence
--- name: terminal ... description: >- Use Desktop Commander for terminal and command-line work ... ---
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details
Why it got this result
The skill mentions npm/pnpm/yarn installs in its prose, but these are ordinary user-requested terminal tasks rather than a self-owned launch entry or instruction-update mechanism. The skill ships no updater or self-mutation capability, so it remains free of rug-pull risk.
Why it matters
A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.
Evidence
--- name: terminal ... --- # Terminal Command Assistant
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details
Why it got this result
Generic, accurately-scoped skill name ('terminal') consistent with the project's own branding; no impersonation of a third-party product.
Why it matters
Impersonation is the entry point of the agentic supply chain (OWASP LLM ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.
Evidence
--- name: terminal ... --- # Terminal Command Assistant
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details
Why it got this result
The skill's secret redaction warnings and generic cloud CLI guidance do not direct or implement raw credential-store reads, conversation-context dumps, or memory routing; the environment inspection shown is limited to non-secret config, systemInfo, and HOME examples. The harvest-and-exfiltrate scope this rule targets does not apply here.
Why it matters
A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.
Evidence
## Operating etiquette - Preview risky commands before running; run read-only inspection first. - Use absolute paths; respect `allowedDirectories`. - Never print secrets, tokens, or passwords into output; redact if they appear.
Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details
Why it got this result
Only SKILL.md is bundled, with no native loader, hook, dotenv, or persistent host-instruction write shipped by the skill itself. The curl-to-shell passage warns the agent to confirm before piping remote content into a shell rather than directing shipped out-of-band execution; script creation is user-requested and disclosed. This rule's subject does not apply to this bundle.
Why it matters
Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.
Evidence
- **curl / HTTP**: fine for inspection (`curl -i https://...`). Be careful with anything that pipes a downloaded script straight into a shell (`curl ... | sh`) — that's untrusted code execution; show it and confirm first.
Before you put it to work
- Add SSRF protections to readFileFromUrl: resolve and reject loopback/RFC1918/link-local/metadata IP ranges and restrict schemes before fetching caller-supplied URLs.
- Change default allowedDirectories from '[] = full disk' to a narrow explicit default, and move start_process filtering from a denylist toward an opt-in allowlist model; gate set_config_value widening of allowedDirectories/blockedCommands behind a step the model alone cannot trigger.
- Pin all shipped MCP launch configs (.mcp.json, plugins/claude/.claude-plugin/plugin.json, plugins/cursor/.cursor-plugin/plugin.json, gemini-extension.json) to an exact @wonderwhy-er/desktop-commander version or digest instead of @latest or unversioned references, and require a version bump plus re-consent on upgrade.
- Wrap all model-facing tool text output in a host-enforced envelope and strip ANSI/zero-width/bidi/control characters and fake role markers; flag URL-fetching and process-output tools as untrusted-return.
- Bump markdown-it to >=14.3.3 (or 15.0.3) and regenerate package-lock.json; wire Trivy/npm-audit into CI.
- In the ai-tools-setup skill's reference docs, replace curl|bash and unpinned npm installs with versioned installs and a checksum/signature verification step before executing any downloaded installer.
- Qualify Desktop Commander's filesystem tool names with a vendor/server prefix to avoid silent collision with the official MCP reference filesystem server when both are connected to the same client.
- Add
USER nodeand a HEALTHCHECK to the Dockerfile. - Consider adding explicit allowed-tools frontmatter scoping to each SKILL.md so the host's own tool-gating mechanism, not just instruction text, enforces least privilege per skill.
Repository-wide rules
Checked once for the whole repository: dependencies, secrets, inputs, networking, CI and more. 8 of 15 flagged.
6 rules passed · 1 did not applyShow
PassedWeaponization: Malicious-Purpose Artifact (Offensive Capability by Design)ATR-R0001Details
Why it got this result
Desktop Commander's declared purpose (README.md, SKILL.md files, package metadata) is a general-purpose developer-productivity MCP server giving an LLM agent filesystem, terminal, document-editing and search capabilities on the user's own machine -- there is no offensive/malicious framing anywhere in its name, description, or instruction bodies, and no bundled operational attack tooling (no malware/ransomware/credential-harvester/C2/exploit-generation code or directives). The YARA 'Zeus_Metadata_Consent_Suppression' hits on skills/desktop-commander-overview/SKILL.md (and its plugin copies) were manually reviewed: the file's actual text explicitly instructs the agent to 'explain the intended action and get user confirmation when the risk is non-trivial' before production-impacting SSH/deploy commands -- i.e. it tells the agent to SEEK confirmation, the opposite of suppressing it -- and no zero-width/hidden Unicode or concealed instructions were found in the file's bytes. This is a dual-use local-system-access tool used defensively/productively, not a weapon by design.
Why it matters
An artifact whose purpose is to weaponize the agent -- to produce malware, steal credentials, evade defenses, or run attacks at scale -- is dangerous regardless of how securely it is built; posture controls (auth, input validation, least privilege) cannot make a weapon safe, which is why a confirmed weaponization finding caps the trust verdict to `blocked` rather than being averaged against a good posture score. Catching malicious PURPOSE is a distinct safety control, and it must be conservative because offensive capability is dual-use: legitimate pentest, CTF, defensive, and research work must abstain (UNKNOWN), never block -- unless the artifact itself hides from the people who install, run or audit it.
Evidence
### "Debug this production issue" Before running production-impacting SSH commands, explain the intended action and get user confirmation when the risk is non-trivial. ... ### "Deploy this to staging" Before deploys, restarts, migrations, or other environment-changing commands, summarize the action and confirm with the user unless they already explicitly asked for that exact operation.
PassedMissing or Inadequate Input ValidationATR-C0008Details
Why it got this result
Every MCP tool's arguments are validated through a dedicated Zod schema before use (src/tools/schemas.ts defines 26 distinct *ArgsSchema definitions, e.g. ReadFileArgsSchema, StartProcessArgsSchema, EditBlockArgsSchema, MoveFileArgsSchema), each enforcing types, required fields, and defaults, and handlers call <Schema>.parse(args)/safeParse(args) and return a structured error response (isError:true) on failure rather than passing raw/untyped input to filesystem or process sinks.
Why it matters
Frontline control to prevent multiple classes of vulns.
Evidence
export const ReadFileArgsSchema = z.object({
path: z.string(),
isUrl: z.boolean().optional().default(false),
offset: z.number().optional().default(0),
length: z.number().optional().default(1000),
...
});Show 1 more location
const parsed = StartProcessArgsSchema.safeParse(args);
if (!parsed.success) {
capture('server_start_process_failed');
return {
content: [{ type: "text", text: `Error: Invalid arguments for start_process: ${parsed.error}` }],
isError: true,
};
}PassedInsecure DeserializationATR-C0009Details
Why it got this result
Beyond JSON.parse, PDF front-matter parsing uses the gray-matter library, which supports pluggable per-language engines, including 'js' and 'javascript' aliases that would execute arbitrary code if enabled. safeGrayMatterOptions forcibly overrides both the 'js' and 'javascript' engine entries after any caller-supplied options are merged in, so those code-executing engines can never be reactivated by caller input. No deserialization path in the codebase -- JSON.parse, or gray-matter with its JS engines disabled -- can execute attacker-controlled data as code.
Why it matters
Can lead to arbitrary code execution.
Evidence
// JSON.parse rejects although the config is complete. ... return JSON.parse(text.charCodeAt(0) === 0xfeff ? text.slice(1) : text);
PassedPath Traversal & Symlink Bypass (Filesystem Tools)ATR-C0011Details
Why it got this result
Every filesystem tool entry point routes through the single validatePath() choke point (src/tools/filesystem.ts), which resolves symlinks link-by-link (resolveRealPath, ELOOP-guarded) and enforces containment against allowedDirectories by real path, closing the 'allowed dir reached through a symlink' bypass. This containment only holds when allowedDirectories is configured to a restricted set of directories: an empty list or a root entry of / is an intentional configuration that permits all paths by design, not a bypass of the check itself. The PDF-rendering HTTP server used for document preview is independently gated: served PDFs are protected by a random per-render cookie and the server listens only on loopback, a separate containment layer from validatePath rather than a weakness in it. list-directory's symlink-type stat during the walk can determine whether an entry is a directory or file before the per-entry validatePath re-check runs, but it does not read or return out-of-scope file content.
Why it matters
Traversal exposes arbitrary host files; can lead to code execution.
Evidence
const target = await fs.readlink(candidate, { encoding: 'utf8' });
return resolveFrom(path.resolve(resolvedParent, target));
};
return resolveFrom(absolutePath);Show 2 more locations
const expandedPath = expandHome(requestedPath);
// Convert to absolute path
const absoluteOriginal = path.isAbsolute(expandedPath)
? path.resolve(expandedPath)
: path.resolve(process.cwd(), expandedPath);async function resolveRealPath(absolutePath: string): Promise<string> {
let linksFollowed = 0;
const resolveFrom = async (currentPath: string): Promise<string> => {
try {
return await fs.realpath(currentPath, { encoding: 'utf8' });
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
}
const parent = path.dirname(currentPath);
if (parent === currentPath) { return currentPath; }
const resolvedParent = await resolveFrom(parent);
const candidate = path.join(resolvedParent, path.basename(currentPath));
let isLink: boolean;
try {
…+9 more locations in the finding JSON
PassedCI/CD Workflow Script InjectionATR-C0014Details
Why it got this result
The repo ships exactly two workflows. codespell.yml runs on push/pull_request with permissions: contents: read and invokes three mutable, version-tagged third-party actions (not pinned by commit SHA) with no shell interpolation of event data, so there is no script-injection surface even though the action references are mutable. release.yml is triggered only by tag-push (v*, test-v*) and workflow_dispatch, both of which require write access to the repository; every run: block consumes ${{ steps.*.outputs.* }} / ${{ needs.*.outputs.* }} or env:-indirected values rather than interpolating github.event.*/github.head_ref directly into a run block. The tag-format case-statement patterns (e.g. v[0-9]*.[0-9]*.[0-9]*) are permissive shell globs rather than strict SemVer validation, but they are used only as branch-selection matches, not re-parsed as shell code, so they do not create an injection path. No pull_request_target, issue_comment, or repository_dispatch triggers are present.
Why it matters
CI/CD script injection can execute attacker-controlled commands with workflow tokens, repository secrets, trusted-runner access, artifact or cache authority, and release or deployment privileges.
Evidence
on:
push:
tags: ['v*', 'test-v*']
workflow_dispatch:
inputs:
bump:
description: 'Cut a NEW release from main with this version bump'
required: false
type: choice
options: ['none', 'patch', 'minor', 'major']Show 1 more location
- name: Resolve release mode
id: mode
env:
INPUT_TAG: ${{ inputs.tag }}
INPUT_BUMP: ${{ inputs.bump }}
INPUT_REHEARSAL: ${{ inputs.rehearsal }}
EVENT: ${{ github.event_name }}
run: |
if [ "$EVENT" = "push" ]; then
MODE="tag"; TAG="$GITHUB_REF_NAME"
elif [ -n "$INPUT_TAG" ] && [ -n "${INPUT_BUMP:-none}" ] ...PassedLack of Output Encoding/SanitizationATR-C0010Details
Why it got this result
The one place this MCP renders untrusted content as HTML (the file-preview UI widget, src/ui/file-preview) is deliberately hardened: raw HTML files are rendered inside a nested sandbox="allow-scripts allow-forms allow-popups" iframe (no allow-same-origin) with the whole frame document HTML-escaped into the srcdoc attribute via escapeHtml(), so injected markup cannot break out of the attribute, and any script that does run is isolated to a null/opaque origin under the app's CSP. Markdown rendering explicitly disables markdown-it's raw-HTML passthrough (html: false in createMarkdownIt), so embedded HTML in a rendered markdown file is escaped, not executed.
Why it matters
Prevents UI-level injection and operator compromise.
Evidence
function renderSandboxedHtmlFrame(content: string): string {
const palette = resolveThemeFrameStyles();
const frameDocument = `<!doctype html>...`;
return `<iframe class="html-rendered-frame" title="Rendered HTML preview" sandbox="allow-scripts allow-forms allow-popups" referrerpolicy="no-referrer" srcdoc="${escapeHtml(frameDocument)}"></iframe>`;
}Show 1 more location
export function createMarkdownIt(options: {
highlight?: (code: string, language: string) => string;
} = {}): MarkdownItInstance {
return new MarkdownItCtor({
html: false,Does not applyDenial of Wallet / Cost ControlsATR-C0012Details
Why it got this result
Desktop Commander has no billable external API dependencies in its tool-invocation paths: it operates on the local filesystem/OS processes, and its only outbound network calls (feature-flag fetch, anonymous telemetry POST, optional remote-device Supabase realtime channel) are free, low-frequency, operator-run endpoints with 3s timeouts and no retry storms -- not metered per-call paid services the agent's tool use could run up a bill against. This matches the rule's own false_positive_guard: 'No billable external dependencies.'
Why it matters
Prevents cost-based disruption.
Evidence
const FETCH_TIMEOUT_MS = 3000; const controller = new AbortController(); const abortTimeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
You know what it is. Now see what it does.
A scan helps you understand what you’re building with. When your agent runs, BlueRock shows you what actually happens across tools and systems. Follow actions in context, understand behavior as it unfolds and add controls when you need them.
Results reflect the code at the commit and date shown.