Back to skill

Security audit

YoudaoNote News

Security checks for vulnerabilities and agentic risk

Overview

The skill appears purpose-aligned, but it handles private note content and external searches in ways users should review carefully before installing.

Review this before installing if your favorite notes may contain sensitive personal, work, health, financial, or confidential topics. The skill may send derived topics from those notes to external search services, and its current helper scripts have local exposure risks for temporary data and the optional Perplexity API key. Scheduled daily pushes should only be enabled intentionally.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Warning
Location
get-favorite-notes.sh:18
Finding

Predictable Temporary File Exposes Private Note Data and Enables Symlink Attacks

Content
View full analysis

Vulnerability Details

File Location: get-favorite-notes.sh:18-24
Vulnerability Type: Predictable temporary file with sensitive content
Risk Level: Medium

Vulnerable Code

bash
TMPFILE="${TMPDIR:-/tmp}/ynote-fav-notes-$$.json"

cleanup() { rm -f "$TMPFILE"; }
trap cleanup EXIT

# Main path: write to disk and parse with jq
if ! mcporter call youdaonote.getRecentFavoriteNotes --args "{\"limit\":$LIMIT}" --output json > "$TMPFILE" 2>/dev/null; then

Technical Analysis

The script creates a temporary filename using only the process ID and places it in ${TMPDIR:-/tmp}. In a shared temporary directory, this name is predictable. The file is created through ordinary shell redirection rather than an atomic, exclusive operation such as mktemp.

A local attacker may pre-create the expected path as a symbolic link. When redirection occurs, the shell follows the link and truncates or overwrites the linked file if the invoking user has permission. The resulting file can also expose private note titles and content if the current umask permits access by other users.

The cleanup trap removes the path after execution but does not prevent pre-creation, symlink following, or access while the script is running.

Attack Path

  1. A local attacker monitors or predicts the process ID of a forthcoming Skill invocation.
  2. The attacker creates /tmp/ynote-fav-notes-<PID>.json as a symbolic link to a file writable by the victim.
  3. The script redirects the Youdao MCP response to that path.
  4. The shell follows the symbolic link and truncates or overwrites the linked target.
  5. Alternatively, if permissions are insufficiently restrictive, the attacker reads the temporary file while it contains private note data.

Impact Assessment

Exploitation requires local access and the ability to create files in the selected temporary directory. A successful attacker may disclose private note titles, ide ...[truncated 221 chars]

Remediation
View remediation

Remediation Suggestions

Create the file atomically with restrictive permissions:

bash
umask 077
TMPFILE="$(mktemp "${TMPDIR:-/tmp}/ynote-fav-notes.XXXXXX")"
cleanup() { rm -f -- "$TMPFILE"; }
trap cleanup EXIT

Also validate that TMPDIR refers to a trusted directory owned by the invoking user or protected against unsafe file replacement. Where possible, avoid writing note content to disk and process it through a pipe.

T09 · Insecure Skill Coding Practices

Warning
Location
perplexity-search-call.sh:27
Finding

Perplexity API Key Is Exposed Through Process Arguments

Content
View full analysis

Vulnerability Details

File Location: perplexity-search-call.sh:27-31, 63-65; perplexity-search-call.mjs:22-25
Vulnerability Type: Secret exposure through command-line arguments
Risk Level: Medium

Vulnerable Code

From perplexity-search-call.sh:

bash
API_KEY="${PERPLEXITY_API_KEY:-}"
if [ -z "$API_KEY" ]; then
  echo 'Error: PERPLEXITY_API_KEY is not set' >&2
  echo 'Configure PERPLEXITY_API_KEY in the OpenClaw configuration or export it in the shell.' >&2
  exit 1
fi
bash
# Invoke the Node helper, passing the API key and JSON parameters through argv
exec node "$SCRIPT_DIR/perplexity-search-call.mjs" "$API_KEY" "$ARGS_JSON"

From perplexity-search-call.mjs:

javascript
const apiKey = (process.argv[2] ?? '').trim();
if (!apiKey) {
  process.stderr.write('Error: API key was not provided by perplexity-search-call.sh\n');
  process.exit(1);
}

Technical Analysis

The wrapper copies PERPLEXITY_API_KEY into the Node process command line as argv[2]. Command-line arguments may be visible through process inspection interfaces, process-monitoring tools, audit systems, diagnostic snapshots, or crash-reporting infrastructure.

Although the Node helper does not access the environment directly, transferring the credential into argv does not improve isolation. It may increase exposure because command lines are commonly collected by operational telemetry.

Attack Path

  1. A user invokes the Perplexity search wrapper.
  2. The shell replaces itself with a Node process whose second argument contains the API key.
  3. A local process observer, administrator, monitoring agent, or diagnostic collector captures the process command line while the request is active.
  4. The observer extracts the credential and reuses it against the Perplexity API.

