Back to skill

Security audit

Markdown Editor With Chat

Security checks for vulnerabilities and agentic risk

Overview

This is a useful local markdown editor, but it has real privacy and access-control risks around file access, network exposure, and chat context sharing.

Install only if you will run it on trusted markdown directories, keep HOST on 127.0.0.1, and understand that enabling chat can send the full current document to the configured gateway. Avoid using it for secrets or private notes until path validation, authentication, markdown sanitization, request limits, and explicit chat-context controls are fixed.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/server.mjs:118
Finding

Filesystem Sandbox Bypass Through Insecure Path Validation

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/index.html:609
Finding

Stored DOM Cross-Site Scripting Through Filenames and Unsafe Markdown Rendering

Content
View full analysis
No markdown files'; return; } tree.innerHTML = items.map(item => { const icon = item.type === 'folder' ? '📁' : '📄'; const cls = item.type === 'folder' ? 'tree-item folder' : 'tree-item'; const active = currentFile === item.path ? ' active' : ''; const onclick = item.type === 'folder' ? `loadFiles('${item.path}')` : `openFile('${item.path}')`; return `
${icon} ${item.name}
`; }).join(''); } ``` Rendered Markdown is assigned directly to `innerHTML`: ```javascript function updatePreview() { const content = document.getElementById('editor').value; document.getElementById('preview').innerHTML = renderMarkdown(content); } ``` Link destinations are inserted into an HTML attribute without attribute encoding or protocol validation: ```javascript html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); ``` Gateway responses are processed by the same renderer and assigned to `innerHTML`: ```javascript function renderChatMessages() { const container = document.getElementById('chatMessages'); container.innerHTML = chatMessages.map(m => { let content; if (m.loading) { content = ' Thinking...'; } else if (m.role === 'assistant') { content = renderMarkdown(m.content); } else { content = escapeHtml(m.content) ...[truncated 2845 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/server.mjs:79
Finding

Unauthenticated File and Chat APIs Can Be Exposed to Private Networks

Content
View full analysis
= 64 && secondOctet <= 127) { return true; // Tailscale IP } } return host === 'localhost' || host === '127.0.0.1' || host === '::1' || host.startsWith('10.') || host.startsWith('192.168.') || host.startsWith('172.16.') || host.startsWith('172.17.') || host.startsWith('172.18.'); } ``` ```javascript if (!isLocalhostHost(HOST)) { console.error('❌ SECURITY: Server can only bind to localhost/private network addresses'); console.error(` HOST=${HOST} is not allowed`); console.error(' Use 127.0.0.1, localhost, or a private IP (10.x, 192.168.x, 172.16-18.x)'); process.exit(1); } ``` The server binds to the accepted address: ```javascript server.listen(PORT, HOST, () => { console.log(`📝 Markdown Editor with Chat`); console.log(` URL: http://${HOST}:${PORT}`); console.log(` Directory: ${MARKDOWN_DIR}`); console.log(` Chat: ${GATEWAY_URL ? 'enabled' : 'disabled'}`); console.log(''); console.log('Press Ctrl+C to stop'); }); ``` The request handler exposes file and chat operations without authentication or authorization: ```javascript if (pathname.startsWith('/api/files/')) { const filePath = pathname.slice('/api/files/'.length); if (req.method === 'GET') { const result = getFile(filePath); // ... } if (req.method === 'PUT' || req.method === 'POST') { const body = await readBody(req); const { ...[truncated 2430 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/server.mjs:147
Finding

Unbounded HTTP Request Buffering Enables Memory Exhaustion

