Pencheff

Risk, reporting, and compliance

Executive dossier

Leadership summary with business risk, grade, posture trends, and audit-ready output.

ScopeDeliverables

Run web, API, code, dependency, cloud, AI, and internal-network assessments from one queue with unified findings, evidence, remediation, and audit output.

OutputUnified evidence

Findings, reports, dashboards, exports, integrations, and retests all read from the same normalized record.

MethodDeterministic first

Pencheff favors repeatable checks, then uses AI for triage, enrichment, orchestration, and remediation where it adds signal.

From the Pencheff docs

Executive risk dashboard

/features/executive-dashboard

The dashboard for the people who don't open finding detail pages. Five aggregations over the same data the operator surface uses, rendered with Recharts (90-day trend) and a hand-rolled CSS heatmap (Recharts has no good heatmap primitive).

Lives at /dashboard/executive in the SaaS UI. See the broader dashboards reference for the per-scan / per-target / per-repo views layered on top of the same data.

What it shows

TileWhat's inside
Severity × scanner heatmapOne cell per (severity, scanner) bucket — rows are critical → info, columns are the scan kinds that produced findings in this workspace. Cell weight is finding count; colour ramp is gilt → oxblood.
90-day new vs. closed trendTwin sparkline overlay — net-new findings against findings closed (fixed or suppressed). The cross-over point is the "are we keeping up?" signal.
Top-10 risky reposRepos ranked by open findings, with critical / high counts called out separately. Each row is a click-through to /repos/{id}.
KEV exposure tileTotal / open / suppressed / fixed findings flagged in the CISA Known-Exploited-Vulnerabilities catalogue, plus a per-severity breakdown. The board tile most operators look at first.
Fix-conversion tileOf every finding, how many had a fix proposed and how many had that proposal applied. Two coverage percentages — the lift between them is your developer-merge friction.

How to read it

The intended operator question is: "In one screen, are we trending in the right direction?" Three signals to read together:

  1. Heatmap weight drifting right (older scanners) is fine; drifting down (lower severity) is good; drifting up-and-left is the warning.
  2. Trend overlay — closed should track new with a small lag. A widening gap means the backlog is growing.
  3. KEV tile open count must trend to zero. A KEV finding open on day 14 is the only metric here that should appear in a board pack.

API

curl -H "Authorization: Bearer $JWT" -H "X-Workspace-Id: $WS" \
  https://api.pencheff.com/dashboard/heatmap
curl -H "Authorization: Bearer $JWT" -H "X-Workspace-Id: $WS" \
  "https://api.pencheff.com/dashboard/trend?window_days=90"
curl -H "Authorization: Bearer $JWT" -H "X-Workspace-Id: $WS" \
  "https://api.pencheff.com/dashboard/top-repos?limit=10"
curl -H "Authorization: Bearer $JWT" -H "X-Workspace-Id: $WS" \
  https://api.pencheff.com/dashboard/kev-exposure
curl -H "Authorization: Bearer $JWT" -H "X-Workspace-Id: $WS" \
  https://api.pencheff.com/dashboard/fix-conversion

Every endpoint scopes via the X-Workspace-Id header — same shape as the operator dashboards.

Tier gating

Aggregations are gated on the EXECUTIVE_DASHBOARD feature flag — team / enterprise / self-hosted. The free + pro plans see an upgrade nudge in place of the tile grid.

Performance

Each aggregation is a single grouped query with no joins beyond the workspace scope. The whole page renders three round-trips: frameworks list, the five tiles in parallel, and the live workspace context. The backend caps each tile's row count so the response stays under a megabyte even on workspaces with 100k+ findings.

From the Pencheff docs

Visual dashboards

/features/dashboards

Pencheff's findings register, scan history, and CVE rollups have always been queryable. This page covers the chart surfaces layered on top of that data — five dashboards that render the same numbers as the tables, but in shapes a stakeholder can read in five seconds.

Every dashboard reuses primitives in apps/web/components/dashboard/ and renders charts via Recharts. All data comes from existing API endpoints; no scan-time work is added.

Per-scan dashboard

Lives at /scans/{id}/dashboard. Linked from the assessment page once the scan transitions to status="done".

TileWhat it shows
Severity donutCounts from Scan.summary rendered as a donut with center-label total.
CVSS histogramFindings bucketed across 0–2, 2–4, 4–6, 6–8, 8–10. Bar fill maps to the severity that band corresponds to.
Verification pieverification_status distribution: unverified / true_positive / false_positive / fixed.
Category barTop-N finding categories sorted desc; bar fill = the highest-severity finding in that category.
OWASP coverageStacked horizontal bars by owasp_category × severity.
Top-risk listTen findings ranked by risk_score (CVSS as tiebreak), each one a click-through to /scans/{id}/findings/{fid}. Surfaces the PriorityStrip (risk score, reachability, SSVC, EPSS, KEV) inline.
Endpoint treemapTop-N affected endpoints sized by finding count, coloured by top severity.
Stat tilesTotal active · KEV-in-scan · reachable · median EPSS.

Pre-completion the route shows a banner and links back to the live assessment page.

LLM red-team variant

When target_kind === "llm", the same /scans/{id}/dashboard route renders a different composition that reflects what an LLM red-team scan actually does — probing strategies, attack techniques, judge verdicts — rather than CVSS-shaped vulnerability surface.

