T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/render-ontology.js:40
- Finding
- Unescaped Ontology Names Allow Mermaid Diagram Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/render-ontology.js:40-45, 102-104` **Vulnerability Type**: Mermaid syntax injection caused by unescaped user-controlled labels **Risk Level**: Medium ### Vulnerable Code ```js // Extract objects (## headers with IDs) const objectRegex = /^## ([A-Z0-9]+) — (.+)$/gm; let match; while ((match = objectRegex.exec(content)) !== null) { objects.push({ id: match[1], name: match[2].substring(0, 30) + (match[2].length > 30 ? '...' : ''), type: type }); } ``` ```js // Add nodes for (const obj of objects) { const shape = obj.type === 'core-self' ? `((${obj.name}))` : `[${obj.name}]`; lines.push(` ${obj.id}${shape}`); } ``` ### Technical Analysis The renderer reads object names from ontology Markdown headings and directly interpolates those names into Mermaid source code. Although names are truncated to 30 characters, truncation does not escape Mermaid metacharacters such as `]`, `)`, `"`, `;`, or comment markers. An object name can therefore terminate its intended node label and append another Mermaid statement. For example, a crafted heading could contain a short payload resembling: ```markdown ## B1 — X];click B1 "https://x";%% ``` The generated Mermaid source would contain attacker-influenced graph syntax rather than treating the entire value as plain label text. The actual behavior of injected directives depends on the Mermaid implementation and its security configuration. Strict renderers may disable active links or scripts, but injected graph nodes, edges, styling, or links can still compromise the integrity of the visualization. A permissive downstream renderer may expose a greater risk. The affected data normally comes from local ontology files, and the documented bootstrap process requires user confirmation before committing extracted objects. This reduces exploitability but does not provide syntactic validation: imported content, manually edited ontology files, or inco ...[truncated 1564 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat object IDs and labels as separate data types: - Continue enforcing a strict identifier allowlist such as `^[A-Z][A-Z0-9]*$`. - Encode labels before inserting them into Mermaid syntax. 2. Escape or reject all Mermaid control characters in labels, including: - Square and round brackets. - Quotes and backticks. - Semicolons. - Newline and carriage-return characters. - Mermaid comment markers and directive syntax. 3. Prefer JSON-style quoted Mermaid labels with explicit escaping. For example: ```js function escapeMermaidLabel(value) { return String(value) .replace(/[\r\n]/g, ' ') .replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/[\[\]();`]/g, ''); } for (const obj of objects) { const label = escapeMermaidLabel(obj.name); const shape = obj.type === 'core-self' ? `(("${label}"))` : `["${label}"]`; lines.push(` ${obj.id}${shape}`); } ``` 4. Validate names before rendering and fail closed when a label contains unsupported control syntax rather than silently rendering it. 5. Configure every downstream Mermaid renderer with its strictest available security mode. Disable JavaScript URLs, click callbacks, external resource loading, and unsafe SVG content. 6. If generated SVG files are displayed in a browser or web application, sanitize them with a maintained SVG sanitizer and serve them with a restrictive Content Security Policy. 7. Add regression tests for labels containing `]`, `)`, `"`, `;`, `%%`, `click`, Mermaid initialization directives, and embedded line breaks. Tests should verify that these values remain inert text and cannot create additional graph statements. ]]>
