A local investigation workspace for penetration testers. Start with a target, a test, or existing proof. Keep your evidence and build the report as you work.
A real agent session running synthetic fixtures, recording results, and updating the canvas. Playback is shortened.
| Investigation canvas | Proof kept inside the test |
|---|---|
| Reviewable finding | Report with evidence |
<sub>All data shown is the built-in synthetic sample engagement; no live system was tested.</sub>
On Ubuntu 24.04+ (including WSL), install Electron's system libraries once:
sudo apt-get update
sudo apt-get install libnspr4 libnss3 libasound2t64 libgtk-3-0t64 libgbm1
WSL also needs a graphical session such as WSLg. ./run.sh checks for missing
shared libraries before building and launching the app.
npm run setup
npm start
This opens the standalone Electron app. Node 22.16+ is required for development; packaged builds include their runtime. Build a runnable directory with npm run package, or an installer/AppImage with npm run dist (output: release/). Docker and the Codex CLI are optional external tools. The current PTY and Docker integration require Linux; the investigation UI and evidence workflow do not require either tool.
The desktop serves only the production bundle on a loopback port assigned to the OS profile. Every HTTP route and terminal WebSocket requires a fresh desktop credential held in Electron's main process. Foreign origins and hosts are rejected; there is no wildcard CORS. The renderer uses a sandbox, context isolation, no Node integration, and a restrictive Content Security Policy. External navigation and popups are blocked.
This is a single-user desktop application: the OS profile owns the database. Terminal agents receive revocable credentials scoped to the selected engagement and cannot access settings, integration inventory, other engagements, or whole-document replacement. Linux containers reach the authenticated service through a private Unix-socket relay; CONNECT proxy access also requires a credential. The desktop credential is never passed to terminal subprocesses.
npm run dev also launches Electron with a production build. npm run dev:web is an explicit frontend-only developer tool, restricted to loopback; it is not the desktop runtime. A manually launched API fails closed unless PENTEST_CANVAS_TOKEN is set to a random value of at least 64 characters. Browser-only acceptance scripts written for the old unauthenticated server must be adapted before use.
Already-running legacy Vite/API processes must be stopped by their operator after switching to the desktop app; editing these files does not secure an old process still running in memory.
Choose Open sample engagement in the sidebar. It creates a separate, clearly labeled synthetic assessment with two test paths, captured example responses, and a reviewed finding ready to view in Report. Its commands only print local fixtures. The demonstration needs no API key or model call.
The investigation now keeps captured proof inside its source Test. Open a Test to read attempts, keep excerpts, write a one-sentence result, and choose an assessment. Standalone imported evidence remains available. Existing evidence IDs, report references, and immutable run history are preserved.
New steps automatically arrange horizontally: host → enumeration → discovered endpoints → endpoint tests → findings → follow-up tests. Each Test with evidence has an Open proof control on its card. Tests owning proof cannot be removed while that proof remains. Use Builds on inside a Test to combine multiple earlier findings or tests. node tests/branching-desktop.mjs verifies this complete flow through the real UI with synthetic local responses.
Use Discover surfaces to paste enumeration results one per line and select which ports, services, or endpoints become targets. Duplicate entries under the same target are skipped. Each discovery links back to the Test and starts with unconfirmed scope.
Next move creates another Test, records gained Access, or tracks an Objective. Tests started from Access inherit it as a prerequisite. Candidate or expired access blocks dependent recorded runs; verification requires captured proof. Access records are capability descriptions and credential references, not a credential vault or automatic session injector. Objectives expose their incoming investigation path and require proof before being marked reached. Connections describe dependencies, not automatic proof that an exploit chain succeeded.
Hide dead ends removes refuted Tests from the canvas view without deleting their attempts. They remain accessible through search, branches, and Coverage. Findings and their reports continue to use selected proof, including proof embedded inside Tests.
Run node tests/investigation-desktop.mjs for the isolated desktop workflow check.
- Target: add the host, application, endpoint, or account under test. Scope is available from the toolbar.
- Test: paste a command and choose where it runs. Every attempt has its own durable command, timestamp, output, environment, and exit state. Completion opens a spacious output reader: select an excerpt and keep it as evidence, or reopen an earlier attempt. A successful execution does not validate a vulnerability.
- Evidence: select useful output and keep it, paste proof from another tool, or import a text capture/image. Captured content is read-only and has a SHA-256 fingerprint. Manually supplied proof is labeled separately from recorded runs.
- Finding: attach evidence, describe impact, reproduction, severity, and remediation. Explicitly review it to include it in the report. Material changes invalidate that review.
- Report: reviewed findings update automatically. The executive summary remains independently editable. Export full Markdown with embedded images or use Print / PDF. Previous free-form report drafts remain downloadable.
Tests with saved results open on their output; Run again reveals the command and execution environment. Output without recorded execution details is labeled plainly and can be kept as manually supplied evidence. Cards show badges only for active work, capture, review, or inclusion in the report.
Evidence can be added to an existing finding, so multiple tests can support one issue. Continue investigation creates a follow-up test or discovered target from a test, evidence item, or finding. Discovery does not change the agreed scope.
A question about an existing finding creates one reusable follow-up Test after that finding. Its command, result, and assessment remain together. Evidence is added only when the tester chooses to keep proof; another Finding is added only for a materially different issue.
The Add menu lets you start anywhere. Connect a target to a test/finding to assign it; connect evidence to a finding to attach it. Search with Ctrl/⌘ K, use Focus for a local view, and Arrange to tidy the canvas. Ctrl/⌘ Enter runs the open test when a target and execution environment are selected. Escape closes the output reader or inspector. Multiple labeled connections between the same nodes are supported; double-click a connection to rename it.
The desktop UI uses bundled DejaVu Sans with a compact navigation rail, neutral surfaces, and teal accents. Reports use the same typography; commands and raw output use DejaVu Sans Mono. Font license information ships with the app.
Investigation is the main workspace: add targets, run tests, and follow evidence on the canvas. Open Coverage in its toolbar to find targets without tests, unassessed questions, and tests missing a target. Search targets or tests and open any item directly in the inspector; the graph stays in place. Coverage also links captured excerpts. An executed command alone does not count as a supported hypothesis.
Findings is the review queue, with links to missing proof or details. Report is the delivery view for reviewed findings.
Export evidence downloads a JSON package containing the report, reviewed finding references, selected evidence (including images), SHA-256 hashes, and source run metadata. Unselected run output and unrelated notes are excluded. Keep this downloaded package as the delivery record; the app does not manage releases.
Engagements, settings, and run records live in Electron’s per-user application data directory (~/.config/pentest-canvas on Linux by default). On the first launch from this checkout, an existing data/pentest-canvas.db is copied using a consistent SQLite snapshot; the original remains intact. Saved integration settings and API key are also copied if present. Set PENTEST_CANVAS_PROFILE to use a separate profile without importing checkout data. The assigned port is retained across launches to preserve browser recovery storage; if another process occupies it, startup fails with an error. Packaged builds exclude all user data and secrets. The standalone API test/development entry point accepts PENTEST_CANVAS_DB. Browser saves merge disjoint changes with newer server state. Same-field conflicts stop saving and keep the local copy, with download/retry/load-saved controls. Unsaved work has a browser recovery copy. Browser layout changes cannot silently replace a different field edited by Codex.
Existing data keeps its original IDs and notes. Old findings begin unreviewed; capture and attach proof before including them. Historical output is labeled as legacy evidence because original run metadata is unavailable. On a backend restart, unfinished runs become interrupted rather than falsely successful.
Open the collapsed, black-and-white Codex terminal when needed. It uses the real Codex CLI locally or in the active container and requires the saved OpenAI API key. No model call is required for the main workflow.
The agent saves a Test before each experiment, uses canvas run TEST_ID @/tmp/run.json for authorized recorded execution (command and explicit environment), and updates the assessment after each attempt. Retries stay on the same Test; new questions become connected follow-ups. Cards and run states refresh across all app pages without a manual context-send action. Negative results and failed attempts remain in the record.
The canvas CLI can inspect/update nodes, read recorded runs (canvas runs TEST_ID), and capture exact excerpts ( canvas capture '{"runId":"...","excerpt":"...","title":"..."}'). Findings proposed by Codex still need human review. Arbitrary commands typed in the Codex terminal are not automatically attributed to a recorded test; use the Test inspector for the durable run workflow.
Codex also receives the canvas geometry and visual conventions. canvas view returns current positions, card bounds, relationships, and browser viewport; canvas image /tmp/canvas.png retrieves a browser-generated schematic overview for an image viewer. It is explicitly labeled as a schematic, and stale images are rejected. canvas connect adds labeled relationships; canvas layout moves cards with collision handling and optional expected-position guards. Agent changes still pass through the same evidence and review rules.
npm run build
npm test
npm run test:desktop
The desktop smoke test requires a graphical session and the Linux GUI libraries required by Electron. It launches an isolated temporary profile with Chromium sandboxing enabled.
Security regression tests use a temporary database and random loopback port. They replay anonymous reads/writes, foreign-origin requests, source-file probes, terminal upgrades, proxy access, and cross-engagement agent access without touching existing engagements.
This remains a local single-user prototype. HTTP/control and terminal APIs require capability credentials; the OS profile is the user boundary, and there are no multi-user accounts. A review marker is a workflow gate, not an authenticated signature. Docker profiles are network arrangements, not a complete enforcement layer for rules of engagement. Do not expose the control API as a multi-user service.
The new Test workflow requires an explicit environment and will not fall back to the host when its chosen container is unavailable. The optional terminal has a separate execution path. Unrecorded /api/run, Distill, the dormant copilot/CVE agent, and its usage accounting have been removed. Captured excerpts are not automatically redacted; review sensitive content before including a finding. Files are currently inline (supported images/text under 4 MB per import), with a 12 MB API body limit, so this is not yet a large artifact repository. PDF export uses the browser's print renderer; DOCX and release snapshot management are not implemented.
The recorder adds a synthetic engagement to the selected profile. REAL_PROFILE=1 uses the existing desktop profile and its saved settings; close the app before recording.
REAL_PROFILE=1 LEAN_DEMO=1 FRAMES=/tmp/canvas-demo-frames npm run demo:record -- /tmp/canvas-demo tests/agent-demo-steps.mjs
node tests/render-demo.mjs /tmp/canvas-demo-frames docs/media/agent-demo.gif 1280 500
Run npm run test:terminal for desktop checks of collapse/reopen, focus, resize, setup progress, errors, and retry.