Skip to content

feat(studio): web UI for raptor under packages/studio/ - #215

Open
yesnet0 wants to merge 6 commits into
gadievron:devfrom
yesnet0:studio
Open

feat(studio): web UI for raptor under packages/studio/#215
yesnet0 wants to merge 6 commits into
gadievron:devfrom
yesnet0:studio

Conversation

@yesnet0

@yesnet0 yesnet0 commented Apr 24, 2026

Copy link
Copy Markdown

Summary

A FastAPI + Jinja2 web UI that reads and writes raptor's project data, triggers all pure-Python and Claude-backed command kinds via a SQLite job queue with live SSE log streaming, and renders raptor's full finding schema inline.

Launch from the repo root:

pip install -r requirements.txt     # includes studio's five new deps
python3 raptor_studio.py            # → http://127.0.0.1:8765

Developed in the open at https://github.com/yesnet0/raptor-studio over 27 commits with screenshots, tests, and a full PRD / changelog. This PR squashes that history for reviewability; the companion repo keeps the full timeline.

Feature surface

Browsing

  • Dashboard with cross-project KPIs (or welcome state when no projects exist)
  • Findings with raptor's full schema rendered inline — final_status / verdict × impact / Stage E feasibility (protections, exploitation_paths, chain_breaks tagged [source]/[binary], what_would_help) / PoC / Source-Sink-Flow proof
  • Per-finding relevant-persona cards ranked by vuln category, tool, status, filename hints — capped at 4
  • Per-run detail: kind-aware artifact summary with scan metrics, fuzzing report, validation bundle counts, inline CodeQL dataflow_*.svg diagrams, OSS-forensics walkthrough (evidence-source count, hypothesis timeline with confirmed/rejected badges, final forensic report — all rendered from markdown)
  • Run diff: classify findings across two runs into new / carried / resolved with status + verdict transition arrows
  • Exploits / Patches / Reports / Activity browsers, each walking the run dirs' well-known subdirs
  • /personas global browser (10 expert briefs from tiers/personas/) + /glossary explaining schema terms
  • Three-lane IA (source / binary / forensics) with type-adaptive sidebar

Triggering

  • Create project (3 types, progressive-disclosure form)
  • SQLite-backed job queue at $STUDIO_DATA_DIR/jobs.db + daemon-thread worker
  • Pure-Python kinds: scan, agentic, codeql, fuzz → spawn python3 raptor_*.py
  • Claude-backed kinds: understand, validate, oss-forensics, crash-analysis → wrap as bash -c "raptor project use <name> && claude -p '<slash-cmd>'"
  • Live log streaming via SSE (/api/jobs/{id}/stream)
  • Cancel SIGTERMs the process group
  • Every trigger form shows the Equivalent CLI for reproducibility

Configuration

  • /settings edits ~/.config/raptor/models.json in raptor's exact 4-role schema
  • Env-var fallback status table (ANTHROPIC_API_KEY, etc.)
  • RAPTOR_MAX_COST display

Schema preservation

Projects created via the UI write raptor's 7-field project.json schema exactly — indistinguishable from raptor project create. Studio-only metadata (type, source_repo, focus, vendor_report_url, language, corpus_dir) lives in $STUDIO_DATA_DIR/project-extras/<name>.json. Raptor's CLI ignores the sidecar. A round-trip test imports core/project/schema.py::validate_project and runs it against studio output.

Finding schema rendered verbatim from raptor's output — no reinterpretation. SARIF 2.1.0 fallback when no findings.json exists.

Tests

python -m pytest packages/studio/tests/
# 161 passed, 1 skipped

17 test modules including test_worker_integration which actually spawns subprocesses to cover completed / failed / cancelled / missing-executable paths end-to-end (~8.5s total).

Architecture

packages/studio/
├── app.py                  # FastAPI entry, ~20 routes
├── config.py               # env-driven runtime paths
├── services/               # 14 modules — readers, writers, classifiers,
│                           #   SQLite jobs, subprocess worker, SARIF,
│                           #   markdown render, personas, diff, forensics
├── templates/              # 23 Jinja2 templates, inline CSS, dark + light
├── static/                 # transparent-bg pixel-art velociraptor
├── tests/                  # 17 modules (incl. live subprocess)
├── scripts/                # seed_demo.py, process_avatar.py
└── docs/
    ├── PRD.md              # product scope + invariants
    ├── CHANGELOG.md        # commit-by-commit narrative
    └── UX_RECONCILIATION.md    # design narrative

