Skip to content

Commit ce4298e

Browse files
committed
Initial Patchrank CLI
0 parents  commit ce4298e

28 files changed

Lines changed: 1307 additions & 0 deletions

.editorconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
insert_final_newline = true
7+
trim_trailing_whitespace = true
8+
indent_style = space
9+
indent_size = 2
10+
11+
[*.py]
12+
indent_size = 4

.gitattributes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
* text=auto eol=lf
2+
3+
*.png binary
4+
*.jpg binary
5+
*.jpeg binary
6+
*.gif binary
7+
*.ico binary
8+
*.pdf binary

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
runs-on: ubuntu-latest
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
python-version: ["3.10", "3.11", "3.12", "3.13"]
19+
steps:
20+
- name: Check out
21+
uses: actions/checkout@v4
22+
23+
- name: Set up Python
24+
uses: actions/setup-python@v5
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
28+
- name: Run unit tests
29+
run: python -m unittest discover -s tests
30+
31+
- name: Run CLI smoke test
32+
run: PYTHONPATH=src python -m patchrank rank examples/trivy.json --kev examples/kev.json --epss examples/epss.json --format markdown --limit 5

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
__pycache__/
2+
*.py[cod]
3+
.pytest_cache/
4+
.mypy_cache/
5+
.ruff_cache/
6+
.venv/
7+
venv/
8+
build/
9+
dist/
10+
*.egg-info/
11+
.coverage
12+
coverage.xml
13+
14+
.env
15+
.env.*
16+
!.env.example
17+
18+
.DS_Store
19+
Thumbs.db
20+
.idea/
21+
.vscode/

CODE_OF_CONDUCT.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Code of Conduct
2+
3+
Be respectful, specific, and evidence-driven. Security work often involves
4+
disagreement about risk; keep discussions focused on data, behavior, and fixes.

CONTRIBUTING.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Contributing
2+
3+
Thanks for helping improve Patchrank.
4+
5+
## Local Checks
6+
7+
```bash
8+
python -m unittest discover -s tests
9+
PYTHONPATH=src python -m patchrank rank examples/trivy.json --kev examples/kev.json --epss examples/epss.json
10+
```
11+
12+
PowerShell:
13+
14+
```powershell
15+
$env:PYTHONPATH = "src"
16+
python -m unittest discover -s tests
17+
python -m patchrank rank examples/trivy.json --kev examples/kev.json --epss examples/epss.json
18+
```
19+
20+
## Guidelines
21+
22+
- Keep runtime dependencies minimal.
23+
- Add tests for each parser or scoring change.
24+
- Keep scoring explainable; every score boost should have a reason string.
25+
- Do not commit private vulnerability reports, tokens, or customer data.
26+
- Prefer fixtures with synthetic package names and public CVE IDs.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 whatisproblem
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Patchrank
2+
3+
Patchrank is a small security CLI that turns vulnerability scanner output into a
4+
patch priority report. It accepts Trivy JSON, Grype JSON, generic CVE JSON, or a
5+
plain text list of CVEs, then ranks findings with CVSS, severity, CISA KEV,
6+
FIRST EPSS, fix availability, internet exposure, and asset criticality signals.
7+
8+
It is built for security teams and maintainers who need a practical answer to:
9+
10+
> What should we patch first?
11+
12+
## Features
13+
14+
- Parse Trivy JSON, Grype JSON, generic JSON, and plain CVE lists.
15+
- Load CISA Known Exploited Vulnerabilities data from a local JSON file.
16+
- Load FIRST EPSS data from a local JSON/CSV-style export.
17+
- Optionally fetch current CISA KEV and FIRST EPSS data.
18+
- Score findings with explainable reason strings.
19+
- Output table, Markdown, or JSON.
20+
- Run with only the Python standard library.
21+
22+
## Install
23+
24+
From a clone:
25+
26+
```bash
27+
python -m pip install .
28+
```
29+
30+
For development without installing:
31+
32+
```bash
33+
python -m patchrank --help
34+
```
35+
36+
When running directly from the repository, set `PYTHONPATH=src` if your shell
37+
does not automatically see the package:
38+
39+
```bash
40+
PYTHONPATH=src python -m patchrank --help
41+
```
42+
43+
PowerShell:
44+
45+
```powershell
46+
$env:PYTHONPATH = "src"
47+
python -m patchrank --help
48+
```
49+
50+
## Quick Start
51+
52+
Rank a Trivy report with local KEV and EPSS fixtures:
53+
54+
```bash
55+
patchrank rank examples/trivy.json --kev examples/kev.json --epss examples/epss.json --format markdown
56+
```
57+
58+
Rank a plain CVE list and mark the asset as internet-facing:
59+
60+
```bash
61+
patchrank rank examples/cves.txt --internet-facing --asset-criticality 8
62+
```
63+
64+
Fetch live KEV and EPSS data:
65+
66+
```bash
67+
patchrank rank examples/cves.txt --fetch-kev --fetch-epss --format json
68+
```
69+
70+
## Scoring Model
71+
72+
Patchrank uses a transparent additive score capped at 100:
73+
74+
- CISA KEV match: high weight because exploitation is known.
75+
- EPSS probability: higher probability raises priority.
76+
- CVSS/severity: impact and vendor severity still matter.
77+
- Fix availability: patched packages are easier to act on.
78+
- Internet-facing asset: exposure raises priority.
79+
- Asset criticality: user-supplied business context.
80+
81+
Priority bands:
82+
83+
| Score | Priority |
84+
|---:|---|
85+
| 80-100 | patch-now |
86+
| 60-79 | urgent |
87+
| 40-59 | soon |
88+
| 0-39 | monitor |
89+
90+
This is an operational heuristic, not a replacement for human risk analysis.
91+
92+
## Supported Inputs
93+
94+
### Trivy
95+
96+
```bash
97+
trivy image --format json --output trivy.json nginx:latest
98+
patchrank rank trivy.json
99+
```
100+
101+
### Grype
102+
103+
```bash
104+
grype dir:. -o json > grype.json
105+
patchrank rank grype.json
106+
```
107+
108+
### Plain Text
109+
110+
Any line containing a CVE ID is accepted:
111+
112+
```text
113+
CVE-2024-3094
114+
CVE-2023-34362
115+
```
116+
117+
## Data Sources
118+
119+
- CISA Known Exploited Vulnerabilities catalog:
120+
https://www.cisa.gov/known-exploited-vulnerabilities-catalog
121+
- FIRST EPSS API:
122+
https://www.first.org/epss/api
123+
124+
Patchrank can run fully offline when you provide local KEV and EPSS files.
125+
126+
## Development
127+
128+
Run tests:
129+
130+
```bash
131+
python -m unittest discover -s tests
132+
```
133+
134+
Run a smoke check from a clone:
135+
136+
```bash
137+
PYTHONPATH=src python -m patchrank rank examples/trivy.json --kev examples/kev.json --epss examples/epss.json --format markdown
138+
```
139+
140+
PowerShell:
141+
142+
```powershell
143+
$env:PYTHONPATH = "src"
144+
python -m patchrank rank examples/trivy.json --kev examples/kev.json --epss examples/epss.json --format markdown
145+
```
146+
147+
## Security
148+
149+
Patchrank reads local reports and optionally fetches public vulnerability
150+
intelligence. It does not need credentials. Do not upload private scanner
151+
reports to third-party systems unless you understand their contents.
152+
153+
See [SECURITY.md](SECURITY.md).
154+
155+
## License
156+
157+
MIT. See [LICENSE](LICENSE).

SECURITY.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Security Policy
2+
3+
## Supported Scope
4+
5+
Security reports are welcome for:
6+
7+
- Incorrect handling of local report files.
8+
- Unsafe network behavior in enrichment features.
9+
- Output that could leak private report content unexpectedly.
10+
- Supply-chain issues in CI or packaging metadata.
11+
12+
Patchrank does not require credentials. Please do not include private scanner
13+
reports, tokens, or customer system details in public issues.
14+
15+
## Reporting
16+
17+
Use GitHub private security advisories when available. Otherwise, open an issue
18+
with a minimal synthetic reproduction.

0 commit comments

Comments
 (0)