Back to skill

Security audit

incident-postmortem

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent incident-report generator, but its HTML output can preserve executable markup from incident data or logs, which is risky for generated reports.

Install only if you are comfortable using it with trusted inputs or markdown/JSON output. Avoid opening generated HTML reports from untrusted incident files or logs until the HTML renderer escapes or sanitizes all user-controlled fields and the title.

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
scripts/generate_postmortem.py:383
Finding
Stored HTML and JavaScript Injection in Generated Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_postmortem.py`, lines 383-466 **Vulnerability Type**: Stored HTML injection / cross-site scripting in generated HTML reports **Risk Level**: Medium ### Vulnerable Code ```python def generate_html(markdown_content, title): """Wrap markdown content in a simple HTML template.""" # Simple markdown-to-HTML conversion for key elements html = markdown_content # Headers html = re.sub(r'^# (.+)$', r'<h1>\1</h1>', html, flags=re.MULTILINE) html = re.sub(r'^## (.+)$', r'<h2>\1</h2>', html, flags=re.MULTILINE) html = re.sub(r'^### (.+)$', r'<h3>\1</h3>', html, flags=re.MULTILINE) # Bold html = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', html) # Italic html = re.sub(r'_(.+?)_', r'<em>\1</em>', html) # Code html = re.sub(r'`(.+?)`', r'<code>\1</code>', html) # Lists html = re.sub(r'^- (.+)$', r'<li>\1</li>', html, flags=re.MULTILINE) # Tables (simple conversion) def convert_table(match): lines = match.group(0).strip().split('\n') rows = [] for i, line in enumerate(lines): if '---' in line: continue cells = [c.strip() for c in line.strip('|').split('|')] tag = 'th' if i == 0 else 'td' row = ''.join(f'<{tag}>{c}</{tag}>' for c in cells) rows.append(f'<tr>{row}</tr>') return f'<table>{"".join(rows)}</table>' html = re.sub(r'(\|.+\|(?:\n\|.+\|)*)', convert_table, html) # Paragraphs (lines not already wrapped) lines = html.split('\n') processed = [] for line in lines: if line.strip() and not line.strip().startswith('<') and not line.strip().startswith('*'): processed.append(f'<p>{line}</p>') else: processed.append(line) html = '\n'.join(processed) return f"""<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="wid ...[truncated 3452 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. HTML-escape every untrusted scalar before inserting it into HTML, including the title, incident metadata, timeline event text, action items, and log messages. Use `html.escape(value, quote=True)` where direct interpolation is unavoidable. 2. Replace the regular-expression Markdown conversion with a maintained Markdown renderer configured to disable or escape raw HTML. 3. If raw HTML must be supported, sanitize the rendered output with a strict allowlist. Remove at minimum: - `<script>`, `<iframe>`, `<object>`, `<embed>`, and similar active elements. - Event-handler attributes such as `onclick`, `onerror`, and `onload`. - Dangerous URL schemes such as `javascript:` and unsafe `data:` URLs. - Unexpected SVG or MathML elements and attributes. 4. Construct the document title from an escaped value: ```python import html safe_title = html.escape(str(title), quote=True) ``` 5. Treat log messages as plain text rather than markup, because logs commonly contain attacker-controlled request data. 6. Consider adding a restrictive Content Security Policy to generated reports as defense in depth: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:"> ``` This should supplement output encoding and sanitization, not replace them. 7. Add regression tests covering payloads in every input channel, including: - Incident titles and summaries. - Timeline event text. - Action-item table cells. - Parsed log messages. - Closing-tag payloads such as `</title>`. - Event handlers and dangerous URL schemes. The tests should verify that payloads appear only as encoded text and cannot create executable DOM elements or attributes. ]]>
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill documents behaviors that imply filesystem read/write access (`--log`, `--timeline`, `--from`, `-o`, `--check-blame`) and potentially network-capable code according to the analyzer, but it does not declare any explicit tool scope or permission boundaries. Undeclared capabilities make it harder for the runtime or reviewer to enforce least privilege, increasing the risk of overbroad file access, unsafe output writes, or unexpected external communication if the backing script supports it.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The HTML template sets <html lang="en"> unconditionally, which imposes a specific language/locale choice in generated output. The file does not provide any option for users to select another language or document why English is required.

Missing User Warnings

Low
Confidence
93% confidence
Finding
This code creates parent directories and writes report content to the path supplied by --out, which is a file-system modifying operation. While the behavior is part of report generation, there is no confirmation prompt and no explicit disclosure near the write path that the script will create directories and write a file rather than only printing to stdout.

Static analysis

No suspicious patterns detected.