office-hours wireframe render silently fails because browse blocks file:// URLs
- Dominant language
- TypeScript
- Stars
- 133k
- Forks
- 19.9k
- Avg merge
- 18h 46m
- Merged PRs (30d)
- 26
Description
## Summary
The office-hours skill generates a wireframe HTML file at `/tmp/gstack-sketch-*.html`, then tries to render it with:
```bash
$B goto "file://$SKETCH_FILE"
$B screenshot /tmp/gstack-sketch.png
```
This silently fails because the browse binary blocks `file://` URLs (added in #17 to prevent SSRF/local resource access). The skill hits the fallback path ("skip the render step") and moves on without telling the user what happened.
## Root Cause
`validateNavigationUrl()` in the browse binary rejects any URL with a `file:` scheme. This is correct for untrusted external content, but the office-hours skill generates the HTML itself — the file is trusted local content.
The skill's fallback message says:
> "Visual sketch requires the browse binary. Run the setup script to enable it."
This is misleading — the browse binary IS set up and working. The issue is the URL scheme, not the binary.
## Reproduction
1. Run `/office-hours` on any project with a UI component
2. Let it reach the "Visual Sketch" section where it generates wireframe HTML
3. Observe: `$B goto "file:///tmp/gstack-sketch-*.html"` exits with code 1 and the error `Blocked: scheme "file:" is not allowed`
4. The skill silently skips the render, screenshot, and "present and iterate" steps
## Workaround
Serve the file over HTTP before navigating:
```bash
python3 -m http.server 18923 --directory /tmp --bind 127.0.0.1 &
$B goto "http://127.0.0.1:18923/gstack-sketch-1234.html"
$B screenshot /tmp/gstack-sketch.png
kill %1
```
This works perfectly — the browse binary renders and screenshots the wireframe without issues.
## Suggested Fix
In the office-hours skill template (`office-hours/SKILL.md.tmpl`), replace the direct `file://` navigation with a temporary HTTP server pattern:
```bash
SKETCH_PORT=$(shuf -i 18000-19000 -n 1)
python3 -m http.server "$SKETCH_PORT" --directory "$(dirname "$SKETCH_FILE")" --bind 127.0.0.1 &
_HTTP_PID=$!
sleep 1
$B goto "http://127.0.0.1:$SKETCH_PORT/$(basename "$SKETCH_FILE")"
$B screenshot /tmp/gstack-sketch.png
kill $_HTTP_PID 2>/dev/null
```
Alternatively, the browse binary could allowlist `file://` URLs that are under `/tmp/gstack-*` since the skill itself generates them and they're trusted.
## Related
- #17 — original SSRF fix that added `file://` blocking
- #340 — user-facing symptoms of this bug (wireframes never rendered, "present and iterate" step skipped)
## Environment
- macOS (aarch64-darwin)
- gstack browse binary built and working (verified with `http://` URLs)
- bun, Google Chrome available via Nix
Contributor guide
Assessment
This issue has not been assessed yet.