Back to skill

Security audit

Html Mark

Security checks for vulnerabilities and agentic risk

Overview

This annotation skill appears legitimate, but it can be injected into arbitrary live sites and stores or exports captured page content without enough privacy guardrails.

Use this mainly on local prototypes or pages you own or are authorized to review. Avoid running the bookmarklet on authenticated, production, financial, healthcare, admin, or third-party pages, and review/redact clipboard exports before pasting them into AI tools or shared systems.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
html-mark.js:697
Finding
Persistent Plaintext Storage of Annotated DOM Content and Input Values<![CDATA[ ## Vulnerability Details **File Location**: `html-mark.js:697-705`, with sensitive data collected at `html-mark.js:747-758` **Vulnerability Type**: Persistent client-side exposure of potentially sensitive page content **Risk Level**: Medium The same implementation is duplicated in `docs/html-mark.js` at the corresponding line ranges. ### Vulnerable Code Sensitive element content, including the current value of an annotated input, is collected and retained: ```javascript if (cur.matches('input, select, textarea')) { const v = cur.placeholder || cur.value || ''; return { label: cur.tagName.toLowerCase(), selector: cur.tagName.toLowerCase(), text: v.slice(0, 80), target: cur }; } ``` An HTML snapshot of the selected element is also captured: ```javascript const ann = { id: id, ctx: getContext(), label: desc.label, selector: desc.selector, text: desc.text, path: cssPath(desc.target), html: desc.target && desc.target.outerHTML ? desc.target.outerHTML.replace(/\s+/g, ' ').slice(0, 200) : '', note: '', pinEl: null, targetEl: desc.target, pageX: e.pageX, pageY: e.pageY }; ``` The collected content is then stored persistently and without protection in `localStorage`: ```javascript function save() { try { localStorage.setItem(STORE_KEY, JSON.stringify(annotations.map(function (a) { return { id: a.id, note: a.note, label: a.label, selector: a.selector, path: a.path, text: a.text, html: a.html, relX: a.relX, relY: a.relY, pageX: a.pageX, pageY: a.pageY }; }))); } catch (e) { /* storage unavailable or full — annotations stay in-memory */ } } ``` ### Technical Analysis The runtime is intended for injection into arbitrary pages, including live and third-party sites through a bookmarklet. When a user annotates an input, select, or textarea, `describeElement()` may copy up to 80 characters from the element's current value. The annotation also includes up to 200 character ...[truncated 3053 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Exclude sensitive form controls by default.** Never collect values from password fields or controls likely to contain credentials or personal information: ```javascript function safeControlText(el) { if (!el || !el.matches('input, select, textarea')) return ''; const type = (el.getAttribute('type') || '').toLowerCase(); const sensitiveTypes = ['password', 'hidden']; const sensitiveAutocomplete = [ 'current-password', 'new-password', 'one-time-code', 'cc-number', 'cc-csc' ]; if (sensitiveTypes.includes(type)) return ''; if (sensitiveAutocomplete.includes(el.autocomplete)) return ''; return el.placeholder || ''; } ``` Prefer storing a placeholder or semantic label rather than the current value. 2. **Do not persist raw `outerHTML`.** Generate a minimized structural description that removes values and sensitive attributes. At minimum, strip attributes such as `value`, `srcdoc`, inline event handlers, authorization data, tokens, and application-specific secret fields. 3. **Make persistence opt-in.** Keep annotations in memory by default and provide a clearly labeled “Remember annotations on this site” setting before writing to localStorage. 4. **Warn users before enabling persistence on live pages.** Explain that selected page text and review notes may be stored under the target site's origin and remain readable by scripts on that origin. 5. **Add expiration and explicit cleanup.** Store a creation timestamp and delete records after a short configurable retention period. Provide a control that removes every `html-mark:*` key for the current origin, not only the active pathname. 6. **Minimize persisted fields.** Store only the note, selector, and normalized position if persistence is necessary. Generate HTML snapshots only during an explicit export operation and keep them in memory. 7. **Apply the fix to both runtime copies.** Keep `html-mark.js` and `docs/html-mark.js` synchronized ...[truncated 315 chars]
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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (27)

Ae1

High
Category
analysis-evasion
Content
A single-file self-contained runtime (`html-mark.js`). After injection it gives the page:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
A single-file self-contained runtime (`html-mark.js`). After injection it gives the page:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
A single-file self-contained runtime (`html-mark.js`). After injection it gives the page:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
A single-file self-contained runtime (`html-mark.js`). After injection it gives the page:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Agent Config Directory Access

High
Category
Agent Snooping
Content
**Default action: inline the runtime as a `<script>` block before the target HTML's `</body>`** (keeps the HTML self-contained so the user can email or AirDrop it).

```bash
cat ~/.claude/skills/html-mark/html-mark.js
```

Then use Edit to wrap the runtime in `<script>…</script>` and insert before `</body>`. **Do not trim anything** — the script is fully self-contained (CSS is inlined, DOM is self-injected, all class names use the `.mm-*` prefix for isolation).
Confidence
87% confidence
Finding
The skill instructs reading a file from the agent's configuration directory under ~/.claude/skills, which is a sensitive local path outside the user's project workspace. Guidance that encourages access to agent config directories can expose secrets, other installed skills, or private local assets if reused or generalized by an agent.

Agent Config Directory Access

High
Category
Agent Snooping
Content
```bash
sed -i '' '/<\/body>/i\
<script>\
'"$(cat ~/.claude/skills/html-mark/html-mark.js)"'\
</script>
' /tmp/mm-test.html
```
Confidence
85% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Agent Config Directory Access

High
Category
Agent Snooping
Content
```bash
sed -i '' '/<\/body>/i\
<script>\
'"$(cat ~/.claude/skills/html-mark/html-mark.js)"'\
</script>
' /tmp/mm-test.html
```
Confidence
85% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Session Persistence

Medium
Category
Rogue Agent
Content
# HTML Mark

> **Click-to-annotate overlay for any HTML page.** Drop coral-gradient pins on any element, write feedback inline, copy out as Markdown / Plain / JSON — or as a **For AI** handoff that pastes straight into Claude Code for one-pass fixes.

![status](https://img.shields.io/badge/status-stable-success) ![type](https://img.shields.io/badge/type-skill-blue) ![style](https://img.shields.io/badge/style-glassmorphism-orange) ![license](https://img.shields.io/badge/license-MIT--0-lightgrey)
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.

Session Persistence

Medium
Category
Rogue Agent
Content
name: html-mark
version: 1.2.0
title: HTML Mark — Click-to-annotate overlay for HTML prototypes
description: Drop coral-gradient pins on any HTML page, write feedback in an inline glass note popup, copy out as Markdown / Plain / JSON — or a For-AI format (unique CSS selector + HTML snapshot per pin) built to paste into Claude Code for one-pass fixes. Pins anchor to their elements and persist in localStorage. Glass-morphism aesthetic, keyboard-friendly, self-contained single file.
author: xuxinmaxen
type: agent
category: productivity
Confidence
83% confidence
Finding
The skill persists annotations in localStorage and promotes exporting notes plus HTML snapshots for later AI-assisted processing. This creates retention of potentially sensitive review data in the browser and increases exposure if the device, browser profile, or shared machine is accessed by others.

Ssd 3

Medium
Confidence
93% confidence
Finding
The 'For AI' export is designed to collect a unique selector plus an HTML snapshot from reviewed elements and paste that data into an external AI workflow. On arbitrary or live pages, this can leak proprietary markup, user-visible sensitive information, or third-party content beyond the original browsing context.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The skill explicitly instructs users to generate bookmarklets or hosted loaders that inject code into arbitrary live websites, including competitor sites. That creates a cross-site script-injection workflow and normalizes running unvetted code in sensitive browsing contexts, which can expose page content, session-visible data, and user interactions to the injected runtime.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The bookmarklet guidance targets live and competitor pages without any warning about consent, terms-of-service, privacy, or handling of visible data. In context, this omission increases the likelihood of unsafe deployment on third-party sites and downstream disclosure of captured content.

Ssd 3

Medium
Confidence
94% confidence
Finding
The documented workflow combines injection into arbitrary live pages with copy/export features, creating a practical path to extract semantically rich page content into external tools. Even if intended for annotation, this materially increases the risk of proprietary or sensitive third-party data leakage.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The tool persists annotations in localStorage, including freeform notes plus page-derived selector, text, path, and HTML snapshot data. On sensitive or shared systems, this can retain confidential page content beyond the session and expose it to other scripts running on the same origin, later users of the browser profile, or accidental recovery after logout. The skill's purpose of annotating arbitrary HTML pages makes this more dangerous because users may run it on internal prototypes or authenticated pages containing sensitive business or user data.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The export feature copies page-derived content including element text, CSS selectors, and in 'For AI' / JSON modes a snapshot of the target element's outerHTML. If a user annotates pages containing secrets, personal data, tokens, hidden values, or internal-only content, this data can be silently packaged into clipboard output and then pasted into another tool or LLM, causing unintended disclosure. The skill context increases risk because it is explicitly designed to collect exact DOM snapshots for handoff to coding agents.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The hosted bookmarklet variant instructs users to inject a remote script into whatever page they visit, which gives that external code the same DOM access as the bookmarklet itself. Without an explicit warning about trust, supply-chain risk, and privacy implications, users may unknowingly execute modified or malicious code from a CDN, fork, or mutable branch such as @main.

Ssd 3

Medium
Confidence
90% confidence
Finding
The advertised AI-oriented workflow encourages packaging DOM selectors and HTML snapshots for pasting into a coding agent. In this context, the feature increases the likelihood that sensitive on-page text, user-entered values, or embedded tokens are transferred into a third-party natural-language channel outside the application's trust boundary.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill persistently stores annotation metadata in localStorage, including captured element text, CSS paths, relative coordinates, and HTML snapshots. Because those snapshots can include user-visible content or form-derived text and localStorage is readable by any script running on the same origin, this creates an unintended local data retention and disclosure risk even if the tool's purpose is legitimate.

Ssd 3

Medium
Confidence
96% confidence
Finding
The element description logic explicitly captures input placeholders or current input values as annotation text. If a user clicks a populated form control, secrets or personal data can be stored and later exported, making this more dangerous than generic page-text capture because it can directly expose credentials, PII, or other entered data.

Ssd 3

Medium
Confidence
95% confidence
Finding
Storing outerHTML for clicked elements can capture inline text, hidden data attributes, prefilled form values, and other sensitive DOM content that the user may not realize is being retained. Because this snapshot is later persisted and exportable, it creates a direct mechanism for leaking confidential page state to other people, systems, or AI tools.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The export feature copies all collected annotations, selectors, text, and possibly HTML snapshots directly to the clipboard without warning that sensitive page content may be included. Clipboard contents are easy to paste into external tools or AI systems, so this creates a realistic exfiltration path for confidential data captured during annotation.

Ssd 3

Medium
Confidence
91% confidence
Finding
The 'For AI' export specifically instructs users to send exact selectors and HTML snapshots to a coding agent, normalizing disclosure of captured page data to an external processor. In the skill context, this is more dangerous because AI handoff is a core feature rather than an incidental side effect, so users are steered toward a potentially unsafe sharing pattern.

Session Persistence

Medium
Category
Rogue Agent
Content
## Setup

1. Create a minimal test page:

```bash
cat > /tmp/mm-test.html <<'EOF'
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.

Scope Creep

Low
Category
Excessive Agency
Content
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
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.

Lp4

Low
Category
MCP Least Privilege
Confidence
65% confidence
Finding
Declared permissions with no matching code capability may indicate removed functionality or pre-staging for future abuse.

Static analysis

No suspicious patterns detected.