Back to skill

Security audit

diy-pc-ingest

Security checks for vulnerabilities and agentic risk

Overview

The skill is not malicious, but it can change Notion data and delegates credentialed requests to an unpinned or overrideable helper, so it should be reviewed before installation.

Install only if you trust the `notion-api-automation` dependency source and can restrict the Notion integration to the intended DIY_PC data sources. Avoid setting `NOTIONCTL_PATH` unless it points to a reviewed local helper, keep unrelated secrets out of the runtime environment, use plan mode before apply, and do not invoke the deprecated Python script.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (5)

T08 · Insecure Dependencies

Error
Location
SKILL.md:14
Finding

Unpinned Executable Dependency Creates a Supply-Chain Risk

Content
View full analysis
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/notion_apply_records.js:69
Finding

Configurable Tool Path Allows Execution of Substituted Code with Inherited Secrets

Content
View full analysis
path.join(root, 'notion-api-automation/scripts/notionctl.mjs')); const found = candidates.find(p => fs.existsSync(p)); if (!found) throw new Error('notionctl not found; install notion-api-automation or set NOTIONCTL_PATH'); return found; } function safeEnv(extra = {}) { return { ...process.env, ...extra }; } async function notionReq(method, apiPath, body) { const p = String(apiPath).startsWith('/v1/') ? String(apiPath) : `/v1${String(apiPath).startsWith('/') ? '' : '/'}${String(apiPath)}`; const args = [ notionctlPath(), 'api', '--compact', '--method', String(method).toUpperCase(), '--path', p, ]; if (body !== undefined && body !== null) args.push('--body-json', JSON.stringify(body)); const env = safeEnv({ NOTION_VERSION: notionVersion() }); let out = ''; try { out = execFileSync('node', args, { encoding: 'utf-8', env, stdio: ['ignore', 'pipe', 'pipe'] }).trim(); ``` ### Technical Analysis `NOTIONCTL_PATH` is treated as an executable JavaScript path after only an existence check. The implementation does not canonicalize the path, reject symbolic links, verify ownership or permissions, constrain it to an approved installation root, or validate its integrity. The selected file ...[truncated 1480 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/_deprecated/notion_apply_records.py:495
Finding

Deprecated Direct-Page Update Bypasses Configured Target Boundaries

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_deprecated/notion_apply_records.py:92
Finding

Deprecated Script Supports Plaintext Notion Credentials

Content
View full analysis
str: # Prefer env tok = os.environ.get("NOTION_API_KEY") or os.environ.get("NOTION_TOKEN") if tok: return tok.strip() # Config inline token tok = ((cfg.get("notion") or {}).get("api_key") or "").strip() if tok and "PUT_YOUR_" not in tok: return tok # Legacy local file path (author setup) key_path = os.environ.get("NOTION_API_KEY_FILE") or "~/.config/notion/api_key" return open(os.path.expanduser(key_path), "r", encoding="utf-8").read().strip() ``` ### Technical Analysis The deprecated implementation accepts a Notion API token directly from a JSON configuration value and falls back to reading a plaintext file. It does not validate file ownership, reject symbolic links, or enforce restrictive permissions. This conflicts with the primary skill documentation's protected SecretRef model. Configuration files are commonly copied, backed up, attached to bug reports, or accidentally committed. A permissively readable token file can also expose the integration credential to other local users or processes. Reading a credential from an environment variable is expected for the declared architecture; the insecure behavior is the additional unprotected inline and file-based fallback. ### Attack Path 1. A user places the token in the legacy configuration or default token file. 2. The file is committed, backed up, logged, shared, or left readable by another local principal. 3. An attacker obtains the token from that plaintext location. 4. The attacker sends authenticated requests to Notion independently of the skill. 5. The attacker accesses all pages and data sources granted to the integration until the token is revoked. ### Impact Assessment Credential compromis ...[truncated 249 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_deprecated/notion_apply_records.py:132
Finding

Raw Notion Error Responses May Leak Private Data into Logs

Content
View full analysis
dict: url = API + path cfg = load_config() headers = { "Authorization": f"Bearer {notion_api_key(cfg)}", "Notion-Version": notion_version(cfg), } data = None if body is not None: data = json.dumps(body, ensure_ascii=False).encode("utf-8") headers["Content-Type"] = "application/json" r = urllib.request.Request(url, method=method, data=data, headers=headers) try: with urllib.request.urlopen(r, timeout=60) as resp: raw = resp.read().decode("utf-8") except urllib.error.HTTPError as e: raw = e.read().decode("utf-8") raise RuntimeError(f"HTTP {e.code} {path}: {raw}") return json.loads(raw) if raw else {} ``` ### Technical Analysis On an HTTP error, the implementation embeds the complete Notion response body and request path in an uncaught exception. Command runners, service managers, CI systems, agent frameworks, or shell redirection may persist that exception in logs. Notion error responses can include identifiers, property names, validation details, or fragments derived from submitted private records. The primary JavaScript implementation deliberately replaces such details with a generic protected-diagnostics message, but the legacy implementation does not apply equivalent sanitization. The direct HTTPS request to `api.notion.com` is necessary for the legacy script's declared function and does not itself indicate exfiltration. The defect is uncontrolled secondary disclosure into local logging systems. ### Attack Path 1. A user processes a private receipt or hardware record through the deprecated script. 2. A malformed property, invalid identifier, schema ...[truncated 670 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This second mismatch finding is substantively valid because the skill performs direct Notion create/update/archive operations and additional transformation behavior despite an innocuous ingestion-focused description and no explicit declared permissions. In security review terms, hidden write and archive capabilities are dangerous because they can bypass user expectations and cause integrity loss in a connected SaaS workspace.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This second mismatch finding is substantively valid because the skill performs direct Notion create/update/archive operations and additional transformation behavior despite an innocuous ingestion-focused description and no explicit declared permissions. In security review terms, hidden write and archive capabilities are dangerous because they can bypass user expectations and cause integrity loss in a connected SaaS workspace.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 50)May include surrounding context.

md
- If `NOTIONCTL_PATH` is set, `scripts/notion_apply_records.js` uses that

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 90)May include surrounding context.

md
- If `NOTIONCTL_PATH` is set, `scripts/notion_apply_records.js` uses that

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 105)May include surrounding context.

md
- If `NOTIONCTL_PATH` is set, `scripts/notion_apply_records.js` uses that

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 106)May include surrounding context.

md
- If `NOTIONCTL_PATH` is set, `scripts/notion_apply_records.js` uses that

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 155)May include surrounding context.

md
- If `NOTIONCTL_PATH` is set, `scripts/notion_apply_records.js` uses that

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 162)May include surrounding context.

md
- If `NOTIONCTL_PATH` is set, `scripts/notion_apply_records.js` uses that

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 102)May include surrounding context.

md
Run from the installed skill directory identified by `SKILL.md` (including owner-qualified paths). Keep private input and plan output outside the repository.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises operational capabilities through metadata and workflow steps that rely on environment secrets, file input, and networked Notion access, but it does not declare an explicit tool scope such as permissions or allowed-tools. That weakens reviewability and policy enforcement, making it easier for a host agent to grant broader access than users expect, especially because the skill can write, update, and archive remote Notion data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file instructs the agent to "Prefer Japanese column names" as a general rule. This is a natural-language locale constraint, and the document does not provide user opt-in or clearly justify that the skill is region-specific enough to require Japanese naming.

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/_deprecated/notion_apply_records.py (reported line 17)May include surrounding context.

python
Notion API:
- Query/schema via data_sources
- Create rows via pages with parent.database_id
"""

from __future__ import annotations

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/_deprecated/notion_apply_records.py (reported line 29)May include surrounding context.

python
import urllib.request
from dataclasses import dataclass

API = "https://api.notion.com/v1"

DEFAULT_NOTION_VERSION = "2025-09-03"

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The page_id/archive escape hatch allows the caller to archive an arbitrary Notion page by ID after only checking that the requested target has configured IDs, but it does not verify that the page actually belongs to that target’s allowed database. In an agent setting, if an upstream LLM or user can supply a page_id, this expands the tool from scoped ingest/upsert into general destructive modification of any page accessible to the integration token.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The manual page_id update path permits direct modification of any Notion page reachable by the API token, bypassing the script’s normal target-scoped lookup and upsert logic. That is dangerous because a caller can write to unrelated records simply by knowing or guessing a page ID, which exceeds the skill’s stated receipt/spec ingestion role and weakens least-privilege assumptions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This manifest-style JSON includes several hard-coded Japanese property names such as "名前", "型番", "シリアル", and "取り外し表示名" alongside English names. Because the file provides no indication that Japanese locale is required or user-selectable, it may impose a language-specific configuration convention without explicit opt-in.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The manifest says the skill ingests records into DIY_PC Notion tables, but a storage record with mirror_to_pcconfig triggers additional writes into the pcconfig table as a second side effect. This is more than a straightforward ingest/upsert of the provided target record and introduces cross-table propagation not mentioned in the description.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/notion_apply_records.js:103