Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9cd09c7
Add PVR triage taskflow
anticomputer Mar 2, 2026
46ea60c
Address PR review: add SPDX headers, pass GH_TOKEN in toolbox env
anticomputer Mar 2, 2026
d580f17
Add PVR triage batch scoring, write-back, and reporter reputation tra…
anticomputer Mar 3, 2026
8e37d26
Add run_pvr_triage.sh: local test and demo script for pvr triage task…
anticomputer Mar 3, 2026
bd0bae2
pvr_triage_batch: skip already-triaged advisories by default
anticomputer Mar 3, 2026
09b5066
Add SCORING.md: reference for batch priority, quality signals, fast-c…
anticomputer Mar 3, 2026
73b0bb1
Address PR review feedback
anticomputer Mar 3, 2026
9c6e8a9
Self-review: robustness and logic fixes
anticomputer Mar 3, 2026
2ad67c3
fetch_file_at_ref: raise default length from 50 to 100 lines
anticomputer Mar 3, 2026
f25edce
pvr-triage: add bulk respond taskflow, 3-path fast-close, reputation …
anticomputer Mar 3, 2026
adf0552
SCORING.md: update 3-path decision table and reputation thresholds
anticomputer Mar 3, 2026
88d25f1
Fix ruff linter errors in test_pvr_mcp
anticomputer Mar 3, 2026
c1d38d7
Fix advisory state: incoming PVRs use triage state, not draft
anticomputer Mar 3, 2026
37e1624
Fix advisory state API: reject→closed, remove withdraw_pvr_advisory
anticomputer Mar 3, 2026
a5b2e2c
Add accept_pvr_advisory: triage→draft state transition
anticomputer Mar 3, 2026
eb5d98a
Update overview doc: accept/reject state transitions in diagram and o…
anticomputer Mar 3, 2026
bde5f8e
Remove comment posting: no GitHub REST API for advisory comments
anticomputer Mar 4, 2026
e632510
Add advisory dedup detection and container-based validation
anticomputer Apr 10, 2026
bcb91a5
Add semantic duplicate analysis on top of structural comparison
anticomputer Apr 10, 2026
0f243d9
Evaluate PVR reports against repository security policy
anticomputer Apr 15, 2026
a36d9b3
Fix dedup clustering: only strong matches cluster
anticomputer Apr 15, 2026
631ecc6
Add configurable advisory state, demo script
anticomputer Apr 15, 2026
d9b1011
Eliminate conversational prompting in taskflow execution
anticomputer Apr 15, 2026
024e747
Fix tests for FastMCP 3.x: remove .fn() indirection
anticomputer Apr 15, 2026
c38595a
Fix demo script for FastMCP 3.x: remove .fn() indirection
anticomputer Apr 15, 2026
88b34f1
Update README: state global, semantic dedup, model name, demo script
anticomputer Apr 15, 2026
ad8f810
Revert "Fix tests for FastMCP 3.x: remove .fn() indirection"
anticomputer Apr 15, 2026
a0a1a21
Revert "Fix demo script for FastMCP 3.x: remove .fn() indirection"
anticomputer Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions docs/pvr_triage_overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# PVR Triage Taskflows — Overview

> 30-minute sync reference. Last updated: 2026-03-03.

---

## The Problem

OSS maintainers get flooded with low-quality vulnerability reports via GitHub's Private Vulnerability Reporting (PVR). Most are vague, duplicated, or AI-generated. Reviewing each one manually is expensive.

---

## The Solution: 4 Taskflows

```
┌─────────────────────────────────────────────────────────────┐
│ INBOX │
│ (GHSAs in triage state via GitHub PVR) │
└───────────────────────┬─────────────────────────────────────┘
┌─────────────────────────┐
│ pvr_triage_batch │ "What's in my inbox?"
│ │
│ • List triage GHSAs │
│ • Score each by │
│ severity + quality │
│ • Show Age (days) │
│ • Rank: highest first │
│ (oldest wins ties) │
└────────────┬────────────┘
│ ranked queue saved to REPORT_DIR
┌─────────────────────────┐
│ pvr_triage │ "Is this real?"
│ (one advisory) │
│ │
│ Task 1: init │
│ Task 2: fetch & parse │
│ Task 3: quality gate ──┼──► fast-close? ──► skip to Task 7
│ Task 4: verify code │
│ Task 5: write report │
│ Task 6: save report │
│ Task 7: draft response │
│ Task 8: save + record │
└────────────┬────────────┘
│ _triage.md + _response_triage.md saved
Maintainer reviews
(edits draft if needed)
┌────────┴────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ pvr_respond │ │ pvr_respond_batch │
│ (one at a time) │ │ (all at once) │
│ │ │ │
│ confirm-gated: │ │ • list_pending │
│ accept (→draft) │ │ • for each: │
│ reject (→closed)│ │ - confirm-gated │
│ │ │ state change │
│ mark as applied │ │ - mark as applied │
│ post draft │ │ • post drafts │
│ manually via UI │ │ manually via UI │
└──────────────────┘ └──────────────────────┘
```

