Skip to content

feat: browser UI v2 — repository version picker + sanitized SKILL.md render (spec 003)#216

Merged
aroff merged 4 commits into
mainfrom
feat/browser-ui-v2
Jul 22, 2026
Merged

feat: browser UI v2 — repository version picker + sanitized SKILL.md render (spec 003)#216
aroff merged 4 commits into
mainfrom
feat/browser-ui-v2

Conversation

@aroff

@aroff aroff commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

The two deferred v2 items from spec 003 (browser skill management). Grilled the two consequential forks, built by parallel sonnet agents, opus-reviewed (verdict: SHIP, sanitization boundary double-verified with an adversarial probe).

1. Repository version picker

  • New read endpoint GET /api/v1/registry/skills/{id}/versions{ id, versions: [{version, repo?}] }, sorted newest-first (semver desc, unparseable lowest). Empty list → 200 (not 404) when no registry/unknown id.
  • Detail drawer shows a Version dropdown for repository-origin skills only (git/local/zip-url are versionless). Picking a version → POST /skills/update { skillId, version }, which pins the repository Origin's VersionConstraint and re-installs via add_from_origin(Update) — recorded in manifest + lock.
  • Guards: version without skillId → 400; version on a non-repository origin → 400. Update stays write-gated.

2. Sanitized SKILL.md render

  • GET /api/v1/skills/{id}/content?format=html|raw (default raw, unchanged). html renders Markdown server-side with comrak (safe mode, no raw-HTML passthrough) then allowlist-sanitizes with ammonia before returning.
  • Drawer gets a Raw / Rendered toggle (Raw = escaped <pre>, default). Rendered assigns the server-sanitized HTML via innerHTML — the one intentional, clearly-commented trust boundary; sanitization is entirely server-side (no client-side markdown parser or sanitizer, keeping the no-build ethos). Belt-and-suspenders rel="noopener noreferrer" pass over rendered links.

Security

Opus review confirmed the sanitization boundary sound and ran an adversarial probe (mixed-case javascript:, base64/svg data: URIs, <svg><script>, entity-encoded hrefs, <style>url(javascript:)) — every executable vector neutralized. Path-confinement on the content endpoint unchanged. Version string is parsed via VersionConstraint, never interpolated into a path or shelled. Test asserts a malicious SKILL.md payload is absent from rendered output.

Testing

Full workspace cargo test + clippy -D warnings green. New tests: versions endpoint (populated + empty + unknown-id), semver-desc sort, ?format=html sanitization (payload stripped), version-pinned update happy path (wiremock registry, asserts =2.0.0 pin) + both 400 guards.

Deps

Adds comrak + ammonia (Cargo deps; no build-step change).

Follow-up (not in this PR)

/registry/* handlers resolve the project from process cwd rather than AppState.project_file_path. Not a serve bug today (serve derives AppState from the same fixed cwd), but worth threading project_file_path through for consistency and to drop the test cwd-serialization — a latent mismatch only if a future --project override is added.

🤖 Generated with Claude Code

aroff and others added 4 commits July 22, 2026 19:45
… drawer (spec 003 v2)

Adds two additive drawer features on top of the existing browser console:
a Raw/Rendered toggle for SKILL.md (Rendered fetches ?format=html, trusting
the server's comrak+ammonia sanitization for the one intentional innerHTML
assignment), and a version picker for repository-origin skills only
(fetches /registry/skills/{id}/versions, applies via POST /skills/update
with { skillId, version }), gated on the existing writable capability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0153XpJc4DrMRAJJqVDchxRm
…r, version-pinned update (spec 003 v2)

Adds the three backend v2 add-ons for spec 003 browser skill management:
GET /api/v1/registry/skills/{id}/versions (sourced from PackageResolver over
the same SourcesManager the other /registry/* browse routes use, sorted
descending by semver), GET /api/v1/skills/{id}/content?format=html (comrak
render + ammonia allowlist-sanitize, safe for innerHTML), and version-pinned
POST /api/v1/skills/update via a new `version` field that resolves to an exact
VersionConstraint pin on a repository-origin skill's Origin.
@aroff
aroff merged commit 9735cf6 into main Jul 22, 2026
10 checks passed
@aroff
aroff deleted the feat/browser-ui-v2 branch July 22, 2026 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant