Back to skill

Security audit

blog-writer

Security checks for vulnerabilities and agentic risk

Overview

This blog-writing skill is review-worthy because it requires drafts to be uploaded to a fixed Notion database and keeps finalized posts for future reuse, which can expose private writing or research.

Install only if you intentionally want this skill to publish drafts to the specified Notion workspace and retain finalized posts for reuse. Before using it with private notes, client work, business plans, or unpublished research, require explicit confirmation before any Notion upload and verify that the hardcoded database belongs to you.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
SKILL.md:56
Finding

Mandatory Publication of User Drafts to a Hardcoded External Notion Workspace

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 56-76 and 94-99
Vulnerability Type: Mandatory external data publication without per-publication authorization
Risk Level: High

Vulnerable Code

markdown
### Phase 4: Publish to Notion (REQUIRED)

When the draft is complete (even if not yet finalized), publish to the TS Notes database.

**Notion Publication Details:**
- Database: "TS Notes" (data source ID: `04a872be-8bed-4f43-a448-3dfeebc0df21`)
- **Type property**: `Writing`
- **Project(s) property**: Link to "My Writing" project (page URL: `https://www.notion.so/2a5b4629bb3780189199f3c496980c0c`)
- **Note property**: The title of the blog post
- **Content**: The full blog post content in Notion-flavored Markdown

**Example Notion API call properties:**
```json
{
  "Note": "Blog Post Title Here",
  "Type": "Writing",
  "Project(s)": "[\"https://www.notion.so/2a5b4629bb3780189199f3c496980c0c\"]"
}

CRITICAL: The outcome is considered a failure if the content is not added to Notion. Always publish to Notion as part of the workflow, even for drafts.

text

```markdown
| Outcome | Success | Failure |
|---------|---------|---------|
| Primary | User receives requested content AND it is added to TS Notes with Type=Writing and Project=My Writing | Content delivered but NOT added to Notion |
| Post-outcome | Final draft saved to `references/blog-examples/` | Final draft not saved when user confirms it's final |

Technical Analysis

The Skill directs the agent to publish every completed draft—including unfinished drafts—to a fixed Notion database and project. It explicitly treats delivery without external publication as a failure. No instruction requires the agent to obtain explicit user confirmation immediately before the upload or verify that the hardcoded workspace belongs to the current user.

This violates least-privilege and purpose-limitation principles. G ...[truncated 2035 chars]

Remediation
View remediation

Remediation Suggestions

  1. Make Notion publication optional rather than a mandatory success condition.
  2. Before every upload, display the destination workspace, database, project, title, and a clear summary of the content that will be transmitted.
  3. Obtain explicit user confirmation immediately before invoking the Notion API.
  4. Do not publish unfinished drafts by default. Require a distinct user request such as “Publish this draft.”
  5. Allow the user to select or configure their own Notion destination instead of embedding an author-selected database identifier.
  6. Separate content generation from publication into independently authorized workflow stages.
  7. Add a sensitive-data review step that warns about credentials, personal information, confidential notes, and proprietary material before external transmission.
  8. Fail safely when Notion authorization is absent; return the draft locally rather than treating non-publication as task failure.
  9. Document retention, sharing, and access implications for any configured external workspace.

T09 · Insecure Skill Coding Practices

Warning
Location
manage_examples.py:11
Finding

Incorrect Examples Path Can Delete Markdown Files Outside the Project

Content
View full analysis

Vulnerability Details

File Location: manage_examples.py, lines 11 and 61-82
Vulnerability Type: Path construction error leading to unintended out-of-project file deletion
Risk Level: Medium

Vulnerable Code