---

## The Quality Gate (Task 3) — Key Logic

```
Reporter has history?
├── HIGH TRUST ──────────────────► Always full verification
│ (≥60% confirmed, ≤20% low)
├── SKEPTICISM ──────────────────► Fast-close if 0 quality signals
│ (≤20% confirmed OR ≥50% low) (no prior report needed)
└── NORMAL / NEW ────────────────► Fast-close only if:
0 quality signals
AND prior similar report exists
```

**Quality signals:** file paths cited · PoC provided · line numbers cited

**Fast-close effect:** skip code verification → use canned response template requesting specifics

---

## Scoring (batch)

```
priority_score = severity_weight + quality_weight

severity: critical=4 high=3 medium=2 low=1
quality: +1 per signal (files, PoC, lines) → max +3

≥5 Triage Immediately
≥3 Triage Soon
2 Triage
≤1 Likely Low Quality — Fast Close
```

---

## Output Files (all in REPORT_DIR)

| File | Written by | What it is |
|---|---|---|
| `GHSA-xxxx_triage.md` | pvr_triage | Full analysis report |
| `GHSA-xxxx_response_triage.md` | pvr_triage | Draft reply to reporter |
| `GHSA-xxxx_response_sent.md` | pvr_respond / batch | State-transition applied marker (idempotent) |
| `batch_queue_<repo>_<date>.md` | pvr_triage_batch | Ranked inbox table |
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overview lists the batch queue output as batch_queue_<repo>_<date>.md, but pvr_triage_batch uses save_triage_report, which appends _triage.md. Unless the writer is changed, the actual output will be batch_queue_<repo>_<date>_triage.md. Align the overview with the real filename to avoid confusion.

Suggested change
| `batch_queue_<repo>_<date>.md` | pvr_triage_batch | Ranked inbox table |
| `batch_queue_<repo>_<date>_triage.md` | pvr_triage_batch | Ranked inbox table |

Copilot uses AI. Check for mistakes.

---

## Reporter Reputation (background)

Every completed triage records **verdict + quality** against the reporter's GitHub login in a local SQLite DB. Score feeds back into the next triage's quality gate automatically. No manual configuration.

---

## One-liner workflow

```bash
./scripts/run_pvr_triage.sh batch owner/repo # see inbox
./scripts/run_pvr_triage.sh triage owner/repo GHSA-xxx # analyse one
./scripts/run_pvr_triage.sh respond owner/repo GHSA-xxx accept # accept one (triage→draft)
./scripts/run_pvr_triage.sh respond owner/repo GHSA-xxx reject # reject one (triage→closed)
./scripts/run_pvr_triage.sh respond_batch owner/repo reject # bulk state transition
# Then post each *_response_triage.md manually via the advisory URL
```

---

## Further reading

- [`taskflows/pvr_triage/README.md`](../src/seclab_taskflows/taskflows/pvr_triage/README.md) — full usage docs for all four taskflows
- [`taskflows/pvr_triage/SCORING.md`](../src/seclab_taskflows/taskflows/pvr_triage/SCORING.md) — authoritative scoring reference and fast-close decision tables
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ ignore = [
"RUF015", # Prefer `next(iter())` over single element slice
"S607", # Starting a process with a partial executable path
"SIM101", # Use a ternary expression instead of if-else-block
"SIM105", # Use contextlib.suppress (false positive: try block contains assignment)
"SIM114", # Combine `if` branches using logical `or` operator
"SIM117", # Use a single `with` statement with multiple contexts
"SIM118", # Use `key in dict` instead of `key in dict.keys()`
Expand All @@ -117,6 +118,9 @@ ignore = [

[tool.ruff.lint.per-file-ignores]
"tests/*" = [
"PLC0415", # Import not at top of file (deliberate in setUp/test methods for patching)
"PT009", # Use assert instead of unittest-style assertEqual (TestCase subclass)
"PT027", # Use pytest.raises instead of assertRaises (TestCase subclass)
"S101", # Use of assert (standard in pytest)
"SLF001", # Private member accessed (tests legitimately access module internals)
]
Loading
Loading