Impact Assessment

A successful attacker may consume the victim's Perplexity quota, incur account charges wh ...[truncated 258 chars]

Remediation
View remediation

Remediation Suggestions

Do not place credentials in command-line arguments. Read the key directly from process.env.PERPLEXITY_API_KEY inside the Node helper:

javascript
const apiKey = (process.env.PERPLEXITY_API_KEY ?? '').trim();

Then invoke the helper without the key argument:

bash
exec node "$SCRIPT_DIR/perplexity-search-call.mjs" "$ARGS_JSON"

For stronger isolation, pass the secret through a dedicated inherited file descriptor or a short-lived file created with mode 0600. Ensure logs, exception handlers, and HTTP error output never include authorization headers or credential values.

T09 · Insecure Skill Coding Practices

Warning
Location
websearch-call.sh:19
Finding

Manual JSON Construction Permits MCP Argument Injection and Request Corruption

Content
View full analysis

Vulnerability Details

File Location: websearch-call.sh:19-41
Vulnerability Type: Unsafe JSON construction from untrusted values
Risk Level: Medium

Vulnerable Code

bash
QUERY=$(node -e "const p=JSON.parse(process.argv[1]);console.log((p.query||'').trim())" "$TOOL_ARGS" 2>/dev/null || echo "")
if [ -z "$QUERY" ]; then
  echo 'query must be a non-empty string' >&2
  exit 1
fi

LIMIT=$(node -e "const p=JSON.parse(process.argv[1]);console.log(typeof p.limit==='number'?p.limit:10)" "$TOOL_ARGS" 2>/dev/null || echo "10")
ENGINES=$(node -e "const p=JSON.parse(process.argv[1]);const e=Array.isArray(p.engines)&&p.engines.length>0?p.engines:['duckduckgo','bing','baidu'];console.log(e.join(' '))" "$TOOL_ARGS" 2>/dev/null || echo "duckduckgo bing baidu")
RECENCY=$(node -e "const p=JSON.parse(process.argv[1]);console.log(p.recency!==false&&p.recency!=='none'?'day':'')" "$TOOL_ARGS" 2>/dev/null || echo "day")

SEARCH_QUERY="$QUERY"
if [ "$RECENCY" = "day" ]; then
  SEARCH_QUERY="$QUERY latest"
fi

TMPFILE="${TMPDIR:-/tmp}/websearch-result-$$.json"
cleanup() { rm -f "$TMPFILE"; }
trap cleanup EXIT

for ENGINE in $ENGINES; do
  MCPORTER_ARGS="{\"query\":\"$SEARCH_QUERY\",\"limit\":$LIMIT,\"engines\":[\"$ENGINE\"]}"

  if mcporter call open-websearch.search --args "$MCPORTER_ARGS" --output json > "$TMPFILE" 2>/dev/null; then

Technical Analysis

The script initially parses TOOL_ARGS as JSON, but it then reconstructs another JSON document by directly interpolating SEARCH_QUERY, LIMIT, and ENGINE. String values are not escaped by a JSON serializer.

Quotes, backslashes, newlines, and control characters in the query can make the generated document malformed or alter its structure. Engine values can similarly inject additional JSON syntax. Engine entries are also flattened into a space-separated string and processed with shell word splitting, so names containing whitespace are split into m ...[truncated 1132 chars]

Remediation
View remediation

Remediation Suggestions

Construct every MCP request with a JSON serializer rather than string concatenation. For example:

bash
MCPORTER_ARGS="$(
  jq -cn \
    --arg query "$SEARCH_QUERY" \
    --arg engine "$ENGINE" \
    --argjson limit "$LIMIT" \
    '{query: $query, limit: $limit, engines: [$engine]}'
)"

Preserve engines as a real array rather than a space-separated string. Validate engine names against an explicit allowlist such as duckduckgo, bing, and baidu. Require limit to be a bounded positive integer before passing it to jq --argjson.

T09 · Insecure Skill Coding Practices

Warning
Location
websearch-call.sh:34
Finding

Predictable Search-Result Temporary File Enables Local Disclosure and Symlink Attacks

Content
View full analysis

Vulnerability Details

File Location: websearch-call.sh:34-43
Vulnerability Type: Predictable temporary file with externally sourced data
Risk Level: Medium

Vulnerable Code

bash
TMPFILE="${TMPDIR:-/tmp}/websearch-result-$$.json"
cleanup() { rm -f "$TMPFILE"; }
trap cleanup EXIT

for ENGINE in $ENGINES; do
  MCPORTER_ARGS="{\"query\":\"$SEARCH_QUERY\",\"limit\":$LIMIT,\"engines\":[\"$ENGINE\"]}"

  if mcporter call open-websearch.search --args "$MCPORTER_ARGS" --output json > "$TMPFILE" 2>/dev/null; then
    if [ -s "$TMPFILE" ]; then
      cat "$TMPFILE"

Technical Analysis

The script stores search responses in a predictable PID-based path under a shared temporary directory. It does not create the file atomically, prevent symbolic-link traversal, or explicitly set restrictive permissions.

Search queries and results may reveal user interests derived from private notes. A local attacker can potentially read the response while the script is running. Pre-creating the path as a symbolic link may also cause shell redirection to truncate or overwrite another file writable by the Skill's user.

Attack Path

  1. A local attacker predicts or observes the search process ID.
  2. The attacker pre-creates /tmp/websearch-result-<PID>.json as a symbolic link or waits for the file to appear.
  3. The script redirects MCP output to the predictable path.
  4. The attacker causes an unintended writable target to be overwritten or reads the search response if file permissions permit.
  5. The cleanup trap removes the temporary pathname but does not reverse disclosure or target-file modification.

Impact Assessment

The issue may expose search queries and result data that reflect topics extracted from private notes. A symlink attack may overwrite files accessible to the invoking user. It does not independently provide access beyond that user's filesystem permissions.

Remediation
View remediation

Remediation Suggestions

Use atomic temporary-file creation with owner-only permissions:

bash
umask 077
TMPFILE="$(mktemp "${TMPDIR:-/tmp}/websearch-result.XXXXXX")"
cleanup() { rm -f -- "$TMPFILE"; }
trap cleanup EXIT

Prefer streaming the MCP response directly when retry logic does not require storage. If storage is necessary, use a trusted per-user runtime directory and minimize the lifetime of the stored response.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill description does not clearly disclose that external search providers such as Perplexity may be used, yet note-derived topic strings are sent to third-party services. This mismatch can cause users to believe processing is confined to YoudaoNote-related functionality when in fact data leaves the local/platform boundary.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill description does not clearly disclose that external search providers such as Perplexity may be used, yet note-derived topic strings are sent to third-party services. This mismatch can cause users to believe processing is confined to YoudaoNote-related functionality when in fact data leaves the local/platform boundary.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill description does not clearly disclose that external search providers such as Perplexity may be used, yet note-derived topic strings are sent to third-party services. This mismatch can cause users to believe processing is confined to YoudaoNote-related functionality when in fact data leaves the local/platform boundary.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill performs network-capable operations, including external web search and optional Perplexity API calls, but does not declare an explicit tool scope such as permissions or allowed-tools. This weakens least-privilege controls and makes it harder for users or the platform to understand and constrain outbound data flows, especially because note-derived topics may be transmitted to third parties.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill reads recent favorite note content and uses that content to derive search topics, but the user-facing description does not clearly warn that private note material influences outbound queries. Even if only summaries or keywords are sent, sensitive interests, projects, health, finance, or work topics could be inferred and disclosed to external services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The workflow sends topic queries to third-party search providers but does not provide a clear user-facing warning about external data transmission. Because the topics are derived from personal notes, this can leak sensitive user interests or confidential context beyond the note platform, and the skill context makes this more dangerous because the data source is inherently personal.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script fetches recent favorite notes and emits note titles and truncated content directly to stdout, which can expose private user data to downstream components, logs, or prompts without any explicit consent or minimization beyond length truncation. In this skill's context, favorite notes are the data source for news-topic inference, so the access is functionally related, but the lack of disclosure, filtering, and output controls still creates a real privacy and data-exposure risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · perplexity-search-call.mjs (reported line 72)May include surrounding context.

js
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);

try {
  const resp = await fetch('https://api.perplexity.ai/search', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · perplexity-search-call.mjs (reported line 72)May include surrounding context.

js
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);

try {
  const resp = await fetch('https://api.perplexity.ai/search', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script reads PERPLEXITY_API_KEY from the environment and passes it to the Node process as a positional command-line argument. Command-line arguments are commonly visible to other local processes via tools like ps or /proc, and may also be captured in logs, crash reports, or job telemetry, which can expose the API key to unintended parties.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script sends user-derived search queries to external search engines through mcporter/open-websearch, but it provides no user-facing notice, consent step, or privacy boundary. In this skill's context, queries are derived from a user's collected notes and interests, so the outbound request may expose sensitive personal topics or note-derived content to third-party search providers.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The file's descriptive comments, truncation notes, and usage guidance are presented in Chinese only. This can violate a language/locale policy when the skill does not provide an alternative language option or indicate that the locale restriction is intentional.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

These lines unconditionally default LANG and LC_ALL to en_US.UTF-8 when unset, imposing a specific locale choice. The file does not offer a language or locale choice or document a justified region-specific need for this constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

Comments and usage text, including error/output strings, are presented in Chinese only. Under the stated policy, forcing a specific language without opt-in or a documented locale-specific justification is a natural-language policy issue.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.