Back to skill

Security audit

Incident Postmortem

Security checks for vulnerabilities and agentic risk

Overview

This skill coherently generates incident postmortem reports from user-provided files, but HTML output should only be used with trusted content.

Install only if you are comfortable letting the skill read incident files you explicitly provide. Prefer Markdown or JSON output for untrusted logs or incident data, and treat generated HTML as unsafe to open or host unless the inputs are trusted or the HTML is sanitized first.

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:416
Finding
Stored HTML and JavaScript Injection in Generated Reports## Vulnerability Details **File Location**: `scripts/generate_postmortem.py`, lines 416–490 **Vulnerability Type**: Unescaped user-controlled content in generated HTML **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="width=device-width, initial-scal ...[truncated 2865 chars]
Remediation
## Remediation Suggestions 1. **Escape untrusted content before constructing HTML.** Use `html.escape()` for text inserted into HTML, including the document title: ```python from html import escape safe_title = escape(str(title), quote=True) ``` 2. **Do not use regex substitutions as a Markdown security mechanism.** Use a maintained Markdown renderer configured to reject or escape raw HTML. 3. **Sanitize rendered HTML with an allowlist sanitizer.** Permit only required formatting tags and safe attributes. Remove at minimum: - `script`, `iframe`, `object`, `embed`, and `style` elements. - Inline event handlers such as `onclick` and `onerror`. - Dangerous URL schemes such as `javascript:`. - Unnecessary external-resource attributes. 4. **Keep values separate from markup.** Escape incident fields, timeline event text, action-item values, and log messages at the point where they enter an HTML text or attribute context. 5. **Add regression tests** covering payloads in every attacker-controlled source, including: - Incident title and summary. - Timeline event text. - Action-item cells. - Parsed log messages. - Closing-tag payloads such as `</title>`. - Event-handler payloads such as `<img src=x onerror=alert(1)>`. 6. **Apply defense in depth** when reports are served over HTTP by using a restrictive Content Security Policy, for example one that blocks inline scripts and limits resource origins. CSP should supplement, not replace, escaping and sanitization.
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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
89% confidence
Finding
The skill instructs use of local log files, output file generation, and references JSON/HTML processing, which implies file read and file write capability, yet it declares no explicit tool scope or permission boundaries. That creates unnecessary ambient authority: an agent may read arbitrary files or write outputs in unintended locations when the skill is invoked, increasing the risk of data exposure or filesystem misuse.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger phrases are very broad and match common operational language such as "incident report," "RCA," and "incident review," which can cause the skill to activate in contexts where the user did not intend log parsing or document generation. In an agent environment, over-broad activation can lead to unnecessary access to sensitive incident data or unintended file operations tied to the skill workflow.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The generated HTML hard-codes `lang="en"`, which imposes a specific language/locale in output regardless of the user's preferences or incident content. This matches the policy category for language or locale constraints without opt-in or documented justification.

Static analysis

No suspicious patterns detected.