T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/notion_apply_records.js:455- Finding
Unrestricted Direct Modification or Archival of Accessible Notion Pages
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill’s Notion ingestion purpose is clear, but it includes live page update and archive powers that can affect more Notion content than the configured DIY-PC tables if misused.
Review before installing. Use a Notion integration shared only with the four intended DIY-PC databases, inspect the generated JSONL before execution, and avoid page_id/id, archive, and overwrite unless you intentionally want to modify a specific page. Treat runs as live writes to Notion, not a dry run.
scripts/notion_apply_records.js:455Unrestricted Direct Modification or Archival of Accessible Notion Pages
scripts/notion_apply_records.js:309Ambiguous Upsert Matches Are Treated as Missing Records and Create Duplicates
The skill presents itself as an ingestion/classification workflow, but the documented input to the apply script is already-formed JSONL records and it also supports undeclared record-archive and cross-table mirroring behavior. This mismatch is risky because operators may trust the skill as a benign formatter while it can perform broader state-changing actions in Notion than the description implies.
The skill presents itself as an ingestion/classification workflow, but the documented input to the apply script is already-formed JSONL records and it also supports undeclared record-archive and cross-table mirroring behavior. This mismatch is risky because operators may trust the skill as a benign formatter while it can perform broader state-changing actions in Notion than the description implies.
The skill presents itself as an ingestion/classification workflow, but the documented input to the apply script is already-formed JSONL records and it also supports undeclared record-archive and cross-table mirroring behavior. This mismatch is risky because operators may trust the skill as a benign formatter while it can perform broader state-changing actions in Notion than the description implies.
Referenced artifact was not completely inspected
5) **Upsert into Notion** using `scripts/notion_apply_records.js`:
Referenced artifact was not completely inspected
5) **Upsert into Notion** using `scripts/notion_apply_records.js`:
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.
~/.config/diy-pc-ingest/config.jsonmkdir -p ~/.config/diy-pc-ingest
cp references/config.example.json ~/.config/diy-pc-ingest/config.json
The README explicitly documents operations that can overwrite existing Notion records, null out fields, directly PATCH arbitrary pages, and archive pages, but it does not require a confirmation step or warn that these actions can modify or hide existing data. In a skill whose purpose is to ingest messy user-provided text and perform upserts, this increases the risk of accidental destructive changes from misclassification or malformed input.
The skill explicitly relies on environment variables, local file reads, and network access to retrieve a Notion API key, load local configuration, and write to Notion, yet it declares no tool scope or permission boundary. This creates an authorization-transparency gap: a user or platform cannot easily tell that the skill can access local secrets and modify remote data, increasing the chance of unintended secret exposure or unauthorized writes.
The workflow instructs the agent to upsert records into external Notion tables and report created links, but it does not require an explicit warning or confirmation that external data will be modified. In an agent setting, silent writes to a live workspace can lead to accidental data corruption, duplicate records, or modifications based on misparsed input.
Direct page-by-ID update and archive operations are high-risk mutation capabilities, and documenting them without a strong safety warning or confirmation step makes accidental or malicious destructive changes easier. Because these controls bypass ordinary upsert matching, a malformed or attacker-influenced record can target arbitrary pages for overwrite or archival if the integration has access.
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.
Notion API:
- Query/schema via data_sources
- Create rows via pages with parent.database_id
"""
from __future__ import annotations
The code issues authenticated HTTP requests to the Notion API and later uses them to create and patch pages, which can modify remote user data. While the module docstring describes purpose, there is no runtime confirmation, visible user disclosure, or explicit warning in this file about making live changes to Notion content.
The script accepts page_id/id and archive/archived fields and will directly PATCH or archive any supplied Notion page, which exceeds the advertised upsert-only behavior. In an agent skill that ingests user-pasted text, this creates a dangerous capability expansion: if upstream parsing or prompt injection introduces those fields, the script can modify or archive arbitrary reachable pages rather than only rows identified through safe matching logic.
The direct PATCH/archive path can make destructive remote changes without any in-script confirmation, preview, or safeguard. Because archiving is more destructive than ordinary upserts and can be triggered by input fields, accidental or adversarial records could hide or alter data irreversibly in the user's Notion workspace.
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.
/**
* bootstrap_config.js
*
* Create ~/.config/diy-pc-ingest/config.json by discovering Notion IDs via /v1/search.
* Requires NOTION_API_KEY (or NOTION_TOKEN) and that the target data sources are shared with the integration.
*
* Usage:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import os from 'os';
import path from 'path';
const API = 'https://api.notion.com/v1';
const NOTION_VERSION = process.env.NOTION_VERSION || '2025-09-03';
function die(msg, code = 1) {
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import os from 'os';
import path from 'path';
const API = 'https://api.notion.com/v1';
const NOTION_VERSION = process.env.NOTION_VERSION || '2025-09-03';
function die(msg, code = 1) {
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import os from 'os';
import path from 'path';
const API = 'https://api.notion.com/v1';
const NOTION_VERSION = process.env.NOTION_VERSION || '2025-09-03';
function die(msg, code = 1) {
The README instructs users to create schema fields exactly as specified and explicitly says to use Japanese property names as-is. This imposes a specific language/locale requirement without opt-in or an alternative localized schema, which matches the policy concern for forced language selection.
The example config includes hard-coded Japanese field names such as "名前", "型番", "シリアル", and "取り外し表示名" alongside English keys. Because this is a manifest/config file and no surrounding text explains that Japanese locale is optional or required for a region-specific integration, it can be interpreted as imposing a language-specific schema without user choice.
The module documentation states a narrow record format, but the implementation also honors undocumented control fields for direct page updates and archiving. This documentation/behavior mismatch is security-relevant because callers, reviewers, or higher-level agents may assume the tool only performs constrained upserts while hidden fields enable broader mutations.
A storage record can trigger an additional write to the pcconfig table via mirror_to_pcconfig, so one requested operation may silently produce two remote mutations. This violates least surprise and broadens the blast radius of malformed or adversarial input, especially in an AI-driven ingestion flow where hidden flags may be injected without the user's awareness.
Detected: suspicious.env_credential_access