Point it at an OpenTaint SARIF and get a self-contained, offline HTML report — click through findings, trace each tainted flow from untrusted input to dangerous call, and read the rules that fired, all without a server.
- Trace taint end to end. A Monaco editor highlights the flow in blue and the dangerous call in red; step through it from untrusted input to dangerous call, with cross-file hops switching the active file for you.
- Findings and rules, side by side. VS Code–style trees group findings by directory and list the ruleset; open a finding to read the rule that fired — markdown description, CWE tags, severity, and ordered taint steps.
- Offline, single file. Export one self-contained
index.html— JS, CSS, fonts, and Monaco all inlined. No server, no network. Share it as-is.
Generate a report from an OpenTaint SARIF — no install required (npx fetches the CLI on demand):
# Open the report in your browser
npx @seqra/opentaint-viewer serve --sarif results.sarif
# Or write a self-contained, offline HTML file
npx @seqra/opentaint-viewer export --sarif results.sarif --out report.htmlPrefer a global command? npm install -g @seqra/opentaint-viewer, then run opentaint-viewer serve --sarif results.sarif.
The builtin ruleset is found next to the opentaint engine binary on your PATH, and the
source root is read from the SARIF — so the common case needs only --sarif. No SARIF yet?
Run OpenTaint to produce one.
CLI options
Add --rules ./my-rules to show your project's custom rules alongside the builtin set.
| Option | Default | Meaning |
|---|---|---|
--sarif <file> |
— (required) | SARIF report. |
--src <dir> |
SARIF %SRCROOT%, else the SARIF's directory |
Source root. |
--builtin-rules <dir> |
../lib/rules next to the opentaint binary on PATH, else next to the CLI |
Builtin ruleset directory (the engine's shipped rules). |
--rules <dir> |
— (optional) | Your project's custom rules; shown under "Custom" and linked from findings. Custom wins on an id collision with a builtin rule. A rule in neither set still renders, marked "definition not available". |
--name <id> |
basename of the source root | Project name in the UI. |
--port <n> (serve) |
5151 |
Listen port. |
--no-open (serve) |
— | Don't auto-open the browser. |
--out <file> (export) |
opentaint-report.html |
Output HTML path. |
Run the engine only through Docker? Extract its ruleset once and pass --builtin-rules ./rules —
see the OpenTaint quick-start for the Docker invocation.
Have OpenTaint installed? Scan the seqra/java-spring-demo
project and open the report — npx fetches the viewer, nothing to install:
git clone https://github.com/seqra/java-spring-demo
opentaint scan --output java-spring-demo/results.sarif java-spring-demo
# Open it in the browser
npx @seqra/opentaint-viewer serve --sarif java-spring-demo/results.sarif
# Or write a shareable offline file
npx @seqra/opentaint-viewer export --sarif java-spring-demo/results.sarif --out report.htmlJust want to preview the UI? The repo commits a demo
data/content.json— 13 findings (Template Injection, SSRF, XSS) over 47 rules — rendered by the dev server:npm install && npm run dev.
Build a static report from source (npm run gen)
The viewer is a generic React app that renders one committed data/content.json. The CLI above
is the easy path; this build-from-scratch flow regenerates the committed demo and works for any project:
npm install
npm run gen -- \
--sarif your-project/results.sarif \
--src your-project/src \
--rules "$(dirname "$(command -v opentaint)")/../lib/rules" \
--name your-project
npm run build:single # writes a self-contained dist-single/index.html; use npm run build for a hosted dist/ buildIn this legacy
npm run genworkflow,--rulesis the builtin ruleset — it predates the CLI's flag split (--builtin-rulesbuiltin,--rulescustom). If your SARIFartifactLocation.urivalues aren't relative to the parent of--src, pass--root <dir>.
Development
| Script | What it does |
|---|---|
npm run dev |
Vite dev server with HMR. |
npm run cli |
Run the CLI from source via tsx (npm run cli -- export --sarif results.sarif --out report.html). |
npm run gen |
Generate data/content.json from a SARIF + source + rules. |
npm run build:single |
Build a single self-contained offline index.html into dist-single/. |
npm run build |
Type-check (tsc --noEmit) and build the hosted site to dist/. |
npm run build:dist |
Build the shippable CLI bundle + template into dist-cli/. |
npm test / npm run coverage |
Vitest unit/component tests (+ V8 coverage). |
npm run e2e |
Playwright end-to-end tests. |
Stack: React 18, TypeScript, Vite, Monaco Editor, Zustand, react-resizable-panels,
Lucide, JetBrains Mono. The viewer is fully static — it loads one bundled data/content.json
(shape in src/types/content.ts) into a Zustand store
and renders everything from it; no backend, no network calls for analysis. CI
(ci.yml) runs the build, coverage, and Playwright suite on every push and PR.
- OpenTaint engine & CLI — https://github.com/seqra/opentaint
- Demo project analyzed here — https://github.com/seqra/java-spring-demo