Services are provider-agnostic and do not import raptor's runtime except services/raptor_version.py which imports RaptorConfig.VERSION directly. Root launcher raptor_studio.py mirrors the raptor_agentic.py / raptor_codeql.py / raptor_fuzzing.py pattern — sys.path bootstrap + argparse + uvicorn.run.

Non-goals (deliberate)

  • Auth / multi-user — raptor is single-user by design
  • Replacing the CLI — every UI action surfaces its Equivalent CLI so power users can reproduce outside the browser; the UI is additive, not a wall

New dependencies (requirements.txt)

fastapi>=0.110.0
uvicorn[standard]>=0.27.0
jinja2>=3.1.0
python-multipart>=0.0.9
markdown>=3.5

All pure-Python (uvicorn's C extensions are optional via httptools).

Test plan

  • 161 pytest pass (incl. 4 live-subprocess worker tests)
  • Schema round-trip: projects created via UI validated by raptor's validate_project
  • TestClient smoke: /, /personas, /glossary, /api/health all 200
  • File-serving route: path-traversal returns 403; disallowed extension returns 403
  • SSE log stream closes cleanly on job terminal status
  • Manual acceptance on maintainer's machine

yesnet0 added 2 commits April 24, 2026 00:24
A FastAPI + Jinja2 web UI that reads and writes raptor's project data,
triggers all pure-Python and Claude-backed command kinds via a SQLite
job queue with live SSE log streaming, and renders raptor's full
finding schema — final_status, verdict × impact, Stage E feasibility,
chain_breaks, exploitation_paths, CodeQL dataflow SVGs inline, OSS
forensics walkthrough.

Launch from the raptor repo root:

    pip install -r requirements.txt
    python3 raptor_studio.py          # → http://127.0.0.1:8765

## Feature surface

Browsing
- Dashboard with cross-project KPIs (or welcome state when empty)
- Findings with raptor's full schema (final_status / verdict × impact
  / Stage E feasibility / chain_breaks / exploitation_paths / PoC)
- Per-run detail: kind-aware artifact summary, scan metrics, fuzzing
  report, validation bundle counts, inline CodeQL dataflow SVGs,
  OSS forensics walkthrough (evidence, hypothesis timeline, final
  report — all markdown-rendered)
- Diff two runs by (file, line, normalized_vuln_type) identity
- Exploits / Patches / Reports / Activity browsers
- /personas global browser + /glossary with grouped concept cards
- Three-lane IA (source / binary / forensics) with type-adaptive
  sidebar — irrelevant lanes collapse under "Other capabilities"

Triggering
- Create project (3 types, typed form with progressive disclosure)
- SQLite-backed job queue + subprocess worker
- Pure-Python kinds (scan, agentic, codeql, fuzz) → raptor_*.py
- Claude-backed kinds (understand, validate, oss-forensics,
  crash-analysis) → bash -c "raptor project use <n> && claude -p '/…'"
- Live log streaming via SSE; cancel via SIGTERM to process group
- Every trigger form shows the Equivalent CLI for reproducibility

Configuration
- /settings edits ~/.config/raptor/models.json (4 role cards:
  analysis / code / consensus / fallback)
- Env-var fallback status, RAPTOR_MAX_COST display

## Schema & round-trip

Projects created via the UI write raptor's exact 7-field schema
(version, name, target, output_dir, created, description, notes) at
~/.raptor/projects/<name>.json — indistinguishable from
`raptor project create`. Studio-only metadata (project type, optional
binary / corpus / focus / language / vendor_report_url) lives in a
sidecar at $STUDIO_DATA_DIR/project-extras/ so raptor's CLI ignores it.

Finding schema rendered verbatim from raptor's own output — no
reinterpretation. SARIF 2.1.0 fallback when no findings.json exists.

## Testing

    python -m pytest packages/studio/tests/
    # 161 passed, 1 skipped

17 test modules including test_worker_integration which actually
spawns subprocesses to exercise the queue + cancel paths.

## Architecture

packages/studio/
├── app.py                  # FastAPI, ~20 routes
├── config.py               # env-driven paths
├── services/               # 14 read/write/classify modules
├── templates/              # 23 Jinja2 templates
├── static/                 # pixel-art velociraptor avatar
├── tests/                  # 17 modules (incl. live subprocess)
├── scripts/                # seed_demo.py, process_avatar.py
├── docs/
│   ├── PRD.md              # product scope + invariants
│   ├── CHANGELOG.md        # commit-by-commit narrative
│   └── UX_RECONCILIATION.md
└── fixtures/

