Back to skill

Security audit

Notion

Security checks for vulnerabilities and agentic risk

Overview

This is mostly a coherent Notion helper, but its profile-selection code can read an unintended local key file and send that value to Notion as an API token.

Review or patch the profile-selection snippet before installing. Use NOTION_API_KEY directly or restrict NOTION_PROFILE to known names such as personal/work, store tokens in a secret manager where possible, and share the Notion integration only with the pages and databases needed for the task.

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

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:54
Finding
Path Traversal Through Unvalidated Notion Profile Name## Vulnerability Details **File Location**: `SKILL.md`, lines 54-66 and 76-81 **Vulnerability Type**: Path traversal leading to unintended local-file disclosure **Risk Level**: Medium The Skill constructs a credential-file path using the unvalidated `NOTION_PROFILE` environment variable: ```bash mkdir -p ~/.config/notion echo "ntn_personal_key" > ~/.config/notion/personal.key echo "ntn_work_key" > ~/.config/notion/work.key chmod 600 ~/.config/notion/*.key ``` ```bash NOTION_PROFILE="${NOTION_PROFILE:-personal}" NOTION_KEY="${NOTION_API_KEY:-$(cat ~/.config/notion/${NOTION_PROFILE}.key 2>/dev/null)}" [ -n "$NOTION_KEY" ] || echo "No Notion key for profile '$NOTION_PROFILE'" ``` The resulting file contents are subsequently included in an outbound HTTP Authorization header: ```bash notion() { local method="$1" path="$2"; shift 2 curl -sS -X "$method" "https://api.notion.com/v1${path}" \ -H "Authorization: Bearer $NOTION_KEY" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" "$@" } ``` ### Technical Analysis `NOTION_PROFILE` is directly interpolated into the path `~/.config/notion/${NOTION_PROFILE}.key` without an allowlist, character validation, or canonical-path containment check. A value containing `../` components can escape the intended Notion configuration directory. The fixed `.key` suffix restricts the immediately reachable targets to paths whose resulting names end in `.key`, but it does not guarantee that the selected file is a configured Notion profile or that it remains under `~/.config/notion`. If `NOTION_API_KEY` is unset, the contents of the selected file are assigned to `NOTION_KEY`. The `notion` helper then sends `NOTION_KEY` to the official Notion API over HTTPS as a bearer token. Sending a valid Notion token to this endpoint is necessary for the declared functionality. However, allowing an unvalidated profile selector to source arb ...[truncated 1653 chars]
Remediation
## Remediation Suggestions Restrict profile selection to explicitly supported names: ```bash NOTION_PROFILE="${NOTION_PROFILE:-personal}" case "$NOTION_PROFILE" in personal|work) ;; *) echo "Invalid Notion profile" >&2 return 1 ;; esac key_file="$HOME/.config/notion/$NOTION_PROFILE.key" NOTION_KEY="${NOTION_API_KEY:-$(cat "$key_file" 2>/dev/null)}" if [ -z "$NOTION_KEY" ]; then echo "No Notion key for profile '$NOTION_PROFILE'" >&2 return 1 fi ``` If arbitrary user-defined profile names are required: 1. Permit only a narrow format such as `^[A-Za-z0-9_-]+$`. 2. Reject profile names containing path separators, traversal components, whitespace, or shell metacharacters. 3. Resolve the key file to a canonical path and verify that it remains beneath `$HOME/.config/notion/`. 4. Require the key file to be a regular file owned by the expected user and not writable by other users. 5. Validate that the loaded value has an expected Notion token prefix, such as `ntn_` or the documented legacy `secret_`, before using it as an Authorization header. 6. Terminate immediately when the profile is invalid or the token is absent rather than merely printing an error and permitting later requests. 7. Continue using HTTPS and avoid printing, logging, or embedding the token in command diagnostics.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (8)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
notion PATCH /pages/{page_id} -d '{"properties": {"Status": {"select": {"name": "Done"}}}}'
```

To replace body content, delete the specific blocks (`DELETE /blocks/{block_id}`) and append new ones. Confirm with the user first, and delete only the blocks you identified by reading them, never the whole page's children as a batch.

## Databases
Confidence
83% confidence
Finding
The skill includes direct guidance for deleting blocks, which is a destructive operation against user content. Although it says to confirm first and delete only identified blocks, exposing raw deletion primitives in an agent skill raises the risk of parameter misuse, targeting the wrong block IDs, or carrying out irreversible content loss in a high-value notes workspace.

Session Persistence

Medium
Category
Rogue Agent
Content
## Quick Start

1. Create a Notion integration at <https://www.notion.so/my-integrations>
2. Copy the Internal Integration Token (starts with `ntn_`)
3. Export it: `export NOTION_API_KEY=ntn_xxx`
4. Share the target pages and databases with the integration in the Notion UI, otherwise they are invisible to the API
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The activation text is unusually broad: it triggers not only on explicit Notion requests but also on generic requests to 'log' or 'file something' into a workspace, even if the user never mentions the API. That increases the chance this skill is invoked in ambiguous contexts and performs external writes to a real Notion workspace when a narrower, safer skill or a clarification step would be more appropriate.

Session Persistence

Medium
Category
Rogue Agent
Content
## Authentication

Create an integration at <https://www.notion.so/my-integrations> and copy the Internal Integration Token. Current tokens start with `ntn_`; older ones start with `secret_` and still work.

```bash
export NOTION_API_KEY=ntn_your_key_here
Confidence
77% confidence
Finding
The skill encourages storing long-lived API tokens in environment variables and plaintext profile files under ~/.config/notion. While common, this creates session persistence for a high-value credential and increases exposure to local compromise, shell history mishandling, accidental backups, or misuse by other processes running as the same user.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
mkdir -p ~/.config/notion
echo "ntn_personal_key" > ~/.config/notion/personal.key
echo "ntn_work_key"     > ~/.config/notion/work.key
chmod 600 ~/.config/notion/*.key
```

Resolve the active key like this. An explicit `NOTION_API_KEY` always wins; otherwise the profile file is used, defaulting to `personal`:
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
notion() {
  local method="$1" path="$2"; shift 2
  curl -sS -X "$method" "https://api.notion.com/v1${path}" \
    -H "Authorization: Bearer $NOTION_KEY" \
    -H "Notion-Version: 2025-09-03" \
    -H "Content-Type: application/json" "$@"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
notion() {
  local method="$1" path="$2"; shift 2
  curl -sS -X "$method" "https://api.notion.com/v1${path}" \
    -H "Authorization: Bearer $NOTION_KEY" \
    -H "Notion-Version: 2025-09-03" \
    -H "Content-Type: application/json" "$@"
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
}'
```

Prefer additive changes. If the user wants a property retyped, suggest adding a new one and migrating rather than converting in place, because a type change discards values that do not fit the new type. Setting a property to `null` removes it; never do that without confirmation.

Note that the API cannot change view-level filters or sorts. Those are UI-only. If the user asks for that, say so instead of changing the underlying schema as a substitute.
Confidence
75% 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.

Static analysis

No suspicious patterns detected.