Pencheff

Code security · Resources

Findings register

Finding lifecycle, severity, verification, and comments.

Repository scanning gives source findings the same operational treatment as runtime findings: scanner provenance, line-level evidence, remediation guidance, SARIF, GitHub annotations, and fix state.

Task routerdocumented
8coverage areas
5operator steps
4evidence fields
Coverage8
Execution5
Evidence4
Controls4
DocsAPIReportsSupport

Operational paths point to setup, references, and the right next task for the workflow.

ScopeFeatured
SectionResources
MethodDeterministic-first
OutputUnified evidence
ProfileCode security
01

Coverage

What does Findings register test?

  • Finding lifecycle, severity, verification, and comments.
  • This page is part of Resources under Featured.
  • It links back into the broader everything needed to operate pencheff experience.
  • Semgrep OSS packs, Bandit, gosec, Brakeman, ESLint security, tree-sitter rules, and niche-language scaffolds.
  • Secret detection with gitleaks and suspicious-code indicators with YARA-style patterns.
  • GitHub repository connection, webhook-triggered scans, hardlink staging, gitignore-aware filtering, and default-deny controls.
  • SARIF and GitHub check run output so developers see findings where they work.
  • Auto-fix preparation for Semgrep autofix, SCA version bumps, and reviewer-friendly patch synthesis.
02

Execution

How does Pencheff run this?

  • Connect or register a repository and choose a branch, scan profile, and scanner policy.
  • Stage the source safely, fan out language-specific scanners, and capture raw scanner output.
  • Normalize results into repo findings with file, line, rule, severity, scanner, and remediation metadata.
  • Merge code results with SCA, IaC, secrets, and runtime context to reduce duplicate triage.
  • Send annotations, SARIF, reports, fix PRs, or dashboard tasks depending on the workflow.
03

Evidence

What evidence does this produce?

  • File path, line number, rule id, scanner name, confidence, language, and vulnerable snippet context.
  • Suggested fix, fixed-version data when applicable, and status across suppressions or rechecks.
  • GitHub check output, SARIF upload, comments, and links back into the finding record.
  • Cross-finding signals when a code pattern aligns with runtime exploitation.
04

Controls

How is this kept safe to run?

  • Scanner choices are explicit and permissively licensed where used in the repo pipeline.
  • Secrets are handled as findings rather than echoed into broad UI surfaces.
  • CI gates can be tuned by severity, reachability, policy, and target branch.
  • Generated fixes remain reviewer-owned and trace back to original scanner evidence.
01

From the Pencheff docs

Findings API

GET /scans/{scan_id}/findings

Return every finding for a scan. Filter with query params:

  • ?severity=critical
  • ?category=injection
  • ?owasp_category=A03
  • ?verified_only=true
  • ?include_suppressed=true
  • ?sort=risk_score (default; use cvss_score or created_at)

GET /findings/{id}

Fetch a single finding with full evidence, comments, assignments, tags.

PATCH /findings/{id}

Update status. Valid fields:

{
  "verification_status": "true_positive" | "false_positive" | "true_negative" | "false_negative",
  "suppressed": true,
  "suppress_reason": "accepted_risk" | "wont_fix" | "false_positive" | "duplicate" | "out_of_scope",
  "suppress_notes": "string",
  "resolved_at": "2026-04-21T…Z",
  "sla_days": 7
}

Collaboration

  • POST /findings/{id}/comments — add a comment
  • GET /findings/{id}/comments — list comments
  • POST /findings/{id}/assign{"assignee_user_id": "..."}
  • POST /findings/{id}/tags{"tag": "p0-fix"}
  • DELETE /findings/{id}/tags/{tag} — remove a tag

Prioritisation fields

Every Finding includes the unified prioritisation surface:

FieldTypeSource
risk_scorefloat (0–100)computed at insert from CVSS × EPSS × KEV × SSVC × reachability
ssvc_decisionstringone of act, attend, track_star, track
reachabilitystringone of exploited, reachable, present, unknown — see Reachability classifier
epssfloat (0–1) | nullEPSS feed; populated for SCA findings
kevboolCISA KEV catalog membership

Sort the list endpoint by risk_score:

GET /scans/{scan_id}/findings?sort=risk_score   (default)

The unified, cross-table queue lives at /unified-findings.

POST /findings/{id}/propose_fix

Generates a draft FixProposal for the finding. SCA findings get a deterministic version-bump diff; SAST/DAST findings synthesise a unified diff via the operator-configured patch-synthesis backend. See Auto-fix PRs.

The route accepts kind{sast, dast}; SCA findings ride the dast kind and Pencheff detects the SCA payload from evidence and routes internally.

POST /findings/{id}/triage

Pro tier. Triage 2.0 — exploitability walkthrough returning { walkthrough, blast_radius, exploit_scenario, fix_outline, confidence }. Cached on finding.ai_triage; pass ?force=true to regenerate. See Triage 2.0.

02

From the Pencheff docs

Unified findings stream

A single sortable, filterable queue across SAST, DAST, SCA, IaC, and secrets — no more "click into a scan, then its findings" friction when you just want to know "what should I fix first?"

Lives at /findings in the dashboard.

What it does

  • Pulls from both the findings table (DAST + SCA from live scans) and repo_findings table (SAST + SCA + secrets + IaC from repo scans).
  • Projects them to a common shape and merges.
  • Sorts by Pencheff's unified risk_score (CVSS × EPSS × KEV × SSVC × reachability) — NULL last, then severity, then created_at.
  • Paginates with stable order across pages.

Filters

The dashboard's filter chips translate one-to-one to query params:

GET /unified-findings
  ?source=sast        # also: dast, sca, iac, secret (multi-select)
  &severity=critical  # critical | high | medium | low | info
  &reachability=exploited
  &include_suppressed=false
  &target_id=<uuid>   # restrict to one target
  &limit=50&offset=0

Server-side filters fire before the merge, so paginated results stay consistent.

Source mapping

Pencheff projects each underlying row to a single source label so filters work uniformly:

sourceFrom findings rows where…From repo_findings rows where…
dastcategory != "components"
scacategory == "components"scannerosv, ghsa, pip-audit, npm-audit
sastscannersemgrep, bandit, gosec, brakeman, eslint, treesitter:*, ruff. (Legacy codeql rows from pre-v0.7 scans still classify as SAST.)
secretscannergitleaks, detect-secrets
iacscannertrivy_iac, checkov

Drill-through

Each row in the list is a hyperlink. The unified item carries enough metadata for the dashboard to deep-link:

  • findings-table rows → /findings/<id> → redirector that resolves the scan_id and forwards to the scan-scoped detail page.
  • repo_findings-table rows → /repos/... (the existing repo finding detail page).

Risk-first by default

The default sort is descending risk_score. A KEV-listed CVSS 6.0 beats an unexploited CVSS 8.5 — that's what Snyk gets wrong by sorting on CVSS alone. See EPSS, KEV, SSVC for the full priority formula.

What's tested

cd apps/api && uv run pytest tests/test_unified_findings.py

12 unit tests cover the projection logic + sort key (risk_score desc, severity tiebreaker, recency tiebreaker) + ecosystem-aware source mapping.