T09 · Insecure Skill Coding Practices
- Location
scripts/flowchart.py:693- Finding
Stored HTML/SVG Injection Through Unescaped Workflow Fields
- Content
View full analysis
{lp.label}') ``` The loop label is inserted directly into an inline SVG element without calling `_xml_escape()`. #### Unvalidated legend attributes (`scripts/flowchart.py:730-736`) ```python for it, w in zip(items, item_widths): fill = it["fill"] stroke = it["stroke"] parts.append(f' ') parts.append(f' {_xml_escape(it["label"])}') cur += w + gap ``` Custom `legend[].fill` and `legend[].stroke` values are embedded in quoted SVG attributes without validation or escaping. ### Technical Analysis The generator treats `nodes.json` as structured input but does not consistently encode its fields for their destination contexts. Most text fields are passed through `_xml_escape()`, but `loops[].label` is inserted as raw SVG markup. An input such as: ```json { "loops": [ { "from": "end", "to": "start", "label": "", "path": "left_edge" } ] } ``` can close the intended `` element and introduce attacker-controlled SVG or HTML elements into the generated document. Custom legend colors create a separate attribute-injection sink. Because values are placed inside `fill="..."` and `stroke=" ...[truncated 1768 chars]- Remediation
View remediation
{_xml_escape(lp.label)}' ) ``` 2. Strictly validate custom legend colors. Accept only explicitly supported CSS color formats, such as six-digit hexadecimal values: ```python import re COLOR_RE = re.compile(r"^#[0-9A-Fa-f]{6}$") def _validate_color(value: str) -> str: if not COLOR_RE.fullmatch(value): raise ValueError(f"Invalid legend color: {value!r}") return value ``` Apply this validation to both `legend[].fill` and `legend[].stroke`. 3. Do not rely only on generic XML escaping for color fields. Validation against an allowlist is preferable because those fields are intended to contain colors, not arbitrary attribute text. 4. Consider replacing the custom escaping function with the standard library implementation: ```python from html import escape safe_value = escape(value, quote=True) ``` 5. Add regression tests covering: - `` in loop labels. - Double and single quotation marks in legend fields. - SVG event attributes such as `onload`. - Markup-breaking values in `fill` and `stroke`. - Ampersands and angle brackets in every user-controlled text field. 6. Add a restrictive Content Security Policy to generated HTML as defense in depth. For a self-contained document that requires no JavaScript or network resources, an appropriate baseline is: ```html ``` Output encoding and input validation remain mandatory even when a Content Security Policy is present. ]]>
