feat: support manifest-driven plugin iframe embeds#4033
Conversation
|
| Filename | Overview |
|---|---|
| api/routes.py | Adds manifest-driven iframe inlining helpers. The name_escaped_for_path variable uses only replace('"','') instead of html.escape() for URL path construction inside HTML attributes, creating an HTML-injection vector in the plugin iframe page. |
| tests/test_plugins_panel.py | Adds a behavioral integration test exercising the full inline-assets-and-payload path including a symlink scenario, and a source-inspection test asserting no hard-coded plugin names. Both tests are correctly structured. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[GET /plugin-tab-path] --> B{dashboard_dir exists?}
B -- No --> Z[Return False / 404]
B -- Yes --> C[Resolve dashboard_path]
C --> D{dist/index.html exists?}
D -- Yes --> E[read_text to _inject_plugin_payload]
D -- No --> F{static/index.html exists?}
F -- Yes --> E
F -- No --> G{dist/index.js exists?}
G -- Yes --> H[Generate HTML shell]
G -- No --> Z
E --> I[_inline_declared_dist_assets]
I --> J{inline_dist_assets == True?}
J -- No --> K[Return html unchanged]
J -- Yes --> L[Inline CSS]
L --> M[Inline JS]
M --> N[Return inlined HTML]
E --> O[Rebase href/src if not inlining]
O --> P{payload_script and not already injected?}
N --> P
P -- Yes --> Q[Insert payload before first script tag]
P -- No --> R[Return html_text]
Q --> R
R --> S[Encode UTF-8 and send 200]
H --> S
Reviews (10): Last reviewed commit: "ci: retrigger flaky gateway sync shard" | Re-trigger Greptile
38424e8 to
c6f805e
Compare
670ee31 to
1b29211
Compare
Review — manifest-driven embed is well-guarded; a couple of notes on the inline escapingReading the new plugin-page block at Path containment is correctAll three inline helpers resolve candidate files and then re-anchor them under the plugin root before reading. For the payload ( payload_path = (_dashboard_path / rel).resolve()
try:
payload_path.relative_to(_dashboard_path)
except ValueError:
continueSame CSP is preservedThe Script/style breakout escaping — two observationsThe payload and JS inlining use the standard encoded = json.dumps(payload, ensure_ascii=False).replace("</", "<\\/")
...
script_text = script_path.read_text(...).replace("</script", "<\\/script")Two small things worth a look (neither blocking):
Re-injection guard
VerdictNo blockers. Containment, CSP, and the manifest-driven contract are all sound, and the behavioral test covers the real serving path rather than asserting on string fragments alone. If you want to tighten the CSS path, escaping only the literal |
173e0cb to
6e46b9d
Compare
6e46b9d to
ac72630
Compare
Summary
webui.inline_payloadwebui.inline_dist_assetsstyle.css/index.jsreferences to authenticated dashboard-plugin asset routes without special-casing any plugin nameContract Routing
Task type: dashboard plugin serving enhancement
Touched areas: plugin iframe page route (
/dashboard-plugins/<name>/page) and plugin panel static guardsScope boundaries: manifest-driven, read-only embedding for sandboxed plugin pages; no Project Cockpit/TARS-specific endpoint or core Insights UI changes included.
Test plan
python -m pytest tests/test_plugins_panel.py -q -o addopts=→ 33 passedpython -m py_compile api/routes.pygit diff --check origin/master...HEAD