Back to skill

Security audit

WeChat to Notion

Security checks for vulnerabilities and agentic risk

Overview

The skill largely does what it claims, but it needs review because it can upload full article content to Notion and has avoidable credential and temporary-file handling risks.

Install only if you are comfortable sending the fetched article text, images, metadata, generated keywords, rating, and comment to your Notion workspace. Before use, replace the API-key check with a presence-only check, use a unique private temporary file per run, and require clear user intent or confirmation before saving each link to Notion.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:18
Finding

Partial Notion API Key Disclosure in Configuration Check

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 18–21
Vulnerability Type: Credential information disclosure
Risk Level: Medium

Vulnerable Code

bash
echo ${NOTION_API_KEY:0:8}...

Technical Analysis

The configuration check prints the first eight characters of NOTION_API_KEY. Because an AI agent may execute this command through a shell tool, the resulting credential prefix can be stored in tool output, terminal logs, execution telemetry, or conversation history.

This disclosure is unnecessary because the workflow only needs to determine whether the variable is configured. It also conflicts with the subsequent instruction that the API key must not be exposed through chat logs.

The disclosed prefix alone does not provide authenticated Notion access, but it reveals credential material that should remain secret and may assist credential identification or correlation if combined with information from another leak.

Attack Path

  1. A user invokes the Skill to archive a WeChat article.
  2. The agent follows the mandatory configuration check in SKILL.md.
  3. The shell expands NOTION_API_KEY and prints its first eight characters.
  4. The output is captured in agent tool results, terminal logs, telemetry, or conversation records.
  5. A party with access to those records obtains part of the Notion credential and can correlate it with other leaked or discovered credential material.

Impact Assessment

This issue does not directly disclose the complete token and does not independently grant Notion API access. Its scope is limited to partial credential exposure. However, it unnecessarily weakens secret confidentiality and increases the impact of other information-disclosure incidents.

Remediation
View remediation

Remediation Suggestions

Replace the value-printing check with a presence-only test:

bash
if [ -n "$NOTION_API_KEY" ]; then
  echo "Notion API key is configured."
else
  echo "Notion API key is missing."
fi

Additional hardening measures:

  • Never print, log, truncate, hash, or otherwise expose any portion of the API key.
  • Ensure errors from Notion requests do not include authorization headers.
  • Preserve the existing instruction that users must configure the key through the secret-management mechanism rather than sending it in chat.
  • Redact known token formats such as ntn_... from agent tool output and execution logs where supported.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:72
Finding

Predictable Shared Temporary File Enables File Clobbering and Content Substitution

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 72–73, 77–78, and 107–108
Vulnerability Type: Unsafe predictable temporary file
Risk Level: Medium

Vulnerable Code

The article is written to a fixed path:

bash
python3 {skillDir}/scripts/fetch_wechat.py <wechat_url> > /tmp/wx_article.json

The same predictable path is subsequently read for analysis:

text
Use the `read` tool to load `/tmp/wx_article.json`.

It is then supplied to the Notion writer:

bash
python3 {skillDir}/scripts/save_to_notion.py \
  /tmp/wx_article.json \
  <notion_db_url> \
  <wechat_url> \
  <read_time_iso8601+08:00> \
  "<kw1>,<kw2>,<kw3>" \
  "<comment>" \
  <rating>

Technical Analysis

/tmp/wx_article.json is a constant filename in a directory commonly shared by multiple processes and users. Shell output redirection opens the path before starting the Python process, follows symbolic links where operating-system protections do not prevent it, and truncates an existing destination.

This creates three related risks:

  • Symlink-based file clobbering: An attacker may pre-create the path as a symbolic link to another file writable by the Skill's operating-system account.
  • Content substitution: Another process using the same account or otherwise able to modify the file may replace the JSON between fetching, analysis, and upload.
  • Concurrency and confidentiality: Simultaneous Skill executions use the same file, allowing one run to overwrite or consume another run's article. File permissions determined by the process umask may also make captured article content readable to other local users.

The practical exploitability of cross-user symlink attacks depends on host protections such as fs.protected_symlinks, directory permissions, and process isolation. Same-account concurrency and accidental cross-run corruptio ...[truncated 1882 chars]

Remediation
View remediation

Remediation Suggestions

Create a private, unpredictable temporary directory for every invocation and remove it reliably:

bash
tmpdir="$(mktemp -d)" || exit 1
chmod 700 "$tmpdir"
trap 'rm -rf -- "$tmpdir"' EXIT

article_file="$tmpdir/wx_article.json"
python3 {skillDir}/scripts/fetch_wechat.py "<wechat_url>" > "$article_file"

Use $article_file for both the analysis and save steps rather than referring to a fixed path.

Additional hardening measures:

  • Set a restrictive umask before creating temporary artifacts:

    bash
    umask 077
    
  • Validate that the generated file is a regular file owned by the current account before reading it.

  • Keep the temporary directory open only for the lifetime of a single workflow.

  • Use unique state per invocation to prevent concurrent runs from sharing data.

  • Ensure cleanup executes on normal completion, errors, and interruption.

  • Where possible, pass parsed data through a protected pipe or in-memory mechanism instead of a shared filesystem location.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (18)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README clearly states that full article content, extracted metadata, and generated comments are written to Notion, which is a third-party service, but it does not warn users about the privacy and data-handling implications. This can lead users to transmit copyrighted, sensitive, or internal material without informed consent, especially because the workflow is presented as automatic and convenient.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Saying that simply sending a WeChat article link in conversation will trigger the full pipeline creates an overly broad activation condition for an agent skill that performs network fetching, AI analysis, and writes to Notion. In an agent environment, this raises the risk of unintended execution from casual mentions, prompt-injected content, or unreviewed links, causing unwanted external requests and data writes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README states that article content will be fetched, analyzed by AI, and written into Notion, but it does not clearly warn users that full article data and generated metadata may be transmitted to external services. In an agent-skill context, this can cause unintentional disclosure of article contents or sensitive links because users may trigger the workflow by simply sending a URL without realizing what data leaves the local environment.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill invokes shell commands, reads environment variables, and performs outbound network requests, but it does not declare any explicit tool scope or allowed-tools restrictions. This increases the blast radius if the skill is misused or modified, because the runtime may permit broader capabilities than are actually required.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

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

md
>    ```
>    OpenClaw will inject it as `NOTION_API_KEY` automatically.

⚠️ Never ask the user to send the API key as a chat message — it will be exposed in conversation logs.

## Setup (One-time)

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 · SKILL.md (reported line 44)May include surrounding context.

Ask if the user has an existing Notion database. If yes, use it directly. If no, ask for a parent page URL and create one:

bash
curl -s -X POST https://api.notion.com/v1/databases \
  -H "Authorization: Bearer $NOTION_API_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json" \

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 · SKILL.md (reported line 44)May include surrounding context.

Ask if the user has an existing Notion database. If yes, use it directly. If no, ask for a parent page URL and create one:

bash
curl -s -X POST https://api.notion.com/v1/databases \
  -H "Authorization: Bearer $NOTION_API_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json" \

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/fetch_wechat.py (reported line 30)May include surrounding context.

python
sys.exit(1)

def fetch_html(url):
    result = subprocess.run([
        'curl', '-s', url,
        '-H', 'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
        '-H', 'Accept-Language: zh-CN,zh;q=0.9',

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The curl request hard-codes Accept-Language: zh-CN,zh;q=0.9, which imposes a specific language/locale on all users. This is a natural-language policy concern because the file provides no user opt-in, fallback, or justification for forcing a Chinese locale.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
86% confidence
Finding

This code transmits article content, metadata, keywords, ratings, comments, and a bearer token to the external Notion API. In the context of a skill that archives fetched WeChat articles, this is expected behavior, but it still creates a real data-exfiltration boundary: any sensitive article data or user-supplied comments will leave the local environment and be stored in a third-party service.

Content

Scanner excerpt · scripts/save_to_notion.py (reported line 60)May include surrounding context.

python
def notion_request(method, path, body=None, key=None):
    cmd = ['curl', '-s', '-X', method,
           f'https://api.notion.com/v1{path}',
           '-H', f'Authorization: Bearer {key}',
           '-H', 'Notion-Version: 2025-09-03',
           '-H', 'Content-Type: application/json']

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/save_to_notion.py (reported line 66)May include surrounding context.

python
'-H', 'Content-Type: application/json']
    if body:
        cmd += ['-d', json.dumps(body)]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if not result.stdout.strip():
        return {}
    return json.loads(result.stdout)

Tainted flow: 'cmd' from os.environ.get (line 98, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/save_to_notion.py (reported line 66)May include surrounding context.

python
'-H', 'Content-Type: application/json']
    if body:
        cmd += ['-d', json.dumps(body)]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if not result.stdout.strip():
        return {}
    return json.loads(result.stdout)

External Transmission

Medium
Category
Data Exfiltration
Confidence
82% confidence
Finding

This request sends the bearer token and database identifier to Notion to inspect database schema. While necessary for the feature, it confirms that the skill communicates with an external third party and can reveal metadata about the user's Notion workspace; combined with the rest of the script, this enables uploading and organizing external content in that workspace.

Content

Scanner excerpt · scripts/save_to_notion.py (reported line 98)May include surrounding context.

python
return mapping

    # Try database schema first (use 2022-06-28 — newer versions omit properties)
    cmd = ['curl', '-s', f'https://api.notion.com/v1/databases/{db_id}',
           '-H', f'Authorization: Bearer {key}',
           '-H', 'Notion-Version: 2022-06-28']
    r = json.loads(subprocess.run(cmd, capture_output=True, text=True).stdout)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/save_to_notion.py (reported line 101)May include surrounding context.

python
cmd = ['curl', '-s', f'https://api.notion.com/v1/databases/{db_id}',
           '-H', f'Authorization: Bearer {key}',
           '-H', 'Notion-Version: 2022-06-28']
    r = json.loads(subprocess.run(cmd, capture_output=True, text=True).stdout)
    props = r.get('properties', {})
    if props:
        return _parse_props(props)

Tainted flow: 'cmd' from os.environ.get (line 98, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/save_to_notion.py (reported line 101)May include surrounding context.

python
cmd = ['curl', '-s', f'https://api.notion.com/v1/databases/{db_id}',
           '-H', f'Authorization: Bearer {key}',
           '-H', 'Notion-Version: 2022-06-28']
    r = json.loads(subprocess.run(cmd, capture_output=True, text=True).stdout)
    props = r.get('properties', {})
    if props:
        return _parse_props(props)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The README instructs users to obtain and export a Notion API key but provides no guidance on secure handling of that credential. Users may place the token in shell history, commit it to dotfiles, logs, or shared configuration, which could allow unauthorized access to their Notion workspace.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The markdown provides direct setup instructions for a Notion API key and later notes that the agent will use the environment variable automatically. Although this is expected for the skill, the document does not warn users that the token is a sensitive credential that should be stored securely and not exposed in logs, screenshots, or shared shells.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The README states that sending a WeChat article link in conversation triggers the automation flow, and the entire usage guidance is presented only in Chinese in this file. For a general-purpose agent skill, this can amount to an implicit locale constraint without user opt-in or an explicit statement that the skill is Chinese-only.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.