Back to skill

Security audit

Notion API

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Notion management skill that can read and modify Notion content using the user's Notion token, with no evidence of hidden persistence or unrelated data handling.

Install only with a Notion integration token you are comfortable using from this environment, and share that integration only with the Notion pages or data sources it should manage. Use dry-run triage before --apply, review rule files before using them, avoid untrusted regular expressions, and prefer NOTION_API_KEY over storing tokens in a local config file.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Findings (1)

other

Warning
Location
scripts/notionctl.mjs:973
Finding

Unbounded User-Supplied Regular Expression Enables CLI Denial of Service

Content
View full analysis

Vulnerability Details

File Location: scripts/notionctl.mjs, lines 973–978
Vulnerability Type: Regular Expression Denial of Service (ReDoS)
Risk Level: Medium

Vulnerable Code

javascript
function ruleMatchesTitle(rule, title) {
  const m = rule.match;
  if (!m || !title) return false;
  if (m.title_regex) {
    const re = new RegExp(m.title_regex);
    return re.test(title);
  }
  if (m.contains) return title.toLowerCase().includes(String(m.contains).toLowerCase());
  return false;
}

Technical Analysis

The triage command loads rules from a caller-selected JSON file and passes each rule's title_regex value directly to the JavaScript RegExp constructor. The resulting expression is evaluated against Notion child-page titles without validation, complexity limits, or an execution timeout.

JavaScript's backtracking regular-expression engine can consume excessive CPU when evaluating expressions containing nested or ambiguous quantifiers. For example, a rule containing ^(a+)+$ can take exponentially longer to reject a sufficiently long title consisting of repeated a characters followed by a nonmatching character.

This issue is particularly relevant in an agent workflow because a rules file in a workspace may be treated as configuration even when its contents originated from an untrusted repository or other external source. Notion page titles are also remote content and should not be assumed trustworthy.

The regular expression is created and evaluated once for each candidate rule and child page. Consequently, a malicious expression can repeatedly block the Node.js event loop during triage.

The separately flagged sensitive network behavior is not itself a vulnerability: scripts/notionctl.mjs sends the Notion API token in an Authorization: Bearer header only to the fixed HTTPS origin https://api.notion.com. Sending that credential and user-selected note content to Notion is ne ...[truncated 2291 chars]

Remediation
View remediation

Remediation Suggestions

  1. Avoid arbitrary regular expressions where possible. Prefer literal matching operations such as contains, starts_with, or equals, which have predictable runtime behavior.

  2. Validate patterns before use. Reject expressions containing risky constructs such as nested quantifiers, ambiguous alternations, excessive repetition, backreferences, or patterns above a small length limit. A regex safety analyzer can provide defense in depth, although static analysis alone should not be treated as a complete guarantee.

  3. Use a linear-time regular-expression implementation. Evaluate configurable patterns through an RE2-compatible library rather than JavaScript's backtracking engine. Document any syntax differences and reject unsupported expressions clearly.

  4. Isolate evaluation if arbitrary JavaScript-compatible regex syntax must be retained. Perform matching in a worker thread or subprocess with strict CPU and wall-clock limits. Terminate the worker when a match exceeds its budget. A promise timeout in the main thread is insufficient because synchronous RegExp.test blocks the event loop.

  5. Limit input size and processing scope. Apply conservative maximum lengths to both title_regex values and page titles before matching. Retain the existing --limit behavior, validate that it is finite and positive, and impose a hard upper bound.

  6. Treat rules as executable policy. Require explicit operator approval before consuming rules from repositories, downloaded archives, or other untrusted workspaces. Resolve rules files against an approved directory where appropriate.

  7. Handle invalid patterns safely. Catch RegExp construction errors per rule, report the rejected rule, and continue or abort deterministically rather than allowing malformed configuration to terminate the entire command.

Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • 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)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill clearly supports environment access for secrets and outbound network access to the Notion API, but it does not declare an explicit tool scope such as permissions or allowed-tools. That weakens policy enforcement and reviewability, increasing the chance an agent can invoke broader capabilities than intended or handle secrets/network access without clear operator consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This skill advertises create, append, import, and move operations against live Notion content, but it does not prominently warn that these actions modify remote user data and may be destructive or hard to undo. In an agent setting, that omission increases the risk of unintended writes, page moves, or bulk changes being executed without sufficiently explicit user confirmation.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest-style JSON uses a broad regex trigger matching generic words like "work," "client," "invoice," and "proposal". Several of these terms can appear in ordinary note titles outside the intended routing context, and the file provides no exclusion examples or tighter scope constraints.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The regex matches very common words such as "home," "rent," "plumber," "council," and "tax," which can overlap with many routine titles. Because the activation scope is defined only by single-keyword matching and lacks negative examples or contextual limits, unintended routing is likely.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/notionctl.mjs (reported line 195)May include surrounding context.

js
const file = readTokenFromFile();
  if (file) return file;
  throw new Error(
    "Missing Notion token. Set NOTION_API_KEY (recommended) or create ~/.config/notion/api_key"
  );
}

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The triage workflow can automatically move child pages when --apply is supplied, with no built-in confirmation, dry-run safeguard beyond a separate mode, or scope restriction. In an agent-oriented CLI that can be driven by untrusted prompts or overly broad automation, this increases the risk of unintended reorganization or data loss from pages being silently moved to the wrong parent.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/notionctl.mjs:172