Back to skill

Security audit

Convert Notion HTML exports to interactive mind maps

Security checks for vulnerabilities and agentic risk

Overview

The skill does convert Notion exports into a mind map, but crafted input can inject script into the generated HTML, so it needs review before installation.

Review before installing or use only with trusted Notion exports. A maliciously crafted export could make the generated mindmap.html run JavaScript when opened, so run it in a separate folder, avoid overwriting important files, use an isolated pinned Python environment, and validate any custom Notion base URL before generating links.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_html.py:11
Finding
Stored Script Injection in Generated Mind Map HTML<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_html.py:11-12, 21, 65` **Vulnerability Type**: Stored script injection caused by unsafe HTML and JavaScript embedding **Risk Level**: High ### Vulnerable Code ```python workspace_title = data.get("t", "Notion Mind Map") json_str = json.dumps(data, ensure_ascii=False, separators=(',', ':')) html = ( '<!DOCTYPE html>\n' '<html lang="zh-CN">\n' '<head>\n' '<meta charset="UTF-8">\n' '<meta name="viewport" content="width=device-width, initial-scale=1.0">\n' '<title>' + workspace_title + ' - 思维导图</title>\n' # ... '<script>\n' '"use strict";\n' 'const RAW = ' + json_str + ';\n' ) ``` The embedded values originate from the supplied HTML file. For example, the workspace title is extracted without output encoding: ```python def extract_workspace_name_from_html(soup, content): match = re.search(r'工作空间名称[::]\s*(.+?)(?:</p>|<li>|$)', content) if match: return match.group(1).strip() title = soup.find('title') if title: t = title.get_text().strip() t = re.sub(r'^Export[-_]?\s*', '', t, flags=re.IGNORECASE) return t or "Notion" return "Notion" ``` ### Technical Analysis The generated document directly concatenates an untrusted workspace title into the HTML `<title>` element. It also serializes the complete attacker-influenced mind-map data as JSON and places it directly inside an executable `<script>` element. `json.dumps()` produces valid JSON, but it does not make the result safe for inclusion in an HTML script element. In HTML parsing, a literal `</script>` sequence terminates the script element even when that sequence occurs inside a JavaScript string. Consequently, a malicious title containing a sequence such as `</script><script>/* attacker code */</script>` can escape the data block and introduce executable JavaScript. The separate insertion into `<title>` is also unsafe. A value containing `</title> ...[truncated 1412 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. HTML-escape the workspace title before inserting it into the `<title>` element: ```python import html safe_workspace_title = html.escape(workspace_title, quote=True) ``` 2. Do not concatenate ordinary JSON directly into an executable script element. Store it in a non-executable JSON element and escape characters significant to HTML parsing: ```python json_str = json.dumps(data, ensure_ascii=False, separators=(',', ':')) json_str = ( json_str .replace('&', '\\u0026') .replace('<', '\\u003c') .replace('>', '\\u003e') .replace('\u2028', '\\u2028') .replace('\u2029', '\\u2029') ) ``` Then embed and parse it as data: ```html <script id="mindmap-data" type="application/json">...</script> <script> const RAW = JSON.parse(document.getElementById("mindmap-data").textContent); </script> ``` 3. Prefer DOM APIs such as `textContent` whenever untrusted values are rendered into HTML. 4. Add a restrictive Content Security Policy to limit the impact of any remaining injection issue. Moving JavaScript to a separate local file would allow inline scripts to be disabled. 5. Add regression tests containing values such as: ```text </title><script>alert(1)</script> </script><script>alert(1)</script> <>&"' and Unicode line separators ``` The tests should verify that these strings remain inert text in the generated document. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/build_mindmap.py:80
Finding
Unvalidated Base URL Allows Dangerous Node Link Schemes<![CDATA[ ## Vulnerability Details **File Location**: `scripts/build_mindmap.py:80, 139` and `scripts/generate_html.py:484` **Vulnerability Type**: Unsafe URL handling and insufficient scheme validation **Risk Level**: Medium ### Vulnerable Code The optional command-line argument is accepted without validation: ```python src = sys.argv[1] notion_base = sys.argv[2] if len(sys.argv) > 2 else None ``` It is concatenated directly with a page identifier: ```python page_id = extract_id_from_ul(ul_elem) link = notion_base + page_id if (notion_base and page_id) else None ``` The resulting value is subsequently opened by the generated page: ```javascript if (e.ctrlKey || e.metaKey) { if (n.link) window.open(n.link, "_blank"); } ``` ### Technical Analysis The documented purpose of `notion_base` is to specify a Notion HTTPS workspace URL, but the code accepts any string. It does not parse the URL, enforce HTTPS, verify the hostname, or reject active and unexpected schemes. An attacker-controlled value can therefore produce links using schemes such as `javascript:`, `data:`, or other protocol handlers. The behavior of active schemes passed to `window.open()` varies by browser and security context, but relying on browser-specific blocking is unsafe. At minimum, arbitrary external links can be generated, allowing trusted-looking mind-map nodes to redirect users to attacker-controlled locations. The URL is also opened with `_blank` without explicitly requesting `noopener` and `noreferrer`, which can increase opener-related risks in environments that do not automatically isolate new tabs. ### Attack Path 1. An attacker supplies a malicious base URL or persuades the operator to invoke the parser with one: ```bash python build_mindmap.py index.html '<attacker-controlled scheme or URL>' ``` 2. `build_mindmap.py` concatenates the unvalidated base value with every extracted page ID. 3. `generate_html.py` embeds the resulting links in `mindmap.html`. 4. The victim ...[truncated 866 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse and validate the supplied URL before using it. Permit only HTTPS Notion hosts: ```python from urllib.parse import urlparse def validate_notion_base(value): parsed = urlparse(value) if parsed.scheme.lower() != "https": raise ValueError("The Notion base URL must use HTTPS") host = (parsed.hostname or "").lower().rstrip(".") if host != "notion.so" and not host.endswith(".notion.so"): raise ValueError("The base URL must use notion.so or a notion.so subdomain") if parsed.username or parsed.password: raise ValueError("Credentials are not allowed in the base URL") return value.rstrip("/") + "/" ``` 2. Reject control characters, malformed ports, fragments, and unexpected query strings. 3. Build node links through URL-aware functions rather than raw string concatenation. 4. Validate links again in the browser before opening them: ```javascript function openNotionLink(link) { const url = new URL(link); const host = url.hostname.toLowerCase(); if (url.protocol !== "https:" || !(host === "notion.so" || host.endsWith(".notion.so"))) { throw new Error("Blocked non-Notion URL"); } window.open(url.href, "_blank", "noopener,noreferrer"); } ``` 5. Ensure the same validation is applied to automatically detected links so future changes do not introduce inconsistent trust rules. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:39
Finding
Unpinned Third-Party Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:39-42` and `README.md:80-84` **Vulnerability Type**: Unpinned dependency and non-reproducible installation **Risk Level**: Low ### Vulnerable Code ```bash pip install beautifulsoup4 ``` ### Technical Analysis The setup instructions install the latest available release of `beautifulsoup4` without an exact version, lockfile, cryptographic hash, explicit package index, or isolated environment requirement. The package name is legitimate and no evidence of a currently malicious package was identified. Nevertheless, unconstrained resolution makes installations non-reproducible and means that reviewed code can later run against a different dependency version. If the dependency, a transitive dependency, or the configured package index is compromised, installation or import may execute unreviewed code with the operator's privileges. ### Attack Path 1. An operator follows the documented installation command. 2. `pip` resolves the package and its dependencies from the operator's configured index at installation time. 3. A future vulnerable or compromised release, compromised dependency, or untrusted package index supplies code that was not part of this audit. 4. The package is installed and subsequently imported by `build_mindmap.py`. 5. Malicious package installation hooks or imported code execute with the privileges of the Python environment. This is a supply-chain exposure rather than evidence that the current named dependency is malicious. ### Impact Assessment If the dependency supply chain were compromised, code could execute with the privileges of the user performing installation or running the parser. Potential scope includes: - Reading or modifying files accessible to that user. - Accessing environment variables and local credentials. - Altering generated output. - Making network requests. - Compromising other projects sharing the same Python environment. The practical likelihood is lower ...[truncated 105 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin a reviewed dependency version in a requirements file: ```text beautifulsoup4==<reviewed-version> ``` 2. Generate and verify cryptographic hashes, for example through a hash-locked requirements file used with: ```bash python -m pip install --require-hashes -r requirements.txt ``` 3. Record and lock relevant transitive dependencies. 4. Use a dedicated virtual environment instead of installing into a shared or privileged Python environment. 5. Document the expected package index explicitly and avoid untrusted mirrors: ```bash python -m pip install --index-url https://pypi.org/simple --require-hashes -r requirements.txt ``` 6. Periodically review and update pinned versions to incorporate security patches rather than leaving the version unconstrained. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (6)

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill explicitly describes reading a user-provided HTML file and generating output files, but it does not declare any tool scope or permission boundaries. That creates an authorization gap: the agent may use file read/write capabilities without an explicit least-privilege contract, making unintended file access or overwrite more likely if the skill is triggered in the wrong context.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger condition is overly broad because it activates on generic mentions of '思维导图' or 'mindmap', even when the user may not be asking to process a Notion export. Overbroad activation can cause the agent to invoke file-processing behavior in irrelevant contexts, increasing the chance of unnecessary file access, unintended outputs, or misuse of the skill on unsuitable inputs.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The generated document sets `lang="zh-CN"` and the embedded UI strings throughout the HTML are in Chinese, which imposes a specific language/locale on all users. The file does not provide any opt-in, configuration, or documented justification for restricting output to this locale.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The skill is described as converting exported Notion HTML into an interactive mindmap, but the generated output also provides capabilities to open embedded links in new tabs and copy them to the clipboard. While links are mentioned in the manifest, this code will act on whatever URL is present in the input data, effectively enabling arbitrary external navigation from the generated artifact rather than just visualization.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The natural-language content from L03 onward is predominantly Chinese, which effectively forces a specific language for users reading the skill documentation. The file does not offer an alternative language, opt-in, or explanation that this skill is intentionally region- or locale-specific.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
The natural-language instructions and usage description are presented in Chinese, which may implicitly force a specific language experience without user opt-in. The file does not state that the skill is intended only for Chinese-speaking users or offer alternate language behavior.

Static analysis

No suspicious patterns detected.