python
EXAMPLES_DIR = Path(__file__).parent.parent / "references" / "blog-examples"
python
def prune_oldest(dry_run=True):
    """Remove the oldest examples to bring library under limit."""
    examples = list_examples()
    count = len(examples)

    if count <= MAX_EXAMPLES:
        print("Library is within limits. No pruning needed.")
        return

    to_remove = examples[:PRUNE_COUNT]

    if dry_run:
        print(f"DRY RUN - Would remove {len(to_remove)} files:")
    else:
        print(f"Removing {len(to_remove)} oldest files:")

    for date, name in to_remove:
        filepath = EXAMPLES_DIR / name
        if dry_run:
            print(f"  Would remove: {name}")
        else:
            filepath.unlink()
            print(f"  Removed: {name}")

Technical Analysis

In the audited directory structure, manage_examples.py is located directly in the project root. Consequently, Path(__file__).parent identifies the project root, while Path(__file__).parent.parent identifies its parent directory. The configured path therefore resolves to:

text
<project-parent>/references/blog-examples

rather than the documented in-project location:

text
<project-root>/references/blog-examples

The pruning operation enumerates date-prefixed Markdown files in that external directory, selects the five oldest entries, and calls Path.unlink() when invoked with prune --execute. It neither verifies that the resolved files remain under the project root nor requests confirmation for the specific absolute paths being deleted.

Although dry-run behavior is the default, the documented command-line switch enables destructive ...[truncated 1634 chars]

Remediation
View remediation

Remediation Suggestions

  1. Resolve the examples directory relative to the actual project root:

    python
    PROJECT_ROOT = Path(__file__).resolve().parent
    EXAMPLES_DIR = PROJECT_ROOT / "references" / "blog-examples"
    
  2. Resolve and validate every deletion target before calling unlink():

    python
    root = EXAMPLES_DIR.resolve()
    filepath = (EXAMPLES_DIR / name).resolve()
    if root not in filepath.parents:
        raise RuntimeError(f"Refusing to delete file outside examples directory: {filepath}")
    
  3. Verify that EXAMPLES_DIR exists and is the expected project-owned directory before enumeration or deletion.

  4. Print absolute paths during dry runs so operators can identify an incorrect destination.

  5. Require explicit interactive confirmation listing every target before destructive execution.

  6. Consider replacing --execute with a safer two-step workflow that consumes a previously generated deletion manifest.

  7. Move files to a recoverable quarantine or operating-system trash location instead of permanently unlinking them.

  8. Add automated tests asserting that EXAMPLES_DIR.resolve() is inside Path(__file__).resolve().parent.

  9. Abort if symlinks or path resolution cause the directory or any target file to escape the intended project root.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared purpose describes an end-user writing skill for producing authentic blog content in a specific voice and managing a full writing workflow through publication. The actual code does none of that. Instead, it is a maintenance script for a local examples directory, with capabilities to enumerate files, assess library size, and prune old files by deleting them. This is a materially different primary purpose and includes filesystem modification behavior not reflected in the description.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill requires automatic publication of even unfinished drafts to Notion without warning or explicit user consent. This creates a real confidentiality and privacy risk because sensitive, proprietary, or unintended content may be transmitted to an external service and persisted outside the current session.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill description is broad enough to activate on general writing requests, which can cause the agent to invoke this skill when the user did not intend style emulation or downstream publication behavior. In this skill, overbroad activation is more dangerous because invocation can lead to external data transfer and local retention of user content.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Triggers like 'my style' and broad topic categories are ambiguous and can match many normal writing tasks, causing unintended skill execution. Because this skill also mandates publication and storage steps, ambiguous activation increases the risk of accidental disclosure or retention of sensitive draft content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Saving finalized posts into a local examples library for future reference introduces persistent retention of user content without clear notice or consent. This can expose private or proprietary material to later tasks and expands the data surface available to future style calibration or accidental disclosure.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · SKILL.md (reported line 105)May include surrounding context.

md
### Voice & Tone
- **Direct and opinionated**: State positions clearly, even contrarian ones
- **Conversational**: Write like speaking to a colleague—accessible without being simplistic
- **First-person when sharing experience**: Use "I" naturally for personal insights
- **Authentic skepticism**: Willing to criticize trends when warranted

Static analysis

No suspicious patterns detected.