T09 · Insecure Skill Coding Practices
- Location
adapters/rendering.py:217- Finding
Remote SVG Content Is Embedded into Browser-Viewed HTML Without Sanitization
- Content
View full analysis
Vulnerability Details
File Location:
adapters/rendering.py:217-261, with the reachable invocation path atadapters/rendering.py:817-836andscripts/samplesize_power.py:850-872
Vulnerability Type: Active-content injection through untrusted SVG
Risk Level: MediumComplete Code Snippet
python # adapters/rendering.py:217-261 def build_figure_widget( figures: list, titles: list[str], pad: float = 8.0, pad_y: float = 24.0 ) -> str: """figures: [{svg, type}, ...] → complete inline HTML fragment.""" blocks = [] for fig, t in zip(figures, titles): inner, vb = extract_svg(fig.get("svg") or "") inner = _strip_clip(inner) min_x, min_y, max_x, max_y = content_bbox(inner, pad=pad, pad_y=pad_y) vb_fit = f"{min_x:g} {min_y:g} {max_x - min_x:g} {max_y - min_y:g}" w = max_x - min_x blocks.append( f'<div><div style="font-size:16px;font-weight:600;margin:0 0 6px;' f'color:var(--color-text-primary);">{t}</div>' f'<div style="overflow-x:auto;max-width:100%;background:#fff;' f'border:0.5px solid var(--color-border-tertiary);' f'border-radius:var(--border-radius-md);">' f'<svg viewBox="{vb_fit}" style="width:{w:g}px;height:auto;display:block;' f'margin:0 auto;">' f"{_wrap_points(inner)}</svg></div></div>" )python # adapters/rendering.py:817-836 svg_figs, titles = [], [] for i, f in enumerate(inline, 1): svg_figs.append({"svg": f.get("content", "")}) ftype = f.get("type") or "" grp = type_names.get(ftype) titles.append(f"{grp} — {f.get('caption')}" if grp else (f.get("caption") or "Figure %d" % i)) widget = build_figure_widget(svg_figs, titles) figures_card = ( '<section class="card"><h2>Figures (%d)</h2>' % len(inline) + widget + "</section>" )python # scripts/samplesize_power.py:850-872 render_figures(res.figures, test, figure_mod ...[truncated 3961 chars]- Remediation
View remediation
Remediation Suggestions
-
Sanitize SVG with a strict allowlist parser
- Permit only required graphical elements such as
svg,g,path,rect,line,circle,polyline,polygon, andtext. - Permit only known-safe presentation and geometry attributes.
- Reject scripts, animation elements,
foreignObject, embedded HTML, and unknown namespaces. - Remove every
on*event-handler attribute.
- Permit only required graphical elements such as
-
Restrict URL-bearing attributes
- Reject
javascript:,data:, remote HTTP(S), and other active or external URL schemes unless explicitly required and safely handled. - Disallow external references in
href,xlink:href, CSSurl(...), image references, and filter resources.
- Reject
-
Escape captions
- Apply
_html_escape()to every remote caption before interpolation. - Treat captions exclusively as text rather than trusted markup.
- Apply
-
Prefer non-active rendering
- Rasterize remote SVG into PNG using a hardened converter before browser display.
- If SVG must remain interactive, place it in a sandboxed iframe without
allow-scriptsorallow-same-origin.
-
Apply browser containment
- Add a restrictive Content Security Policy that blocks scripts, plugins, frames, external connections, and remote resources.
- Avoid inline scripts in the report so that
script-src 'none'can be enforced. - Where the host controls the viewer, assign generated reports an isolated opaque origin.
-
Validate before persistence and display
- Perform validation immediately after parsing the remote response.
- Reject unsafe figures rather than saving and advertising them through the
__FIGURE__marker. - Record a non-sensitive warning explaining that an unsafe remote figure was omitted.
-
Add regression tests
- Test payloads containing
<script>,onload,foreignObject, unsafehref, CSS URLs, malformed nested SVG, and caption-based tag injection. - Assert that no active content survives into ...[truncated 15 chars]
- Test payloads containing
-
