CodeQL flagged 16 high-severity "path depends on user-provided value"
alerts. Investigated each:
- install_locally() (/api/install) built a filesystem path from
metadata.id without validating it at that point -- it was only
implicitly safe because _generate_plugin_files() validates the same
field (re-extracted independently) earlier in the same request. That's
a real gap: reorder or change that earlier call and it's an exploitable
path traversal / arbitrary file write. Fixed by validating plugin_id
directly against _PLUGIN_ID_RE at the point the path is built, matching
the pattern already used correctly in validate_id() and load_plugin().
- The other 10 flagged locations (serve_font's allowlist check,
validate_id, load_plugin and its downstream reads) were already
guarded by an explicit check earlier in the same function -- false
positives from CodeQL not modeling those as sanitizers.
Also fixed 2 of the 5 "stack trace exposed" warnings that were genuine:
install_locally() and load_plugin() returned raw OSError/Exception text
to the client in a 500 response; now logged server-side with a generic
client-facing message. The other 3 (generate_zip/install_locally/
preview_code returning str(ValueError) from _generate_plugin_files) are
deliberate, human-authored validation messages, not exception internals
-- left as-is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
- Dropped a pointless f-string prefix (no placeholders) on the default
plugin description.
- Replaced two bare except:pass/continue blocks (manifest.json listing,
config_schema.json parsing) with a logged warning before falling
through to the same skip-this-entry behavior -- same control flow,
now visible in logs instead of silent.
Skipped as false positives (verified against actual usage, not fixed):
- Jinja2 Environment(autoescape=False) -- this env renders manager.py.j2,
a Python source-code generator, never HTML; autoescaping would corrupt
generated code. Flagged by a generic XSS rule that assumes all Jinja2
environments render HTML.
- "Flask route directly returning a formatted string" on _as_rgb_filter
-- that's a Jinja *filter* function, not a Flask route.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
Web UI (/composer/) for building a working LEDMatrix plugin without
writing Python: drop elements (text, time, date, countdown, scrolling
text, bar/waveform, groups, custom config variables) onto a canvas
matching the real panel's pixel grid, configure them with live preview,
then generate a real plugin (manager.py + manifest.json + config_schema.json)
from manager.py.j2 -- downloadable as a ZIP or installed directly.
NOTE: composer_bp is not yet registered in web_interface/app.py, so this
blueprint is currently inert. Split out of the original chore/dead-code-
removal commit, which had accidentally bundled this in alongside unrelated
dead-code deletions; app.py registration was not part of that commit
either and still needs to be added before this is reachable.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