Back to skill

Security audit

NewsletterKit Email Newsletter Builder

Security checks for vulnerabilities and agentic risk

Overview

This is a small local newsletter builder that stores draft items in a JSON file and does not show hidden network, credential, or privilege behavior.

Installers should understand that newsletter items are stored locally in newsletter-items.json. Review generated Markdown carefully before publishing if content comes from untrusted sources, and prefer the HTML output path when link and text escaping matter.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
src/newsletter-kit.js:39
Finding
Unescaped Markdown Content Allows Newsletter Markup Injection## Vulnerability Details **File Location**: `src/newsletter-kit.js`, lines 39-49 **Vulnerability Type**: Markdown content injection **Risk Level**: Medium ### Vulnerable Code ```javascript _generateMarkdown(intro) { let md = `# ${this.name}\n*${new Date().toLocaleDateString()}*\n\n`; if (intro) md += intro + '\n\n'; for (const [section, items] of Object.entries(this.sections)) { md += `## ${section}\n\n`; for (const item of items) { md += item.url ? `- [${item.title}](${item.url})` : `- ${item.title}`; if (item.note) md += ` — ${item.note}`; md += '\n'; } md += '\n'; } return md; } ``` ### Technical Analysis The Markdown generator directly interpolates the newsletter name, introduction, section name, item title, URL, and note into the output without escaping Markdown metacharacters or validating URL schemes. An attacker who can influence newsletter items can inject arbitrary Markdown structures such as headings, images, deceptive links, or raw HTML. Unlike the HTML generator, which escapes text and restricts links to HTTP or HTTPS, the Markdown generator applies no equivalent controls. The exact consequences depend on the downstream Markdown renderer. If it permits raw HTML or unsafe URI schemes, the injection could become HTML injection or client-side script execution. Even with a restrictive renderer, an attacker can manipulate newsletter presentation and introduce phishing links. ### Attack Path 1. An attacker supplies content to `addItem()` through an application or workflow that accepts externally curated newsletter content. 2. The attacker places Markdown syntax or raw HTML in `section`, `title`, `note`, or `url`. For example, a title could contain a forged link or an HTML element. 3. The application calls `generate({ format: "markdown" })`. 4. `_generateMarkdown()` inserts the malicious value into the document without escaping or validation. 5. ...[truncated 673 chars]
Remediation
## Remediation Suggestions 1. Escape Markdown control characters in all untrusted text fields, including the newsletter name, introduction, section names, titles, and notes. 2. Parse URLs with the standard `URL` class and explicitly allow only required schemes, preferably `https:` and, if necessary, `http:`. 3. Reject URLs containing control characters, malformed encoding, or unsupported schemes. 4. Decide whether raw HTML is a supported feature. If it is not, escape or remove HTML tags before generating Markdown. 5. Apply validation when items are added rather than relying exclusively on output-time sanitization. 6. Add tests covering injected headings, images, nested links, raw HTML, `javascript:` URLs, `data:` URLs, and newline-based document-structure injection. 7. Document that downstream publishing platforms should use a Markdown renderer configured to disable raw HTML and unsafe links.

T09 · Insecure Skill Coding Practices

Note
Location
src/newsletter-kit.js:15
Finding
Attacker-Controlled Section Keys Can Cause Denial of Service## Vulnerability Details **File Location**: `src/newsletter-kit.js`, lines 15-16 **Vulnerability Type**: Unsafe use of attacker-controlled object property names **Risk Level**: Low ### Vulnerable Code ```javascript addItem(item) { const section = item.section || 'General'; if (!this.sections[section]) this.sections[section] = []; this.sections[section].push({ title: item.title, url: item.url || null, note: item.note || '', added: new Date().toISOString() }); this._saveItems(); return { section, count: this.sections[section].length }; } ``` The affected object is initialized as follows: ```javascript this.sections = {}; ``` ### Technical Analysis `this.sections` is a normal JavaScript object and therefore inherits properties from `Object.prototype`. The `section` value is used directly as a property name without checking whether the property is owned by `this.sections` or whether its value is an array. For inherited names such as `constructor` or `toString`, the condition `!this.sections[section]` evaluates to false because the inherited property already contains a truthy function. The code then invokes `.push()` on that function, producing a `TypeError`. This is an inherited-property collision rather than a demonstrated privilege-escalation or prototype-pollution primitive. Nevertheless, a crafted item can synchronously terminate the current operation if the caller does not catch the exception. ### Attack Path 1. An attacker reaches an integration that passes externally supplied item data to `addItem()`. 2. The attacker submits an item whose section is an inherited object property, such as `constructor`. 3. `this.sections.constructor` resolves to the inherited `Object` constructor. 4. The initialization condition is skipped because that inherited value is truthy. 5. The method attempts to execute `.push()` on the constructor function. 6. JavaScript throws a `T ...[truncated 564 chars]
Remediation
## Remediation Suggestions 1. Store sections in a `Map` or a null-prototype object such as `Object.create(null)`. 2. Check properties with `Object.hasOwn(this.sections, section)` rather than relying on truthiness. 3. Verify that every existing section value is an array before invoking `.push()`. 4. Validate section names and impose reasonable type and length limits. 5. Validate the structure loaded from `newsletter-items.json`; parsed JSON should be treated as untrusted or potentially corrupted local state. 6. Do not silently suppress persistence and loading errors. Return or log controlled errors so invalid state can be diagnosed. 7. Add regression tests for `constructor`, `prototype`, `toString`, `valueOf`, and `__proto__`. A hardened object-based implementation could use: ```javascript this.sections = Object.create(null); const section = typeof item.section === 'string' && item.section ? item.section : 'General'; if (!Object.hasOwn(this.sections, section)) { this.sections[section] = []; } if (!Array.isArray(this.sections[section])) { throw new TypeError('Invalid section data'); } ```
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Scope Creep

Low
Category
Excessive Agency
Content
**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for
Confidence
70% confidence
Finding
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Scope Creep

Low
Category
Excessive Agency
Content
**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for
Confidence
70% confidence
Finding
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Missing User Warnings

Low
Confidence
77% confidence
Finding
The skill automatically reads a local JSON data file on initialization, which accesses persisted user content without any user-facing notice, logging, or explanatory comment. Under the code-file criteria, file access affecting user data should include some disclosure unless already clearly documented as part of the skill behavior.

Missing User Warnings

Low
Confidence
89% confidence
Finding
This code writes newsletter state to disk with fs.writeFileSync, but there is no confirmation prompt, logging, or explanatory comment near the operation. For a code file, persistent file writes should have at least some visible disclosure unless clearly communicated elsewhere in the skill description, which is not present in this file.

Static analysis

No suspicious patterns detected.