mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-23 11:28:14 +00:00
fix(composer): coerce prefixed colour channels, non-finite numbers, marquee ids
Three more routes into the generated source, plus a fix to one of my own tests
that was checking the wrong branch.
Prefixed colour channels were interpolated raw
----------------------------------------------
Five tuples were built without coercion:
p['fill_tuple'] = f"({el.get('r', 100)}, {el.get('g', 200)}, ...)"
p['empty_tuple'] = f"({el.get('emptyR', 50)}, ...)"
p['label_tuple'] = f"({el.get('labelR', 200)}, ...)"
so progress_bar, pips, sparkline and gauge took arbitrary expressions the same
way width/height did. Confirmed: every one of the five put __import__ into the
generated source. They now go through a new _rgb_tuple helper, which _rgb_expr
also delegates to.
The pre-existing colour test only covered r/g/b on a text element, which is why
the prefixed channels and these four types were never exercised.
Non-finite numbers escaped as a 500
-----------------------------------
json.loads accepts Infinity/-Infinity/NaN by default and Flask's get_json
passes them straight through, so a payload can hand _safe_int a non-finite
float. int(inf) raises OverflowError, which is neither ValueError nor
ComposerInputError, so it escaped both handlers and surfaced as a 500 with a
traceback rather than a 422. Verified end to end through Flask's parser.
Marquee ids reached the source as identifiers
---------------------------------------------
data_key is spliced UNQUOTED into variable names (_{{ data_key }}_text = ...)
and only '-' was normalised. A punctuated id landed in the generated source as
code. ast.parse caught it, so this was not exploitable, but the caller got an
opaque "Generated code has a syntax error" instead of being told the id was
unusable -- the same failure mode as the empty-block bug. Now restricted to
identifier characters and bounded to 64.
The line-anchor test was testing the wrong branch
-------------------------------------------------
test_line_branch_applies_the_anchor_offset searched the whole file for
"case 'line': {". getBoundingBox has one too and comes first, so the assertion
was reading the bounding-box branch: stripping the anchor offset from
_drawElement left all 11 checks green. Both line tests are now scoped to their
own function via tree-sitter, so they cannot be satisfied by the same branch.
Tests: 35 of the injection suite's checks fail against the reverted fixes; the
scoped line test fails when _drawElement's offset is removed. Full suite 4059
passed, the one failure being test_install_lowmem (pre-existing, awaiting #492).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STMbQE4YctTacQXfbYqKuW
This commit is contained in:
co-authored by
Claude Opus 5
parent
acc55ef119
commit
37fc1b56b5
@@ -96,18 +96,28 @@ def test_element_strokes_scale_with_scale():
|
||||
|
||||
|
||||
def test_line_branch_applies_the_anchor_offset():
|
||||
text = CANVAS.read_text()
|
||||
line_branch = text[text.index("case 'line': {"):]
|
||||
"""Scoped to _drawElement.
|
||||
|
||||
getBoundingBox has its own `case 'line': {` and appears first in the file,
|
||||
so searching the whole text found *that* branch -- this assertion passed
|
||||
with the draw branch's anchor offset removed. Verified: stripping it and
|
||||
re-running gave 11/11 green.
|
||||
"""
|
||||
body = _function_source(CANVAS, "_drawElement")
|
||||
line_branch = body[body.index("case 'line': {"):]
|
||||
line_branch = line_branch[:line_branch.index("case 'divider'")]
|
||||
assert "ax - el.x0" in line_branch and "ay - el.y0" in line_branch, \
|
||||
"line drawing ignores xAnchor/yAnchor"
|
||||
assert "el.x0 * s" not in line_branch, "line still drawn from unanchored endpoints"
|
||||
assert "moveTo(el.x0 * s" not in line_branch, \
|
||||
"line still drawn from unanchored endpoints"
|
||||
|
||||
|
||||
def test_line_bounding_box_applies_the_anchor_offset():
|
||||
text = CANVAS.read_text()
|
||||
i = text.index("case 'line':", text.index("getBoundingBox"))
|
||||
box = text[i:i + 400]
|
||||
"""The companion to the above: scoped to getBoundingBox specifically, so
|
||||
the two tests cannot both be satisfied by the same branch."""
|
||||
body = _function_source(CANVAS, "getBoundingBox")
|
||||
box = body[body.index("case 'line'"):]
|
||||
box = box[:box.index("case 'divider'")]
|
||||
assert "ax - el.x0" in box, "line bounding box ignores the anchor"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user