TileWhat it shows
Verdict funnelThe centerpiece. Total probes flowed in → vulnerable / refused / ambiguous. Built from /scans/{id}/llm-transcripts?format=json. When the transcript file expired or was never written, the dashboard degrades to "vulnerable count = total findings" and surfaces the limitation inline.
OWASP-LLM-Top-10 heatmapOne row per LLM01LLM10 with absolute failure count and per-category success rate (failures ÷ probes-in-category). Built from Scan.summary.llm_redteam_summary.by_category plus the transcript per-category totals.
Strategy breakdownBar chart of by_strategy (base, jailbreak, dataset, custom, guardrail) plus a horizontal-bar list of the top 10 techniques (direct_injection, role_play, constraint_violation, …).
Judge-confidence histogramWhen LLM-as-judge is enabled, distribution of judge.confidence across 10 buckets 0.0–1.0. Cell colour ramps gilt → oxblood for high-confidence vulnerable verdicts.
Token + latency profileStacked bar of total prompt / completion / cached / reasoning tokens (OpenTelemetry GenAI semantic conventions), plus latency p50 and p95 derived from per-probe latency_ms.
Top-10 failures listRendered from Scan.summary.llm_redteam_summary.top_failures — already computed by the runner at plugins/pencheff/pencheff/modules/llm_red_team/reporting.py.
Severity rollup tilesby_severity counts per LLM finding, with critical accented red.
Recommended-guardrails CTALink into /scans/{id}/recommended-guardrails.

The LLM dashboard renders without any new backend work — the redteam_summary payload was already persisted on Scan.summary, just not visualised. Verdict funnel + token profile + latency come from the existing transcript endpoint.

Per-target trend dashboard

Embedded on /targets/{id} once the target has ≥2 completed scans. Powered by GET /dashboard/target/{target_id}/trend.

  • Grade trajectory line — score (0–100) per completed scan over time.
  • Severity stack area chart — severity counts per scan, x-axis = created_at.
  • Stat tiles — open total · merged fixes · MTTR (days, computed from Finding.created_at → last_rechecked_at for findings now verification_status="fixed").
  • Scan-pair delta strip+N new · −N fixed · ±N regressed per consecutive pair, derived from severity-summary diffs (not finding-by-finding identity comparison — accurate enough for trending and avoids an O(scans²) Finding cross-join). Each row links into /scans/compare?a=&b= for a finding-level diff.
  • Empty state — targets with 0 or 1 completed scans don't show the trend section; one snapshot isn't a trend.

Per-repo trend dashboard

Lives at /repos/{id}/dashboard. Linked from the repo's scan-history header when ≥2 scans are on file. Powered by GET /repos/{id}/trend.

  • Severity score over time — weighted score (25 × critical + 10 × high + 4 × medium + 1 × low) per RepoScan, x-axis = completed_at.
  • Severity stack — severity counts per scan, area chart.
  • Stat tiles — open findings · merged fixes · latest commit SHA · severity score for the latest scan.
  • Latest-scan donut + scanner duration list — quick view into the most recent commit's findings + per-scanner runtime.
  • One-click link into /repos/scans/{id}/dashboard for the latest scan's full breakdown.

Per-repo-scan dashboard

Lives at /repos/scans/{id}/dashboard. Linked from the scan detail page next to "View compliance mapping". Reuses /repos/scans/{id} and /repos/scans/{id}/findings (no new backend).

  • Stat tiles — total findings · scanners run · scan duration · fix progress (merged + open / total).
  • Severity donut — distribution across the scan's findings.
  • Scanner-effort bar — finding count per scanner (semgrep / bandit / gosec / brakeman / eslint / gitleaks / ghsa / yara / trivy_iac / checkov); tooltip shows duration + skipped/error states.
  • File-hotspot treemap — top-N affected files sized by finding count, coloured by top severity.
  • Fix-status pienone / proposed / pr_open / merged distribution. The pivot stat that tells you whether the autofix layer is converting.
  • Top CVEs table — top-12 CVE-tagged findings with installed → fixed version delta and a click-through to the linked PR (when fix_pr_url is set).

Workspace executive dashboard

Lives at /dashboard/executive. Plan-gated on the EXECUTIVE_DASHBOARD feature flag — see the executive-dashboard page for the per-tile breakdown and the API endpoints.

The previous version rendered the 90-day trend as inline SVG; it now uses the same Recharts primitives as the rest of the dashboard suite. The heatmap stays as a CSS grid (Recharts has no good heatmap primitive).

API surface

EndpointPowers
GET /scans/{id} + GET /findings?scan_id=Per-scan dashboard.
GET /scans/{id}/llm-transcripts?format=jsonLLM verdict funnel + token / latency profile.
GET /dashboard/target/{target_id}/trendPer-target trend section.
GET /repos/{id}/scans + GET /repos/{id}/trendPer-repo trend dashboard.
GET /repos/scans/{id} + GET /repos/scans/{id}/findingsPer-repo-scan dashboard.
GET /dashboard/heatmap / /trend / /top-repos / /kev-exposure / /fix-conversionExecutive dashboard.

Code locations

  • Shared primitives — apps/web/components/dashboard/{SeverityDonut,CategoryBar,CvssHistogram,OwaspCoverage,TopRiskList,VerificationPie,EndpointTreemap,TrendLine,SeverityStack}.tsx
  • Repo-specific — apps/web/components/dashboard/repo/{ScannerEffortBar,FileHotspotTreemap,CveTable,FixStatusPie}.tsx
  • LLM-specific — apps/web/components/dashboard/llm/{VerdictFunnel,OwaspLlmHeatmap,StrategyBreakdown,JudgeConfidence,TokenProfile,TopFailuresList}.tsx
  • Severity tokens (single source of truth) — apps/web/lib/sev.ts
  • Backend aggregations — apps/api/pencheff_api/routers/dashboard.py (target_trend), apps/api/pencheff_api/routers/repos.py (repo_trend)

Related

Keep exploring Platform.