Content
View full analysis
{ let body = ''; req.on('data', chunk => body += chunk); req.on('end', () => resolve(body)); req.on('error', reject); }); } ``` This helper is used by both file-write and chat endpoints: ```javascript const body = await readBody(req); const { content } = JSON.parse(body); ``` ```javascript const body = await readBody(req); const { messages } = JSON.parse(body); ``` ### Technical Analysis `readBody()` appends every incoming chunk to a JavaScript string without enforcing a maximum byte count. The server also does not reject oversized `Content-Length` values at this layer or abort a chunked request after a limit is exceeded. Repeated string concatenation can increase memory and CPU consumption. A sufficiently large request can exhaust the Node.js heap and terminate the process. A slow client may also hold connections and resources for an extended period because no request-body deadline is enforced in this code. The absence of API authentication increases exploitability when the service is bound to a non-loopback address. ### Attack Path 1. An attacker connects to a reachable instance. 2. The attacker sends a `PUT /api/files/example.md` or `POST /api/chat` request. 3. The body contains an extremely large JSON string, or it is streamed indefinitely using chunked transfer encoding. 4. Every chunk is concatenated into the in-memory `body` variable. 5. Memory and CPU usage continue to grow until the request ends, the runtime becomes unresponsive, or the process terminates from heap exhaustion. 6. Repeating the request or opening multiple concurrent connections increases the denial-of-service effect. ### Impact Assessment The attacker ...[truncated 313 chars]
Remediation
View remediation
{ const chunks = []; let received = 0; req.on('data', chunk => { received += chunk.length; if (received > maxBytes) { req.destroy(); reject(new Error('Request body too large')); return; } chunks.push(chunk); }); req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); req.on('error', reject); }); } ``` 3. Check `Content-Length` early and return HTTP `413 Payload Too Large` when it exceeds the configured limit. 4. Enforce request, header, body, and idle timeouts. 5. Limit concurrent requests and apply per-client rate limiting for remotely reachable deployments. 6. Validate the parsed JSON structure, message count, individual message length, and Markdown content type before further processing. 7. Handle oversized-body errors without attempting to continue parsing or write another response after the connection has been destroyed. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (10)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/server.mjs --folder /path/to/markdown --port 3333

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/server.mjs --folder /path/to/markdown --port 3333

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/server.mjs --folder /path/to/markdown --port 3333

Context Leakage

High
Category
Data Exfiltration
Confidence
93% confidence
Finding

This is a context-leakage issue: the chat workflow implicitly couples conversation requests with the editor's document state, causing unrelated sensitive content to be shared with the backend/model. In a markdown editor, users may store API keys, internal docs, or personal notes, so automatic inclusion of full file contents materially increases confidentiality risk.

Content

Scanner excerpt · scripts/index.html (reported line 854)May include surrounding context.

html
panel.classList.toggle('hidden');
    }
    
    // Send chat message
    async function sendChat() {
      const input = document.getElementById('chatInput');
      const message = input.value.trim();

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill metadata declares required environment variables and documents optional networked chat functionality, but it does not declare an explicit tool/permission scope such as allowed environment access or network access. This creates a trust and review gap: operators may install the skill without understanding that it can consume secrets from the environment and communicate with an external gateway, increasing the chance of unintended data exposure or over-privileged execution.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The chat feature sends the full current document content to /api/chat on every message, which expands the editor from a local filesystem tool into one that discloses user data to a backend service. Even if this is a product feature rather than malicious behavior, it creates a real confidentiality risk because sensitive notes may be transmitted without granular user control.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

There is no visible warning at send time that user chat queries automatically transmit the current file contents to the backend. Users may reasonably believe they are only sending their typed prompt, so the absence of disclosure can lead to unintentional exposure of secrets, drafts, credentials, or private notes.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code automatically embeds the entire current file in the system prompt for every chat request, regardless of whether the user intended to share it. This creates a direct sensitive-data disclosure channel to the model/backend and can leak confidential material through normal use of the chat feature.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This code performs filesystem writes and may create parent directories, but the only disclosure is an internal comment. There is no confirmation prompt or user-facing log at the point of save, and the visible startup output does not warn that API requests will modify files on disk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The chat proxy forwards user-provided messages to an external gateway using a server-side bearer token, which can expose potentially sensitive document content to a third party without explicit user disclosure in this server code. In the context of a local markdown editor, users may reasonably assume data stays local, so silently transmitting content off-host increases privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/server.mjs:72