Back to skill

Security audit

YNote Clip

Security checks for vulnerabilities and agentic risk

Overview

The skill performs webpage clipping, but it has under-scoped access to private page content, broad note tools, remote credential-bearing endpoints, and unsafe helper scripts that users should review before installing.

Install only if you are comfortable with the skill opening pages in the OpenClaw browser profile, extracting full page or chat content, and uploading it plus images to Youdao's MCP endpoint. Avoid clipping private chat, account, intranet, or sensitive authenticated pages until the parser is narrowed, endpoint and private-network protections are added, the hard-coded Apify token is removed, and temporary-file and HTML-sanitization issues are fixed.

Vulnerability Patterns
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (8)

T04 · Embedded Malicious Code

Error
Location
static/inject-sdk.fn.js:1
Finding
Encoded Runtime Parser Extracts Authenticated AI-Chat Conversations<![CDATA[ ## Vulnerability Details **File Location**: `static/inject-sdk.fn.js:1`; decoded equivalent in `static/collect-window.js:1` **Vulnerability Type**: Embedded encoded code with undisclosed sensitive-data extraction behavior **Risk Level**: Critical ### Vulnerable Code ```javascript (() => { const s = document.createElement('script'); s.textContent = atob('dmFyIGNvbGxlY3RQYXJzZXI7KCgpPT57...'); // The decoded script is inserted into the visited page. })(); ``` The decoded bundle registers dedicated extractors for authenticated AI-chat services: ```javascript [ { name: "Deepseek", url: "https://chat.deepseek.com/", getContent: getDeepseekContent }, { name: "腾讯元宝", url: "https://yuanbao.tencent.com/chat", getContent: getYuanbaoContent }, { name: "通义千问", url: "https://tongyi.aliyun.com/", getContent: getTongyiContent }, { name: "kimi", url: "https://kimi.moonshot.cn/", getContent: getKimiContent }, { name: "doubao", url: "https://www.doubao.com/", getContent: getDoubaoContent }, { name: "yiyan", url: "https://yiyan.baidu.com/", getContent: getYiyanContent } ] ``` Representative decoded extraction behavior includes reading user questions and model responses from the page DOM: ```javascript t.push({ type: "question", value: a.innerText }); t.push({ value: m ? m.innerHTML : "", title: l, content: s, type: "answer" }); ``` ### Technical Analysis The Skill hides a large executable parser in Base64, decodes it at runtime using `atob`, and executes it as a script in the context of the visited page. Although Base64 is not encryption, this packaging significantly reduces reviewability and conceals the effective behavior from ordinary inspection. The decoded parser is broader than a conventional article extractor. It contains site-specific code for extracting conversations from multiple AI-chat applications. When the managed browser has an authen ...[truncated 1403 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the Base64 decode-and-execute mechanism and ship readable, reviewable source code. 2. Remove AI-chat-specific extractors unless private-chat clipping is an explicitly declared feature. 3. Require explicit, informed confirmation before extracting content from authenticated or private pages. 4. Display the exact origin and content category that will be uploaded. 5. Limit the parser to user-selected article containers rather than complete conversation histories. 6. Add automated tests that prevent extraction from account, messaging, email, banking, and private-chat origins by default. 7. Publish the provenance and integrity hash of any bundled parser library. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
twitter-apify.mjs:24
Finding
Hard-Coded Apify API Token Exposes a Reusable Service Credential<![CDATA[ ## Vulnerability Details **File Location**: `twitter-apify.mjs:24`, `twitter-apify.mjs:104-105`, `twitter-apify.mjs:124`, `twitter-apify.mjs:153-154`, `twitter-apify.mjs:279` **Vulnerability Type**: Hard-coded secret and credential transmission in URL query parameters **Risk Level**: High ### Vulnerable Code ```javascript const ACTOR_ID = 'nfp1fpt5gUlBwPcor'; const DEFAULT_APIFY_TOKEN = 'apify_api_vsVgnrJKGDCfhfTil8FpBaMaM4vexW1TZocw'; ``` ```javascript async function startApifyRun(token, tweetUrl) { const input = { startUrls: [tweetUrl], maxItems: 1, }; const url = `https://api.apify.com/v2/acts/${ACTOR_ID}/runs?token=${token}`; return await httpsRequest(url, { method: 'POST', body: input, timeout: 30, }); } ``` ```javascript async function waitForResult(token, runId, maxAttempts = MAX_POLL_ATTEMPTS) { const url = `https://api.apify.com/v2/actor-runs/${runId}?token=${token}`; // ... } async function fetchDataset(token, datasetId) { const url = `https://api.apify.com/v2/datasets/${datasetId}/items?token=${token}&clean=true`; // ... } ``` ```javascript const token = process.env.APIFY_API_TOKEN || DEFAULT_APIFY_TOKEN; ``` ### Technical Analysis A reusable Apify credential is embedded directly in the distributed source and is automatically used whenever `APIFY_API_TOKEN` is absent. Anyone with access to the Skill package can recover and reuse the token. The token is also placed in query strings. Query parameters are more likely than authorization headers to be retained in request logs, monitoring systems, reverse-proxy logs, browser or debugging output, and error reports. ### Attack Path 1. An attacker downloads or otherwise obtains the Skill package. 2. The attacker reads `twitter-apify.mjs`. 3. The attacker extracts the embedded Apify token. 4. The attacker invokes Apify APIs using the credential. 5. Available quotas, actor runs, datasets, or account resources are abused to the extent allo ...[truncated 420 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke the exposed token immediately and rotate any related credentials. 2. Remove `DEFAULT_APIFY_TOKEN` entirely. 3. Require `APIFY_API_TOKEN` to be supplied through an approved secret manager or protected process environment. 4. Fail safely when no token is configured. 5. Use an Apify-supported authorization header instead of a URL query parameter. 6. Scope the replacement token to only the actor and operations required by this Skill. 7. Apply spending limits, rate limits, and monitoring to the Apify account. 8. Add secret scanning to the release pipeline and repository history. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
static/parse-page.fn.js:5
Finding
Page-Controlled Image URLs Enable Server-Side Request Forgery and Data Relay<![CDATA[ ## Vulnerability Details **File Location**: `static/parse-page.fn.js:5-15`; `clip-note.mjs:274-282`, `clip-note.mjs:295-341`, `clip-note.mjs:446-452`, `clip-note.mjs:619-632` **Vulnerability Type**: SSRF through unrestricted image retrieval **Risk Level**: High ### Vulnerable Code ```javascript const imgs = div.querySelectorAll('img'); const imageUrls = Array.from(imgs) .map(img => img.getAttribute('src')) .filter(src => src && !src.startsWith('data:')) .map(src => { try { return new URL(src, document.baseURI).href; } catch (e) { return null; } }) .filter(Boolean); ``` ```javascript async function fetchImageViaFetch(url, referer, timeoutMs) { const res = await fetch(url, { headers: { 'Referer': referer, 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', }, signal: AbortSignal.timeout(timeoutMs), redirect: 'follow', }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const buffer = Buffer.from(await res.arrayBuffer()); const contentType = res.headers.get('content-type')?.split(';')[0]?.trim() || ''; return { buffer, contentType }; } ``` The fallback deliberately bypasses the system resolver: ```javascript const digOut = execFileSync( 'dig', [hostname, '@8.8.8.8', '+short'], { encoding: 'utf8', timeout: 5_000 } ); const result = execFileSync('curl', [ '--max-time', '15', '--resolve', `${hostname}:${port}:${ip}`, '-A', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', '-H', `Referer: ${referer}`, '-L', '-s', '-o', '-', url, ], { timeout: 20_000 }); ``` Retrieved bytes are subsequently prepared for upload: ```javascript const base64Data = finalBuf.toString('base64'); images.push({ url, mimeType: finalMime, data: base64Data }); ``` ### Technical Analysis Image destinations originate from HTML controlled by the visited page. The Skill accepts any URL that can be constructed by `new ...[truncated 1665 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Allow only `https:` image URLs unless a narrowly defined exception is required. 2. Resolve each hostname and reject loopback, private, link-local, reserved, multicast, and unspecified addresses for IPv4 and IPv6. 3. Repeat destination validation after every redirect. 4. Disable automatic redirects or enforce a strict redirect limit with revalidation. 5. Restrict image downloads to the source page's origin or an explicit host allowlist. 6. Remove the Google DNS and `curl --resolve` bypass. 7. Verify the response `Content-Type` against a strict image allowlist. 8. Validate the actual file signature rather than trusting the URL or response header. 9. Apply response-size limits while streaming, rather than after downloading the entire body. 10. Run the downloader in a network sandbox with no access to private networks or metadata services. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
collect-page.sh:45
Finding
Output Path Injection Allows Arbitrary JavaScript Execution<![CDATA[ ## Vulnerability Details **File Location**: `collect-page.sh:20-21`, `collect-page.sh:45-51` **Vulnerability Type**: Shell-controlled data interpolated into `node -e` source code **Risk Level**: High ### Vulnerable Code ```bash TARGET_URL="${1:?用法: bash collect-page.sh <URL> [DATA_FILE]}" DATA_FILE="${2:-/tmp/ynote-clip-data.json}" ``` ```bash node -e " const raw = JSON.parse( require('fs').readFileSync('$DATA_FILE.raw','utf-8') ); const d = raw.result ?? raw; require('fs').writeFileSync('$DATA_FILE', JSON.stringify(d)); console.log(JSON.stringify({ title: d.title, imageCount: (d.imageUrls||[]).length, source: d.source, contentLength: (d.content||'').length })); " ``` ### Technical Analysis The optional `DATA_FILE` argument is inserted directly into JavaScript source enclosed in single quotes inside the `node -e` program. Shell quoting of the outer command does not make this safe for JavaScript string construction. A path containing a single quote and JavaScript syntax can terminate the intended string literal and inject arbitrary code. The injected program executes with the same filesystem, environment, and process privileges as the Skill. This is distinct from ordinary shell command injection: the attacker-controlled value is interpreted by the JavaScript parser after shell expansion. ### Attack Path 1. An attacker gains control over or influences the second argument passed to `collect-page.sh`. 2. The supplied value contains a quote that closes the JavaScript string and appends JavaScript statements. 3. The shell expands `$DATA_FILE` into the `node -e` source. 4. Node parses and executes the injected statements. 5. The attacker can invoke Node APIs to read files, write files, execute child processes, or access environment variables. ### Impact Assessment Successful exploitation provides arbitrary code execution under the account running the Skill. This can expose API keys in the process environment, read ac ...[truncated 309 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Pass the path as a data argument rather than embedding it in source: ```bash node - "$DATA_FILE.raw" "$DATA_FILE" <<'NODE' const fs = require('fs'); const rawPath = process.argv[2]; const outputPath = process.argv[3]; const raw = JSON.parse(fs.readFileSync(rawPath, 'utf8')); const d = raw.result ?? raw; fs.writeFileSync(outputPath, JSON.stringify(d), { mode: 0o600 }); console.log(JSON.stringify({ title: d.title, imageCount: (d.imageUrls || []).length, source: d.source, contentLength: (d.content || '').length })); NODE ``` Additionally: 1. Reject output paths outside a dedicated per-run directory. 2. Resolve and validate paths using `realpath`. 3. Reject symbolic links. 4. Do not expose the output-path option to untrusted agent-generated arguments unless necessary. 5. Add tests using quotes, newlines, backslashes, and JavaScript metacharacters. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
collect-page.sh:21
Finding
Predictable Shared Temporary Files Permit Data Exposure and Symlink Clobbering<![CDATA[ ## Vulnerability Details **File Location**: `collect-page.sh:21`, `collect-page.sh:42-55`; `twitter-apify.mjs:35`, `twitter-apify.mjs:316` **Vulnerability Type**: Unsafe predictable temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```bash DATA_FILE="${2:-/tmp/ynote-clip-data.json}" ``` ```bash $BROWSER evaluate \ --fn "$(cat "$SCRIPT_DIR/static/parse-page.fn.js")" \ >"$DATA_FILE.raw" node -e " const raw = JSON.parse( require('fs').readFileSync('$DATA_FILE.raw','utf-8') ); const d = raw.result ?? raw; require('fs').writeFileSync('$DATA_FILE', JSON.stringify(d)); // ... " rm -f "$DATA_FILE.raw" ``` The Twitter path uses the same predictable default: ```javascript const result = { url: null, output: '/tmp/ynote-clip-data.json', timeout: 120, }; ``` ```javascript fs.writeFileSync(args.output, JSON.stringify(ynoteData, null, 2)); ``` ### Technical Analysis Potentially private page content is written to fixed paths in a globally shared temporary directory. Neither implementation creates the final data file with exclusive creation semantics, verifies that the path is not a symbolic link, nor assigns an explicit restrictive mode. The final `/tmp/ynote-clip-data.json` file is not removed by the documented workflow. Concurrent runs also use the same path and can overwrite or consume one another's content. On systems where another local user or compromised process can manipulate the shared temporary directory, predictable filenames create a time-of-check/time-of-use and symlink-clobbering risk. ### Attack Path 1. A local attacker predicts `/tmp/ynote-clip-data.json` or `/tmp/ynote-clip-data.json.raw`. 2. The attacker creates a symbolic link at one of those paths or monitors the path for creation. 3. The Skill writes extracted webpage or conversation content. 4. The attacker reads the resulting data or redirects the write to another file writable by the Skill account. 5. Alternatively, two concurrent clip ...[truncated 463 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a unique per-run directory using `mktemp -d`. 2. Set `umask 077` before creating files. 3. Create files with exclusive flags and mode `0600`. 4. Reject symbolic links and validate file ownership. 5. Pass the generated path between workflow stages instead of relying on a fixed global path. 6. Delete both raw and final files in a guaranteed cleanup handler. 7. Use atomic rename from a securely created file when finalization is required. 8. Prevent concurrent runs from sharing state. 9. For Node.js, use exclusive creation such as: ```javascript fs.writeFileSync(path, data, { flag: 'wx', mode: 0o600 }); ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
clip-note.mjs:59
Finding
Configurable MCP Endpoint Can Receive API Credentials and Private Content<![CDATA[ ## Vulnerability Details **File Location**: `clip-note.mjs:59-60`, `clip-note.mjs:137-146`, `clip-note.mjs:199-204`; `mcp-call.sh:21-24`, `mcp-call.sh:37-41`, `mcp-call.sh:57-59` **Vulnerability Type**: Unrestricted credential-bearing endpoint override and optional plaintext transport **Risk Level**: Medium ### Vulnerable Code ```javascript const SSE_URL = process.env.YNOTE_MCP_URL || 'https://open.mail.163.com/api/ynote/mcp/sse'; const API_KEY = process.env.YNOTE_API_KEY; ``` ```javascript const proto = this.#sseUrl.protocol === 'https:' ? https : http; this.#request = proto.request({ hostname: this.#sseUrl.hostname, port: this.#sseUrl.port || (this.#sseUrl.protocol === 'https:' ? 443 : 80), path: this.#sseUrl.pathname + this.#sseUrl.search, method: 'GET', headers: { 'Accept': 'text/event-stream', 'Cache-Control': 'no-cache', 'x-api-key': this.#apiKey }, }); ``` ```javascript return fetch(this.#messageUrl.toString(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream', 'x-api-key': this.#apiKey }, body: JSON.stringify(body), }); ``` The shell implementation has the same behavior: ```bash SSE_URL="${YNOTE_MCP_URL:-https://open.mail.163.com/api/ynote/mcp/sse}" API_KEY="${YNOTE_API_KEY:?请设置 YNOTE_API_KEY 环境变量}" curl -sfN \ -H "Accept: text/event-stream" \ -H "Cache-Control: no-cache" \ -H "x-api-key: $API_KEY" \ "$SSE_URL" >>"$SSE_OUT" 2>/dev/null & ``` It then constructs the message endpoint from server-provided data: ```bash MESSAGE_URL="${BASE_URL}${ENDPOINT}" ``` ### Technical Analysis `YNOTE_MCP_URL` accepts an arbitrary origin and supports both HTTP and HTTPS. The YNote API key is sent in an `x-api-key` header during the initial SSE request and subsequent message requests. Full clipped content and encoded images are then transmitted through MCP tool calls. If an attacker can influence the environment, ...[truncated 1312 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https:` for all credential-bearing connections. 2. Allowlist the expected YNote hostname and port. 3. Reject URL usernames, fragments, unexpected ports, and non-HTTPS schemes. 4. Enforce that the SSE-provided message endpoint has the same trusted origin as the initial endpoint. 5. Do not follow redirects to another origin while retaining credentials. 6. Use a separate development credential for custom test endpoints. 7. Avoid inheriting `YNOTE_MCP_URL` from broad or untrusted process environments. 8. Scope `YNOTE_API_KEY` to the minimum MCP tools required for clipping. 9. Add certificate validation and optionally certificate or public-key pinning where operationally appropriate. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
twitter-apify.mjs:193
Finding
Untrusted Page and Apify Data Is Uploaded as Unsanitized HTML<![CDATA[ ## Vulnerability Details **File Location**: `twitter-apify.mjs:193-220`, `twitter-apify.mjs:231-235`; `clip-note.mjs:603-632`; `static/marked.mjs:1-72` **Vulnerability Type**: Stored HTML injection and unsafe URL attribute construction **Risk Level**: Medium ### Vulnerable Code ```javascript function buildHtmlContent(tweet, text, imageUrls = []) { const parts = []; if (tweet.author) { const authorUrl = `https://x.com/${tweet.author.userName}`; parts.push( `<p><strong><a href="${authorUrl}">` + `@${tweet.author.userName}</a></strong> ` + `(${tweet.author.name || ''})</p>` ); } const richText = convertToRichText(tweet, text); parts.push(`<p>${richText}</p>`); for (const imgUrl of imageUrls) { parts.push(`<p><img src="${imgUrl}" /></p>`); } if (tweet.url) { parts.push(`<p><a href="${tweet.url}">原推文链接</a></p>`); } return parts.join('\n'); } ``` ```javascript result = result.replace( urlEntity.url, `<a href="${urlEntity.expanded_url || urlEntity.url}">` + `${displayUrl}</a>` ); ``` Markdown conversion also generates HTML without a sanitization step: ```javascript if (values.markdown && bodyHtml) { bodyHtml = marked.parse(bodyHtml); } ``` The resulting HTML is sent directly to MCP: ```javascript let result = await client.callTool('clipperSaveWithImages', { title, bodyHtml, sourceUrl, images: JSON.stringify(images), }); ``` ### Technical Analysis Tweet body text is initially escaped, but multiple other fields returned by the third-party actor are inserted directly into HTML attributes or text contexts. These include usernames, author names, tweet URLs, media URLs, expanded URLs, and display URLs. The code does not perform contextual HTML attribute escaping or validate URL schemes. General webpage HTML is also accepted from the DOM parser and forwarded without a strict sanitization phase. The bundled Markdown parser permits raw HTML as part of normal Markdown processing an ...[truncated 1061 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Sanitize all generated and extracted HTML with a maintained allowlist-based sanitizer. 2. Permit only necessary elements such as paragraphs, headings, lists, safe links, and images. 3. Remove scripts, styles, iframes, forms, embedded objects, event-handler attributes, and dangerous CSS. 4. Escape every dynamic value according to its HTML context. 5. Validate `href` and `src` values and permit only approved schemes such as `https:`. 6. Normalize URLs before validation and reject control characters. 7. Treat Apify results as untrusted input. 8. Disable raw HTML in Markdown or sanitize the rendered output. 9. Retain server-side sanitization in YNote as defense in depth. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:148
Finding
Workflow Executes the User's Entire Shell Startup File<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:19`, `SKILL.md:148-151`, `SKILL.md:164-169` **Vulnerability Type**: Excessive local code execution through shell profile sourcing **Risk Level**: Medium ### Vulnerable Code ```bash source ~/.zshrc && node {baseDir}/clip-note.mjs \ --data-file /tmp/ynote-clip-data.json \ --markdown \ --source-url "原始URL" ``` The primary workflow repeats the same instruction: ```bash source ~/.zshrc && node {baseDir}/clip-note.mjs \ --data-file /tmp/ynote-clip-data.json \ --source-url "原始 URL" ``` The stated purpose is only to import environment variables: ```text source ~/.zshrc ensures that YNOTE_API_KEY and other environment variables are visible to the Node process. ``` ### Technical Analysis `source ~/.zshrc` does not selectively load `YNOTE_API_KEY`. It executes every command in the shell startup file in the current process context. Startup files commonly contain aliases, command substitutions, package-manager hooks, network calls, directory changes, and arbitrary user-defined shell code. Executing an entire interactive profile exceeds the minimum privilege necessary to provide one API key to the clipping process. It also makes the Skill's effective behavior dependent on mutable code outside the audited package. ### Attack Path 1. A malicious installer, compromised local process, or prior configuration change modifies `~/.zshrc`. 2. The user requests webpage clipping. 3. The agent follows the documented workflow and sources the profile. 4. Every command in the profile executes before `clip-note.mjs`. 5. The injected profile code can access the Skill's environment and act with the user's privileges. ### Impact Assessment Malicious profile code can read environment secrets, modify files, alter executable resolution, redirect network settings, replace invoked commands, or execute arbitrary local programs. The issue does not independently modify the startup file, but it expands the Skill's tr ...[truncated 85 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not source `~/.zshrc`, `~/.bashrc`, or other general startup files. 2. Configure `YNOTE_API_KEY` through the agent platform's approved secret or environment mechanism. 3. Pass the variable directly to the process when necessary: ```bash YNOTE_API_KEY="$YNOTE_API_KEY" \ node {baseDir}/clip-note.mjs ... ``` 4. If a dedicated file is unavoidable, use a narrowly scoped configuration file containing data only, enforce mode `0600`, validate ownership, and parse it without shell execution. 5. Invoke external tools using absolute trusted paths or a controlled `PATH`. 6. Update the documentation so users are never instructed to execute mutable shell profiles as part of the clipping workflow. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (35)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose is webpage clipping, but the skill exposes broader note-management behavior including createNote, searchNotes, listNotes, getNoteTextContent, and generic MCP invocation via helper scripts. This mismatch is dangerous because users or orchestrators may grant trust based on a narrow description while the skill can read, enumerate, and manipulate unrelated note data beyond the advertised function.

Ae1

High
Category
analysis-evasion
Content
将网页内容剪藏到有道云笔记。通过 `clip-note.mjs`(Node.js)完成图片处理和 MCP 调用。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
将网页内容剪藏到有道云笔记。通过 `clip-note.mjs`(Node.js)完成图片处理和 MCP 调用。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
将网页内容剪藏到有道云笔记。通过 `clip-note.mjs`(Node.js)完成图片处理和 MCP 调用。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Chaining Abuse

High
Category
Tool Misuse
Content
BASE_URL=$(echo "$SSE_URL" | sed 's|^\(https\{0,1\}://[^/]*\).*|\1|')

TMPDIR_MCP=$(mktemp -d)
trap 'kill "$SSE_PID" 2>/dev/null; wait "$SSE_PID" 2>/dev/null; rm -rf "$TMPDIR_MCP"' EXIT

SSE_OUT="$TMPDIR_MCP/sse.out"
touch "$SSE_OUT"
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Ssd 3

High
Confidence
96% confidence
Finding
The collector contains site-specific handlers for DeepSeek, Yuanbao, Tongyi, Kimi, Doubao, and Yiyan that iterate through chat DOM nodes and serialize full user questions and assistant answers into HTML for saving. This creates a concrete data leakage path because sensitive natural-language content—credentials, business data, personal information, prompts, or generated analysis—can be exfiltrated into notes without minimization, redaction, or scope limits.

Obfuscated Code

High
Category
Supply Chain
Content
(()=>{const s=document.createElement('script');s.textContent=atob('dmFyIGNvbGxlY3RQYXJzZXI7KCgpPT57InVzZSBzdHJpY3QiO3ZhciBlPVtmdW5jdGlvbihlLHQsbil7dmFyIG89dGhpcyYmdGhpcy5fX2NyZWF0ZUJpbmRpbmd8fChPYmplY3QuY3JlYXRlP2Z1bmN0aW9uKGUsdCxuLG8pe3ZvaWQgMD09PW8mJihvPW4pO3ZhciByPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxuKTtyJiYhKCJnZXQiaW4gcj8hdC5fX2VzTW9kdWxlOnIud3JpdGFibGV8fHIuY29uZmlndXJhYmxlKXx8KHI9e2VudW1lcmFibGU6ITAsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRbbl19fSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUsbyxyKX06ZnVuY3Rpb24oZSx0LG4sbyl7dm9pZCAwPT09byYmKG89biksZVtvXT10W25dfSk7dC5fX2VzTW9kdWxlPSEwLHQucGFyc2U9dm9pZCAwLG8odCxuKDEpLCJwYXJzZSIpfSxmdW5jdGlvbihlLHQsbil7dmFyIG89dGhpcyYmdGhpcy5fX2F3YWl0ZXJ8fGZ1bmN0aW9uKGUsdCxuLG8pe3JldHVybiBuZXcobnx8KG49UHJvbWlzZSkpKChmdW5jdGlvbihyLGkpe2Z1bmN0aW9uIGZ1bGZpbGxlZChlKXt0cnl7c3RlcChvLm5leHQoZSkpfWNhdGNoKGUpe2koZSl9fWZ1bmN0aW9uIHJlamVjdGVkKGUpe3RyeXtzdGVwKG8udGhyb3coZSkpfWNhdGNoKGUpe2koZSl9fWZ1bmN0aW9uIHN0ZXAoZSl7ZS5kb25lP3IoZS52YWx1ZSk6ZnVuY3Rpb24gYWRvcHQoZSl7cmV0dXJuIGUgaW5zdGFuY2VvZiBuP2U6bmV3IG4oKGZ1bmN0aW9uKHQpe3QoZSl9KSl9KGUudmFsdWUpLnRoZW4oZnVsZmlsbGVkLHJlamVjdGVkKX1zdGVwKChvPW8uYXBwbHkoZSx0fHxbXSkpLm5leHQoKSl9KSl9LHI9dGhpcyYmdGhpcy5fX2dlbmVyYXRvcnx8ZnVuY3Rpb24oZSx0KXt2YXIgbixvLHIsaSxhPXtsYWJlbDowLHNlbnQ6ZnVuY3Rpb24oKXtpZigxJnJbMF0pdGhyb3cgclsxXTtyZXR1cm4gclsxXX0sdHJ5czpbXSxvcHM6W119O3JldHVybiBpPXtuZXh0OnZlcmIoMCksdGhyb3c6dmVyYigxKSxyZXR1cm46dmVyYigyKX0sImZ1bmN0aW9uIj09dHlwZW9mIFN5bWJvbCYmKGlbU3ltYm9sLml0ZXJhdG9yXT1mdW5jdGlvbigpe3JldHVybiB0aGlzfSksaTtmdW5jdGlvbiB2ZXJiKGkpe3JldHVybiBmdW5jdGlvbihjKXtyZXR1cm4gZnVuY3Rpb24gc3RlcChpKXtpZihuKXRocm93IG5ldyBUeXBlRXJyb3IoIkdlbmVyYXRvciBpcyBhbHJlYWR5IGV4ZWN1dGluZy4iKTtmb3IoO2E7KXRyeXtpZihuPTEsbyYmKHI9MiZpWzBdP28ucmV0dXJuOmlbMF0/by50aHJvd3x8KChyPW8ucmV0dXJuKSYmci5jYWxsKG8pLDApOm8ubmV4dCkmJiEocj1yLmNhbGwobyxpWzFdKSkuZG9uZSlyZXR1cm4gcjtzd2l0Y2gobz0wLHImJihpPVsyJmlbMF0sci52YWx1ZV0pLGlbMF0pe2Nhc2UgMDpjYXNlIDE6cj1pO2JyZWFrO2Nhc2UgNDpyZXR1cm4gYS5sYWJlbCsrLHt2YWx1ZTppWzFdLGRvbmU6ITF9O2Nhc2UgNTphLmxhYmVsKyssbz1pW
...[truncated 27 chars]
Confidence
99% confidence
Finding
The file base64-encodes a large script and injects it into the document at runtime, which hides behavior from reviewers and users and frustrates security inspection. Obfuscation is especially dangerous here because the hidden code performs broad DOM scraping and content transformation, making it easier to conceal privacy-invasive or harmful logic.

Ssd 3

High
Confidence
98% confidence
Finding
The decoded payload contains explicit logic to harvest and package article contents and AI/chat conversations, including user prompts and model responses from services such as DeepSeek, Kimi, Doubao, Tongyi, Yuanbao, and Yiyan. This creates a high-risk data exposure path because sensitive prompts, proprietary text, credentials pasted into chats, or personal information can be extracted and later transmitted or stored outside the original context.

Hidden Instructions

High
Category
Prompt Injection
Content
* The code in this file is generated from files in ./src/
 */

function M(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var T=M();function H(u){T=u}var _={exec:()=>null};function k(u,e=""){let t=typeof u=="string"?u:u.source,n={replace:(r,i)=>{let s=typeof i=="string"?i:i.source;return s=s.replace(m.caret,"$1"),t=t.replace(r,s),n},getRegex:()=>new RegExp(t,e)};return n}var Re=(()=>{try{return!!new RegExp("(?<=1)(?<!1)")}catch{return!1}})(),m={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^<a /i,endATag:/^<\/a>/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^</,endAngleBracket:/>$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notS
...[truncated 27 chars]
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The implementation materially differs from the declared skill purpose: instead of clipping arbitrary webpages to Youdao Cloud Notes, it sends Twitter/X URLs to Apify and transforms returned tweet data. This mismatch is dangerous because users and reviewers may grant permissions or trust based on the manifest while the code actually exfiltrates request data to a third-party service and performs a different function than advertised.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill declares broad operational capabilities—shell execution, network access, MCP access, environment-variable use, and browser automation—but does not constrain them with an explicit tool scope such as permissions or allowed-tools. In practice, this increases the blast radius if the skill is mis-invoked, modified, or prompt-injected, because the runtime can access more capabilities than users would reasonably infer from a clipping skill.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger phrases include broad natural-language activators like '保存网页' and '收藏网页', which can cause the skill to fire in ambiguous contexts without clear user intent. For a skill with shell, browser, network, and note-writing capabilities, accidental invocation can lead to unintended external fetches and persistence of content into a user's notebook.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
  # Ubuntu / Debian
  sudo apt install imagemagick

  # Alpine
  apk add imagemagick
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
  # Ubuntu / Debian
  sudo apt install imagemagick

  # Alpine
  apk add imagemagick
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The instruction mandates replying with the fixed Chinese phrase "正在保存中...", and the response template later is also prescribed entirely in Chinese. This imposes a locale/language choice on the user without documenting that the skill is region-specific or allowing user opt-in.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The file says the agent "必须" use a fixed Chinese response template and follow-up prompt, regardless of the user's preferred language. That is a language/locale policy issue because no alternative language path or opt-in mechanism is provided.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
After clipping, the skill inspects recent favorite notes and queries cron-task state to decide whether to show a growth prompt. This is unrelated to saving a webpage and constitutes cross-skill behavioral profiling, revealing user state from other workflows and increasing unnecessary data access and surveillance surface.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file-level documentation, usage guidance, and runtime-facing error/debug strings are written in Chinese only, which imposes a specific language without any opt-in or alternative. Under the stated policy, a forced language/locale is a violation unless the skill offers user choice or clearly documents a justified region-specific constraint.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The manifest describes a skill for clipping webpages to Youdao Cloud Notes, which naturally justifies network access and note creation, but not spawning local utilities. This code imports child_process and later relies on external commands like dig, curl, sips, convert, and identify to perform DNS bypassing and image processing, adding host-level execution capability beyond the obvious scope of simple web clipping.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script transmits the full clipped page HTML, source URL, and base64-encoded image data to a remote MCP endpoint, which can include sensitive browsing content, authenticated-page excerpts, or personal data. In a clipping skill, this data flow is expected, but the lack of an explicit consent/disclosure boundary increases the risk of users unknowingly exporting sensitive content to a third-party service.

Missing User Warnings

Medium
Confidence
76% confidence
Finding
The script opens an arbitrary target URL in the browser automation tool and injects/evaluates page-parsing code, which inherently causes network activity and processing of page data. Although this is related to the script's purpose, there is no explicit runtime disclosure that remote content will be fetched and parsed via the browser profile.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code performs file writes of scraped webpage data, including the final JSON output and temporary raw data, but the only disclosure is in code comments and usage text rather than a clear runtime warning or confirmation. Because the script stores potentially sensitive page content to a user-supplied path, it should more explicitly warn the user before or during execution.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script reads YNOTE_API_KEY from the environment and sends it in the x-api-key header during outbound curl requests. While the header use is necessary for the tool's function, there is no user-facing prompt, log, or warning that credentials will be transmitted to a remote MCP endpoint.

External Transmission

Medium
Category
Data Exfiltration
Content
# ─── Step 2: MCP 握手(initialize + initialized)───

curl -sf -X POST "$MESSAGE_URL" \
    -H "Content-Type: application/json" \
    -H "x-api-key: $API_KEY" \
    --max-time "$TIMEOUT" \
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This file programmatically extracts page DOM content, including article bodies and AI chat transcripts, but contains no built-in disclosure, consent check, or user-facing indication that sensitive on-page data may be collected and saved. In a clipping skill, silent collection is especially risky because users may invoke it on pages containing private prompts, model outputs, account data, or proprietary text they did not intend to export wholesale.

Static analysis

Detected: suspicious.env_credential_access, suspicious.obfuscated_code

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
clip-note.mjs:59

Potential obfuscated payload detected.

Warn
Code
suspicious.obfuscated_code
Location
static/inject-sdk.fn.js:1