666ghj/MiroFish · error · StarHistoryError

generated SVG contains a forbidden attribute

Error message

generated SVG contains a forbidden attribute

What it means

Raised by `_validate_svg`'s attribute loop (scripts/star_history.py:1222) when an attribute name is namespaced (starts with `{`, e.g. `{http://www.w3.org/1999/xlink}href`) or is not in the per-element allowlist in `allowed_attributes`. The published SVG permits only reviewed, plain-named attributes on each element type — xlink-prefixed or novel attributes are rejected.

Source

Thrown at scripts/star_history.py:1222

                "href": OWNER_AVATAR_DATA_URI,
                "clip-path": "url(#clip-circle-title)",
            }
            watermark_attributes = {
                "x": "635",
                "y": "508.333",
                "width": "20",
                "height": "20",
                "href": WATERMARK_LOGO_DATA_URI,
            }
            if element.attrib == avatar_attributes:
                avatar_count += 1
            elif element.attrib == watermark_attributes:
                watermark_count += 1
            else:
                raise StarHistoryError("generated SVG contains an unreviewed image")
        for raw_name, value in element.attrib.items():
            if raw_name.startswith("{") or raw_name not in allowed_for_element:
                raise StarHistoryError("generated SVG contains a forbidden attribute")
            if (
                not value.isascii()
                or "\\" in value
                or "/*" in value
                or "*/" in value
                or any(ord(character) < 0x20 for character in value)
            ):
                raise StarHistoryError("generated SVG contains an unsafe attribute value")
            exact_values = exact_attribute_values.get(raw_name)
            if exact_values is not None and value not in exact_values:
                raise StarHistoryError("generated SVG contains an unsafe attribute value")
            if raw_name == "href":
                if local_name != "image" or value not in {
                    OWNER_AVATAR_DATA_URI,
                    WATERMARK_LOGO_DATA_URI,
                }:
                    raise StarHistoryError("generated SVG contains an external resource")
            lowered = value.lower().replace(" ", "")

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Use the SVG2 plain attribute names the template already uses — `href`, not `{xlink}href`.
  2. In forks, add any new attribute to the target element's set in `allowed_attributes` (and to `exact_attribute_values` if it must be pinned), after review.
  3. Avoid editor round-trips: Inkscape adds `id`/`data-*` everywhere and this check will fail.
  4. Print `element.attrib` for the failing element (catch StarHistoryError around `_validate_svg` in a debug harness) to see exactly which name was rejected.

Example fix

# before: namespaced attribute serializes as {ns}href
link.set("{http://www.w3.org/1999/xlink}href", uri)

# after: plain SVG2 attribute, already allowlisted
link.set("href", uri)
Defensive patterns

Strategy: try-catch

Validate before calling

def plain_attribute_names(payload: bytes) -> bool:
    root = ET.fromstring(payload)
    return not any(name.startswith("{") for el in root.iter() for name in el.attrib)

Try / catch

try:
    _validate_svg(payload)
except StarHistoryError as exc:
    if "forbidden attribute" in str(exc):
        raise ValueError("attribute is namespaced or not allowlisted for this element") from exc
    raise

Prevention

When it happens

Trigger: Serializing with ElementTree while elements carry namespaced attributes (classic `{xlink}href` when code sets `"{http://www.w3.org/1999/xlink}href"`), fork code adding unlisted attributes like `opacity`, `class`, `id`, or `filter`, or attributes valid on one element used on another (e.g. `font-size` on `<rect>`).

Common situations: Forks porting markup that uses SVG 1.1 `xlink:href`; adding CSS hooks (`class`) for downstream styling; editors that attach `id`/`data-name` attributes to every node; using `href` on an element whose allowlist set lacks it.

Understand the failure class

Related errors


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/6e65556f59657648. Report an issue: GitHub.