T09 · Insecure Skill Coding Practices
- Location
SKILL.md:54- Finding
Path Traversal Through Unvalidated Notion Profile Name
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 54-66 and 76-81
Vulnerability Type: Path traversal leading to unintended local-file disclosure
Risk Level: MediumThe Skill constructs a credential-file path using the unvalidated
NOTION_PROFILEenvironment 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/*.keybash 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_PROFILEis directly interpolated into the path~/.config/notion/${NOTION_PROFILE}.keywithout an allowlist, character validation, or canonical-path containment check. A value containing../components can escape the intended Notion configuration directory.The fixed
.keysuffix 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. IfNOTION_API_KEYis unset, the contents of the selected file are assigned toNOTION_KEY.The
notionhelper then sendsNOTION_KEYto 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
View 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 fiIf arbitrary user-defined profile names are required:
- Permit only a narrow format such as
^[A-Za-z0-9_-]+$. - Reject profile names containing path separators, traversal components, whitespace, or shell metacharacters.
- Resolve the key file to a canonical path and verify that it remains beneath
$HOME/.config/notion/. - Require the key file to be a regular file owned by the expected user and not writable by other users.
- Validate that the loaded value has an expected Notion token prefix, such as
ntn_or the documented legacysecret_, before using it as an Authorization header. - Terminate immediately when the profile is invalid or the token is absent rather than merely printing an error and permitting later requests.
- Continue using HTTPS and avoid printing, logging, or embedding the token in command diagnostics.
- Permit only a narrow format such as