Services layer is pure-Python, provider-agnostic, does not import
raptor's Python runtime except raptor_version.py which reads
RaptorConfig.VERSION directly.

## Non-goals (deliberate)

- Auth / multi-user — raptor is single-user by design
- Replacing the CLI — every UI action surfaces its Equivalent CLI so
  power users can reproduce outside the browser

## Provenance

Developed in the open at https://github.com/yesnet0/raptor-studio over
27 commits. This PR squashes that history for reviewability; the
companion repo retains the full timeline and the test run that
generated the dashboard screenshots.

## Dependencies added to requirements.txt

    fastapi>=0.110.0
    uvicorn[standard]>=0.27.0
    jinja2>=3.1.0
    python-multipart>=0.0.9
    markdown>=3.5

All pure-Python (uvicorn's C extensions are optional via httptools).
Changes to adapt content written for the companion repo to the in-tree
context, and to pre-answer likely review questions.

New:
- packages/studio/docs/FAQ.md — pre-answers scope / architecture /
  security / integration / ops / future questions, with explicit
  mentions of why-not alternatives (Flask, Django, SPA, etc).
- packages/studio/docs/ARCHITECTURE.md — one-page call-flow plus
  request lifecycles for create-project, browse-findings, trigger-run
  (pure-Python + Claude-backed), and state-location table.

Rewritten:
- packages/studio/docs/PRD.md — dropped "v0.0.1 pre-absorption" framing;
  now describes the in-tree context with the right relationships to
  raptor (packages/studio/, core.config.RaptorConfig.VERSION, etc.).
  Test count corrected to 160.
- packages/studio/README.md — links to FAQ + ARCHITECTURE docs; test
  count to 160.
- packages/studio/docs/UX_RECONCILIATION.md — preamble clarifying the
  doc dates from the companion-repo era and how to read it in-tree.
- packages/studio/docs/CHANGELOG.md — added an entry describing the
  in-tree migration squashed in this PR.

Code cleanup (drive-by):
- Dropped the legacy `binary=` kwarg alias on create_project() and
  the ProjectExtras.binary property. No existing users to maintain on
  a fresh merge; cleaner diff for reviewers.
- Removed the matching back-compat test (test_backcompat_reads_old_
  binary_field).

Tests: still 160 passing, 1 skipped.
yesnet0 added a commit to yesnet0/raptor-studio that referenced this pull request Apr 24, 2026
The upstream absorption PR (gadievron/raptor#215) introduced two new
documents (FAQ.md, ARCHITECTURE.md) written to pre-answer maintainer
review questions and diagram the request lifecycles. Mirror both
into the companion repo so anyone who lands here from the PR sees
the same docs.

- docs/FAQ.md — added with a top-banner noting it was originally
  written for the in-tree context; companion-repo equivalents noted
  where they differ (e.g. raptor_version.py regex-scrape vs direct
  import).
- docs/ARCHITECTURE.md — added verbatim.
- docs/PRD.md — top-banner linking to the open upstream PR; clarifies
  that the companion repo remains the standalone home regardless of
  the PR outcome.
- README.md — upstream-PR banner at top; trimmed outdated test/
  commit counts.
yesnet0 and others added 3 commits April 24, 2026 03:52
Both `core.project.project.PROJECTS_DIR` and `core.startup.PROJECTS_DIR`
now fall back to `$RAPTOR_PROJECTS_DIR` before the default
`~/.raptor/projects`. Defaults unchanged.

Why: companion tooling (raptor-studio) and test harnesses need to point
raptor at an alternate registry without monkey-patching module constants.
Without this, studio's UI could display projects from an alternate
`RAPTOR_PROJECTS_DIR`, but the subprocess-driven `raptor project use`
would read from the hardcoded default and report "Project not found" —
silent, confusing divergence between UI state and CLI state.

Tests: new `test_env_projects_dir.py` covers both modules; existing
137 project tests + 12 startup tests still pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New server-side picker (GET /api/fs/list) + reusable `_fs_picker.html`
partial rendering a modal with breadcrumb navigation, shortcut pills
(Home / Projects / Downloads / Desktop / /tmp), and directory/file
selection. Any input tagged `data-fs-picker="dirs"` (or `"files"`)
automatically gets a 📂 Browse button injected beside it.

Wired into:
- /projects/new target input — mode switches source→dirs, binary→files,
  button hidden for forensics (target is a URL)
- /projects/new corpus_dir / source_repo / output_dir — dirs
- /projects/{name}/{kind}/new target — dirs or files depending on
  spec.target_arg
- trigger-form render_field macro — path-ish field names (corpus,
  output_dir, binary, input_file, reproducer, map) get auto-picker

Why: typing an absolute path from memory is a consistent newcomer
friction point; the picker reveals the filesystem inline and fills the
field. Preserves the existing smart-name autofill: commit fires an
`input` event, which the existing listener picks up to set the name
from the chosen basename.

Single-user localhost assumption (documented in the endpoint comment
and raptor-studio's PRD) means no path-traversal restriction — just
normalization via Path.resolve().

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Same pass as raptor-studio main; see that repo's companion commit for
the rationale.

- Drop "on the roadmap" copy from stage page + project settings page
- Project settings page now has an edit form for description/notes,
  writes back via new update_project_metadata() round-tripping the
  raptor project.json schema
- next_action() takes project_kind — binary projects now suggest Fuzz,
  forensics suggest OSS forensics
- "Validation bundle" card hidden on non-validate runs (checklist
  alone no longer qualifies — understand writes one too)
- Pure-Python triggers now wrap argv with
  `raptor project use <project> && python3 ...` so artifacts land in
  the right output_dir regardless of the previous active project
- Exploits / Patches / Reports / run-detail Report files / SARIF files
  link to the file-serve route; suffix whitelist expanded to cover
  exploit/patch source (.py/.c/.sh/.patch/.diff/…)
- Activity page: run names link to run detail

Tests: 160 passed, 1 skipped — no regressions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@grokjc grokjc added the enhancement New feature or request label Apr 26, 2026
@danielcuthbert
danielcuthbert changed the base branch from main to dev April 27, 2026 13:48
@danielcuthbert

Copy link
Copy Markdown
Collaborator

I have tested this PR locally against the current repo state. The Studio work looks useful and the tests pass cleanly against the PR's declared base branch, but there are a few security issues I think we should fix before merging.

Validation performed:

  • PR head test run: packages/studio/tests passed (160 passed, 1 skipped)
  • Merge result into origin/dev: clean merge, Studio tests passed, project/startup tests passed
  • FastAPI smoke checks passed for /, /personas, /glossary, and /api/health
  • compileall passed for the Studio, project, and startup modules

Security risks to resolve:

  1. CSRF / local web UI drive-by actions

    Studio has state-changing POST routes without CSRF tokens or origin checks:

    • packages/studio/app.py project settings route
    • packages/studio/app.py job creation route
    • packages/studio/app.py global settings route

    Even when bound to 127.0.0.1, a malicious website can submit cross-origin forms to localhost. It would not need to read the response to mutate settings or enqueue expensive / unsafe RAPTOR jobs. We should add CSRF protection and reject state-changing requests with missing or untrusted Origin / Sec-Fetch-Site headers.

  2. Worker subprocesses inherit the full host environment

    packages/studio/services/worker.py currently builds the subprocess environment from os.environ. That bypasses RAPTOR's existing guidance for untrusted repo analysis. Studio-launched scans should start from RaptorConfig.get_safe_env(), add PYTHONUNBUFFERED=1, and only preserve required provider/API config deliberately.

  3. Stored XSS via report Markdown rendering

    packages/studio/services/markdown_render.py allows raw inline HTML, and templates render the result with | md | safe. RAPTOR outputs can contain model text, repo-controlled content, vendor report snippets, findings, and other untrusted material. A malicious report could execute script inside Studio and then call same-origin Studio endpoints. We should sanitise rendered Markdown with something like bleach/nh3, or disable raw HTML and only render escaped content.

  4. Remote binding is too easy for an unauthenticated UI

    raptor_studio.py allows --host 0.0.0.0, while Studio also exposes filesystem browsing APIs. If someone starts this on all interfaces, remote users on the network can interact with the UI and browse paths visible to the RAPTOR process. I would make non-loopback binding require an explicit --allow-remote flag with a strong warning, and ideally add authentication before supporting remote use.

  5. Main branch integration is not clean yet

    The PR targets dev and merges cleanly there. It does not merge cleanly directly into current main; .gitignore conflicts. main also now uses pinned dependency style, while this PR adds loose requirements such as fastapi>=..., uvicorn>=..., and markdown>=.... If we want this to land on main, it needs a rebase/retarget pass and pinned dependencies in the current style.

In short: functionally this is in good shape against dev, but I would like the web UI security boundaries tightened before we merge it.

@yesnet0

yesnet0 commented Jun 21, 2026

Copy link
Copy Markdown
Author

Hey gents, checking in on this - Can I help with anything?

@grokjc

grokjc commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Picking up on what @danielcuthbert said... want to tag-team the security fixes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants