Back to skill

Security audit

paper-notion-summarizer

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but its Notion updater can modify or delete the wrong page and its PDF fetching is too broadly trusted.

Review before installing. Use a Notion integration restricted to a dedicated parent page, run `--dry-run` first, and avoid `--force-update` until page selection is constrained by parent or page ID. Run PDF extraction in a network-restricted environment and pin `pypdf` to a reviewed version.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/extract_paper.py:239
Finding

Unrestricted Publisher PDF Retrieval Permits Server-Side Request Forgery

Content
View full analysis
str: if not pdf_url or PdfReader is None: return "" try: r = requests.get(pdf_url, headers={"User-Agent": "paper-notion-summarizer/2.0"}, timeout=60, allow_redirects=True) except Exception: return "" if r.status_code != 200 or not r.content: return "" try: reader = PdfReader(BytesIO(r.content)) except Exception: return "" pages = [] for page in reader.pages: try: text = page.extract_text() or "" except Exception: text = "" if text: pages.append(text) return "\n".join(pages) ``` The unvalidated URL is passed into that retrieval function: ```python source = (paper.extra.get("pdf_url") or "").strip() if source: candidate_text = fetch_pdf_text(source) ``` ### Technical Analysis Crossref metadata is treated as trusted when selecting a publisher-provided PDF URL. The implementation does not validate: - The URL scheme - The destination hostname - Resolved IP addresses - Redire ...[truncated 2191 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/push_to_notion.py:148
Finding

Title-Based Notion Page Selection Can Destructively Overwrite an Unrelated Page

Content
View full analysis
Optional[str]: payload = { "query": title, "filter": {"value": "page", "property": "object"}, "page_size": 10, } try: data = notion_request("POST", "/search", token, payload) except Exception: data = notion_request("POST", "/search", token, {"query": title, "page_size": 10}) target = _normalize_title(title) for item in data.get("results", []): if item.get("object") != "page": continue try: rich = item.get("properties", {}).get("title", {}).get("title", []) item_title = "".join([x.get("plain_text", "") for x in rich]) except Exception: item_title = "" if item_title and _normalize_title(item_title) == target: return item.get("id") return None ``` The selected page is then updated without verifying that it belongs to the requested parent page: ```python existing = search_page_by_title(title, token) if existing and force_update: clear_block_children(existing, token) append_blocks(existing, token, all_blocks) return existing ``` `clear_block_children()` deletes every direct child block of the selected page: ```python def clear_block_children(page_id: str, token: str) -> None: blocks = get_block_children(page_id, token) for block in blocks: notion_request("DELETE", f"/blocks/{block['id']}", token) ``` ### Technical Analysis The `parent_page_id` argument is used when creating a new page, but it is not used to constrain or validate existing-pag ...[truncated 1934 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:154
Finding

Unpinned Runtime Installation of the PDF Parsing Dependency

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This second description-behavior mismatch indicates that some components may perform Notion search/update/delete/append operations beyond the high-level description, while core promised capabilities such as paper understanding and language adaptation may not be implemented in code. Hidden or under-described content management actions against a third-party workspace can lead to unintended modification or deletion of user data, especially when paired with API credentials.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This second description-behavior mismatch indicates that some components may perform Notion search/update/delete/append operations beyond the high-level description, while core promised capabilities such as paper understanding and language adaptation may not be implemented in code. Hidden or under-described content management actions against a third-party workspace can lead to unintended modification or deletion of user data, especially when paired with API credentials.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
84% confidence
Finding

The documented use of DELETE /v1/blocks/{block_id} gives the skill destructive write capability in Notion. In a summarizer skill, delete functionality is not obviously required for the core task, and if block IDs can be influenced by model output, prompt injection, or weak validation, the agent could remove unrelated content from the user's workspace.

Content

Scanner excerpt · references/notion_api.md (reported line 13)May include surrounding context.

md
- Auth: `Authorization: Bearer ${NOTION_API_KEY}`
- Query existing pages: `POST /v1/search` with query title string
- Append blocks: `PATCH /v1/blocks/{page_id}/children` with `{"children": [...]}`
- Delete blocks: `DELETE /v1/blocks/{block_id}`

## Required env
- `NOTION_API_KEY` (or `~/.config/notion/api_key`)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill instructs the agent to use network access, read and write local files under /tmp, and access environment-backed credentials, but it does not declare any explicit tool scope or permissions boundary. This is dangerous because users and hosting platforms cannot clearly constrain or review what external access the skill requires, increasing the chance of unintended data access, credential use, or exfiltration during execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill sends generated paper summaries to Notion, a third-party external service, but the user-facing description does not prominently warn that content will be uploaded outside the local environment. This is risky because papers, notes, prompts, or derived summaries may contain sensitive research context, and users may not realize their data is being transmitted and stored by an external provider.

Content

No source excerpt is available for this finding.

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 · references/notion_api.md (reported line 5)May include surrounding context.

md
def fetch_from_crossref(query: str, doi: Optional[str] = None) -> Optional[Paper]:
    if doi:
        url = f"https://api.crossref.org/works/{quote_plus(doi)}"
        r = requests.get(url, headers={"User-Agent": "paper-notion-summarizer/2.0"}, timeout=40)
        if r.status_code != 200:
            return None

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 · scripts/extract_paper.py (reported line 192)May include surrounding context.

python
def fetch_from_crossref(query: str, doi: Optional[str] = None) -> Optional[Paper]:
    if doi:
        url = f"https://api.crossref.org/works/{quote_plus(doi)}"
        r = requests.get(url, headers={"User-Agent": "paper-notion-summarizer/2.0"}, timeout=40)
        if r.status_code != 200:
            return None

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 · scripts/extract_paper.py (reported line 198)May include surrounding context.

python
def fetch_from_crossref(query: str, doi: Optional[str] = None) -> Optional[Paper]:
    if doi:
        url = f"https://api.crossref.org/works/{quote_plus(doi)}"
        r = requests.get(url, headers={"User-Agent": "paper-notion-summarizer/2.0"}, timeout=40)
        if r.status_code != 200:
            return None

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 · scripts/push_to_notion.py (reported line 109)May include surrounding context.

python
"Notion-Version": NOTION_API_VERSION,
        "Content-Type": "application/json",
    }
    url = f"https://api.notion.com/v1{path}"
    r = requests.request(method, url, headers=headers, json=payload, timeout=40)
    if r.status_code >= 400:
        raise RuntimeError(f"Notion API error {r.status_code}: {r.text[:400]}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

When --force-update is used, the script deletes all existing child blocks from the matched Notion page and rewrites the content without any confirmation, backup, or safety check. In an agent context, a mistaken title match or unintended flag usage can destroy user-authored page content, causing irreversible data loss within the scope of the integration's permissions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The manifest describes creating and posting a structured summary as a Notion page, which implies creating the page content once or updating it coherently. In the non-force update path, the code appends a fresh copy of all blocks to any matching page, causing cumulative duplication rather than a clean create/update behavior.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The skill's stated purpose is to fetch paper metadata, summarize a paper, and post the result to Notion. Accessing a local credential file at ~/.config/notion/api_key is an extra capability not described in the manifest and is not strictly necessary when command-line or environment-based token input already exists.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The script sends the provided summary data to the Notion API over the network, but the code does not include a visible runtime notice that local file contents and metadata will be transmitted externally. The module docstring describes the purpose, but there is no direct user-facing disclosure during execution.

Content

No source excerpt is available for this finding.