scori 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scori-0.1.0/.github/workflows/ci.yml +29 -0
- scori-0.1.0/.github/workflows/publish.yml +33 -0
- scori-0.1.0/.gitignore +30 -0
- scori-0.1.0/.markdownlint.json +4 -0
- scori-0.1.0/.pre-commit-config.yaml +12 -0
- scori-0.1.0/.vscode/extensions.json +9 -0
- scori-0.1.0/.vscode/launch.json +29 -0
- scori-0.1.0/.vscode/settings.json +17 -0
- scori-0.1.0/CHANGELOG.md +40 -0
- scori-0.1.0/PKG-INFO +154 -0
- scori-0.1.0/README.md +129 -0
- scori-0.1.0/ROADMAP.md +166 -0
- scori-0.1.0/pyproject.toml +73 -0
- scori-0.1.0/src/scori/__init__.py +7 -0
- scori-0.1.0/src/scori/__main__.py +421 -0
- scori-0.1.0/src/scori/_types.py +28 -0
- scori-0.1.0/src/scori/friction.py +333 -0
- scori-0.1.0/src/scori/scanner.py +101 -0
- scori-0.1.0/tests/conftest.py +12 -0
- scori-0.1.0/tests/test_friction.py +118 -0
- scori-0.1.0/tests/test_scanner.py +30 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, macos-latest]
|
|
15
|
+
python: ["3.11", "3.12", "3.13"]
|
|
16
|
+
runs-on: ${{ matrix.os }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python }}
|
|
22
|
+
- name: Install deps
|
|
23
|
+
run: uv sync --group dev
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: uv run ruff check .
|
|
26
|
+
- name: Type-check
|
|
27
|
+
run: uv run mypy src/
|
|
28
|
+
- name: Test
|
|
29
|
+
run: uv run pytest
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Publica no PyPI ao fazer push de uma tag v* (ex.: v0.1.0).
|
|
2
|
+
#
|
|
3
|
+
# IMPORTANTE — antes do primeiro push é preciso configurar o Trusted
|
|
4
|
+
# Publisher em https://pypi.org/manage/account/publishing/ com:
|
|
5
|
+
# PyPI Project Name : scori
|
|
6
|
+
# Owner : <teu-user-github>
|
|
7
|
+
# Repository name : scori
|
|
8
|
+
# Workflow filename : publish.yml
|
|
9
|
+
# Environment name : pypi
|
|
10
|
+
# Sem isso o passo "uv publish --trusted-publishing always" falha por
|
|
11
|
+
# falta de credenciais — não há tokens nem secrets para configurar.
|
|
12
|
+
|
|
13
|
+
name: publish
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
push:
|
|
17
|
+
tags: ["v*"]
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
id-token: write
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
publish:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
environment:
|
|
27
|
+
name: pypi
|
|
28
|
+
url: https://pypi.org/p/scori
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: astral-sh/setup-uv@v5
|
|
32
|
+
- run: uv build
|
|
33
|
+
- run: uv publish --trusted-publishing always
|
scori-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.coverage
|
|
9
|
+
.coverage.*
|
|
10
|
+
htmlcov/
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
|
|
15
|
+
# uv / venv
|
|
16
|
+
.venv/
|
|
17
|
+
.python-version
|
|
18
|
+
uv.lock
|
|
19
|
+
|
|
20
|
+
# OS / IDE
|
|
21
|
+
.DS_Store
|
|
22
|
+
.idea/
|
|
23
|
+
*.swp
|
|
24
|
+
|
|
25
|
+
# Claude Code local config
|
|
26
|
+
.claude/
|
|
27
|
+
|
|
28
|
+
# scori
|
|
29
|
+
.scori/
|
|
30
|
+
scori-report.html
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.6.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
9
|
+
rev: v1.11.2
|
|
10
|
+
hooks:
|
|
11
|
+
- id: mypy
|
|
12
|
+
additional_dependencies: [types-requests]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2.0",
|
|
3
|
+
"configurations": [
|
|
4
|
+
{
|
|
5
|
+
"name": "scori scan",
|
|
6
|
+
"type": "debugpy",
|
|
7
|
+
"request": "launch",
|
|
8
|
+
"module": "scori",
|
|
9
|
+
"args": ["scan", "--path", "."],
|
|
10
|
+
"justMyCode": false
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "scori friction (json)",
|
|
14
|
+
"type": "debugpy",
|
|
15
|
+
"request": "launch",
|
|
16
|
+
"module": "scori",
|
|
17
|
+
"args": ["friction", "--path", ".", "--format", "json"],
|
|
18
|
+
"justMyCode": false
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "scori friction (table)",
|
|
22
|
+
"type": "debugpy",
|
|
23
|
+
"request": "launch",
|
|
24
|
+
"module": "scori",
|
|
25
|
+
"args": ["friction", "--path", ".", "--format", "table"],
|
|
26
|
+
"justMyCode": false
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
|
|
3
|
+
"python.testing.pytestEnabled": true,
|
|
4
|
+
"python.testing.pytestArgs": ["tests"],
|
|
5
|
+
"[python]": {
|
|
6
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
|
7
|
+
"editor.formatOnSave": true,
|
|
8
|
+
"editor.codeActionsOnSave": { "source.fixAll.ruff": "explicit" }
|
|
9
|
+
},
|
|
10
|
+
"mypy-type-checker.enabled": true,
|
|
11
|
+
"editor.rulers": [88],
|
|
12
|
+
"files.exclude": {
|
|
13
|
+
"**/__pycache__": true,
|
|
14
|
+
"**/.mypy_cache": true
|
|
15
|
+
},
|
|
16
|
+
"snyk.advanced.autoSelectOrganization": true
|
|
17
|
+
}
|
scori-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
|
|
5
|
+
semantic versioning [SemVer](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `scori update` command with three modes:
|
|
12
|
+
- `--dry-run`: shows a table of pending version bumps without touching files
|
|
13
|
+
- `--apply`: writes updated versions to manifest files and creates a backup
|
|
14
|
+
in `.scori-backup/`
|
|
15
|
+
- `--rollback`: restores manifest files from the last backup
|
|
16
|
+
- `--max-friction <label>`: limits updates to deps at or below the given
|
|
17
|
+
friction label (`low`, `medium`, `high`, `critical`)
|
|
18
|
+
- `scori monitor` command: shows only dependencies with available updates,
|
|
19
|
+
sorted by friction score (highest first); marks with ★ packages where
|
|
20
|
+
updating fixes known CVEs
|
|
21
|
+
- `--watch` flag on `scori monitor` for continuous polling (default interval:
|
|
22
|
+
300 s, configurable via `--interval`)
|
|
23
|
+
- CVEs fixed by the latest release now contribute up to +15 pts to the
|
|
24
|
+
friction score (`+3` per fixed CVE, capped at 15); CVEs that persist in
|
|
25
|
+
the latest version do not affect the score
|
|
26
|
+
- `--ci` flag on `scori friction`: exits with code 1 if any dependency score
|
|
27
|
+
exceeds a configurable threshold (default: 75, override with `--threshold`)
|
|
28
|
+
- `CVEs` column in `scori friction` table showing known vulnerabilities per
|
|
29
|
+
version via the OSV API
|
|
30
|
+
- Installed version resolution for unpinned dependencies by inspecting the
|
|
31
|
+
project's local venv (`.venv/`, `venv/`, `env/`)
|
|
32
|
+
|
|
33
|
+
## [0.1.0] - 2026-05-12
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- `scori scan`: reads requirements.txt, pyproject.toml, setup.cfg
|
|
38
|
+
- `scori friction`: computes friction score 0–100 per dependency
|
|
39
|
+
- Local cache in ~/.cache/scori/ with 1-hour TTL
|
|
40
|
+
- Output as table (CLI), JSON, and basic HTML
|
scori-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scori
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Software Composition Risk Intelligence — score the real cost of updating a dependency
|
|
5
|
+
Project-URL: Homepage, https://github.com/pauloestevao795/scori
|
|
6
|
+
Project-URL: Repository, https://github.com/pauloestevao795/scori
|
|
7
|
+
Project-URL: Issues, https://github.com/pauloestevao795/scori/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/pauloestevao795/scori/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Paulo Estevao <pestevao@scori.dev>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
Keywords: dependencies,friction,risk,sca,score,security,update
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Security
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: packaging>=24.0
|
|
22
|
+
Requires-Dist: requests>=2.31
|
|
23
|
+
Requires-Dist: rich>=13.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# scori
|
|
27
|
+
|
|
28
|
+
**Software Composition Risk Intelligence** — *Know the cost before you update.*
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/scori/)
|
|
31
|
+
[](https://pypi.org/project/scori/)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
[](https://github.com/pauloestevao795/scori/actions/workflows/ci.yml)
|
|
34
|
+
|
|
35
|
+
Free tools like `pip-audit`, OSV-Scanner, and Dependabot detect vulnerabilities and open update PRs — but none of them answer the question that matters: *is it worth updating this lib right now, or does the migration cost outweigh the risk of not doing it?* `scori` quantifies that friction as a single 0–100 score per dependency, using public data from PyPI, GitHub, and the OSV vulnerability database.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install scori
|
|
41
|
+
# or
|
|
42
|
+
uv add scori
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Show friction scores for every dependency
|
|
49
|
+
scori friction --path .
|
|
50
|
+
scori friction --path . --format json > report.json
|
|
51
|
+
scori friction --path . --format html # writes scori-report.html
|
|
52
|
+
scori friction --path . --ci # exit 1 if any score > 75
|
|
53
|
+
scori friction --path . --ci --threshold 50 # stricter gate
|
|
54
|
+
|
|
55
|
+
# Show only dependencies with updates available, sorted by friction
|
|
56
|
+
scori monitor --path .
|
|
57
|
+
scori monitor --path . --watch # re-check every 5 minutes
|
|
58
|
+
scori monitor --path . --watch --interval 60 # re-check every 60 s
|
|
59
|
+
|
|
60
|
+
# Preview and apply dependency version updates
|
|
61
|
+
scori update --path . --dry-run # show what would change
|
|
62
|
+
scori update --path . --apply # write changes + create backup
|
|
63
|
+
scori update --path . --apply --max-friction medium # only Low/Medium deps
|
|
64
|
+
scori update --path . --rollback # restore from last backup
|
|
65
|
+
|
|
66
|
+
# List all detected dependencies
|
|
67
|
+
scori scan --path .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Example output (`scori friction --format table`, with color indicators):
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
scori — friction scores
|
|
74
|
+
┌───────────┬─────────┬─────────┬───────┬───────┬──────────┬─────────┐
|
|
75
|
+
│ Package │ Current │ Latest │ Jump │ Score │ Label │ CVEs │
|
|
76
|
+
├───────────┼─────────┼─────────┼───────┼───────┼──────────┼─────────┤
|
|
77
|
+
│ django │ 3.2.0 │ 5.1.0 │ major │ 78 │ Critical │ 3 → 0 ✓ │
|
|
78
|
+
│ nltk │ 3.8.1 │ 3.9.4 │ minor │ 35 │ Medium │ 9 → 0 ✓ │
|
|
79
|
+
│ requests │ 2.31.0 │ 2.32.3 │ patch │ 8 │ Low │ — │
|
|
80
|
+
└───────────┴─────────┴─────────┴───────┴───────┴──────────┴─────────┘
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The **CVEs** column shows known vulnerabilities in your current version and
|
|
84
|
+
whether they are fixed in the latest release:
|
|
85
|
+
|
|
86
|
+
- `9 → 0 ✓` — 9 CVEs in current, all fixed in latest (prioritize this update)
|
|
87
|
+
- `3` — 3 CVEs, still present in latest (update won't help with security)
|
|
88
|
+
- `—` — no known vulnerabilities in either version
|
|
89
|
+
|
|
90
|
+
`scori monitor` shows only the packages that have a newer release available,
|
|
91
|
+
sorted by friction score (highest first), and marks with ★ any package where
|
|
92
|
+
updating also fixes known CVEs.
|
|
93
|
+
|
|
94
|
+
## How it works
|
|
95
|
+
|
|
96
|
+
The friction score is a weighted sum of five components (max 100):
|
|
97
|
+
|
|
98
|
+
| Component | Max weight | Logic |
|
|
99
|
+
|-------------------------------------|------------|-------------------------------------|
|
|
100
|
+
| Semantic version jump | 50 | patch=5, minor=25, major=50 |
|
|
101
|
+
| Breaking signals in changelog | 20 | +4 per keyword found (max 20) |
|
|
102
|
+
| Affected transitive dependencies | 15 | +3 per transitive dep (max 15) |
|
|
103
|
+
| CVEs fixed by updating | 15 | +3 per fixed CVE (max 15) |
|
|
104
|
+
| Months without updating in project | 10 | +1 per month (max 10) |
|
|
105
|
+
| Current version yanked | 5 | +5 if `yanked: true` in PyPI API |
|
|
106
|
+
|
|
107
|
+
Labels:
|
|
108
|
+
|
|
109
|
+
- 0–25 → **Low** → *Safe to update*
|
|
110
|
+
- 26–50 → **Medium** → *Update with tests*
|
|
111
|
+
- 51–75 → **High** → *Update in isolated branch*
|
|
112
|
+
- 76–100 → **Critical** → *Manual migration required*
|
|
113
|
+
|
|
114
|
+
CVE data is fetched from the [OSV database](https://osv.dev) (free, no auth required).
|
|
115
|
+
CVEs that are **fixed by updating** contribute up to +15 points to the
|
|
116
|
+
friction score — a dependency where updating resolves known vulnerabilities
|
|
117
|
+
will score higher, pushing it toward the top of your update queue. CVEs that
|
|
118
|
+
remain present in the latest version do not affect the score (updating won't
|
|
119
|
+
help with those).
|
|
120
|
+
|
|
121
|
+
### Data sources
|
|
122
|
+
|
|
123
|
+
| Source | Data |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| `https://pypi.org/pypi/{pkg}/json` | Latest version, release dates, yanked status |
|
|
126
|
+
| `https://api.github.com/repos/{owner}/{repo}/releases` | Release notes for breaking signal detection |
|
|
127
|
+
| `https://api.osv.dev/v1/query` | Known CVEs per version |
|
|
128
|
+
|
|
129
|
+
Set `GITHUB_TOKEN` in your environment to raise the GitHub API rate limit from 60/h to 5000/h. PyPI and GitHub release data is cached in `~/.cache/scori/` for 1 hour. OSV results are cached in memory for the duration of a single run.
|
|
130
|
+
|
|
131
|
+
### Version resolution
|
|
132
|
+
|
|
133
|
+
For pinned dependencies (`fastapi==0.115.8`), the pinned version is used directly. For unpinned dependencies (`uvicorn` with no version), scori looks up the installed version in the project's local venv (`.venv/`, `venv/`, or `env/`) before falling back to `0.0.0`.
|
|
134
|
+
|
|
135
|
+
## Roadmap
|
|
136
|
+
|
|
137
|
+
- **v0.2** — `scori report`: rich HTML with charts and history
|
|
138
|
+
- **v0.3** — support for `poetry.lock` and `uv.lock` for real transitive tree
|
|
139
|
+
|
|
140
|
+
## Contributing
|
|
141
|
+
|
|
142
|
+
PRs and issues are welcome. Local setup:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
git clone https://github.com/pauloestevao795/scori
|
|
146
|
+
cd scori
|
|
147
|
+
uv sync --group dev
|
|
148
|
+
uv run pre-commit install
|
|
149
|
+
uv run pytest
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
scori-0.1.0/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# scori
|
|
2
|
+
|
|
3
|
+
**Software Composition Risk Intelligence** — *Know the cost before you update.*
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/scori/)
|
|
6
|
+
[](https://pypi.org/project/scori/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://github.com/pauloestevao795/scori/actions/workflows/ci.yml)
|
|
9
|
+
|
|
10
|
+
Free tools like `pip-audit`, OSV-Scanner, and Dependabot detect vulnerabilities and open update PRs — but none of them answer the question that matters: *is it worth updating this lib right now, or does the migration cost outweigh the risk of not doing it?* `scori` quantifies that friction as a single 0–100 score per dependency, using public data from PyPI, GitHub, and the OSV vulnerability database.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install scori
|
|
16
|
+
# or
|
|
17
|
+
uv add scori
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Show friction scores for every dependency
|
|
24
|
+
scori friction --path .
|
|
25
|
+
scori friction --path . --format json > report.json
|
|
26
|
+
scori friction --path . --format html # writes scori-report.html
|
|
27
|
+
scori friction --path . --ci # exit 1 if any score > 75
|
|
28
|
+
scori friction --path . --ci --threshold 50 # stricter gate
|
|
29
|
+
|
|
30
|
+
# Show only dependencies with updates available, sorted by friction
|
|
31
|
+
scori monitor --path .
|
|
32
|
+
scori monitor --path . --watch # re-check every 5 minutes
|
|
33
|
+
scori monitor --path . --watch --interval 60 # re-check every 60 s
|
|
34
|
+
|
|
35
|
+
# Preview and apply dependency version updates
|
|
36
|
+
scori update --path . --dry-run # show what would change
|
|
37
|
+
scori update --path . --apply # write changes + create backup
|
|
38
|
+
scori update --path . --apply --max-friction medium # only Low/Medium deps
|
|
39
|
+
scori update --path . --rollback # restore from last backup
|
|
40
|
+
|
|
41
|
+
# List all detected dependencies
|
|
42
|
+
scori scan --path .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Example output (`scori friction --format table`, with color indicators):
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
scori — friction scores
|
|
49
|
+
┌───────────┬─────────┬─────────┬───────┬───────┬──────────┬─────────┐
|
|
50
|
+
│ Package │ Current │ Latest │ Jump │ Score │ Label │ CVEs │
|
|
51
|
+
├───────────┼─────────┼─────────┼───────┼───────┼──────────┼─────────┤
|
|
52
|
+
│ django │ 3.2.0 │ 5.1.0 │ major │ 78 │ Critical │ 3 → 0 ✓ │
|
|
53
|
+
│ nltk │ 3.8.1 │ 3.9.4 │ minor │ 35 │ Medium │ 9 → 0 ✓ │
|
|
54
|
+
│ requests │ 2.31.0 │ 2.32.3 │ patch │ 8 │ Low │ — │
|
|
55
|
+
└───────────┴─────────┴─────────┴───────┴───────┴──────────┴─────────┘
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The **CVEs** column shows known vulnerabilities in your current version and
|
|
59
|
+
whether they are fixed in the latest release:
|
|
60
|
+
|
|
61
|
+
- `9 → 0 ✓` — 9 CVEs in current, all fixed in latest (prioritize this update)
|
|
62
|
+
- `3` — 3 CVEs, still present in latest (update won't help with security)
|
|
63
|
+
- `—` — no known vulnerabilities in either version
|
|
64
|
+
|
|
65
|
+
`scori monitor` shows only the packages that have a newer release available,
|
|
66
|
+
sorted by friction score (highest first), and marks with ★ any package where
|
|
67
|
+
updating also fixes known CVEs.
|
|
68
|
+
|
|
69
|
+
## How it works
|
|
70
|
+
|
|
71
|
+
The friction score is a weighted sum of five components (max 100):
|
|
72
|
+
|
|
73
|
+
| Component | Max weight | Logic |
|
|
74
|
+
|-------------------------------------|------------|-------------------------------------|
|
|
75
|
+
| Semantic version jump | 50 | patch=5, minor=25, major=50 |
|
|
76
|
+
| Breaking signals in changelog | 20 | +4 per keyword found (max 20) |
|
|
77
|
+
| Affected transitive dependencies | 15 | +3 per transitive dep (max 15) |
|
|
78
|
+
| CVEs fixed by updating | 15 | +3 per fixed CVE (max 15) |
|
|
79
|
+
| Months without updating in project | 10 | +1 per month (max 10) |
|
|
80
|
+
| Current version yanked | 5 | +5 if `yanked: true` in PyPI API |
|
|
81
|
+
|
|
82
|
+
Labels:
|
|
83
|
+
|
|
84
|
+
- 0–25 → **Low** → *Safe to update*
|
|
85
|
+
- 26–50 → **Medium** → *Update with tests*
|
|
86
|
+
- 51–75 → **High** → *Update in isolated branch*
|
|
87
|
+
- 76–100 → **Critical** → *Manual migration required*
|
|
88
|
+
|
|
89
|
+
CVE data is fetched from the [OSV database](https://osv.dev) (free, no auth required).
|
|
90
|
+
CVEs that are **fixed by updating** contribute up to +15 points to the
|
|
91
|
+
friction score — a dependency where updating resolves known vulnerabilities
|
|
92
|
+
will score higher, pushing it toward the top of your update queue. CVEs that
|
|
93
|
+
remain present in the latest version do not affect the score (updating won't
|
|
94
|
+
help with those).
|
|
95
|
+
|
|
96
|
+
### Data sources
|
|
97
|
+
|
|
98
|
+
| Source | Data |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `https://pypi.org/pypi/{pkg}/json` | Latest version, release dates, yanked status |
|
|
101
|
+
| `https://api.github.com/repos/{owner}/{repo}/releases` | Release notes for breaking signal detection |
|
|
102
|
+
| `https://api.osv.dev/v1/query` | Known CVEs per version |
|
|
103
|
+
|
|
104
|
+
Set `GITHUB_TOKEN` in your environment to raise the GitHub API rate limit from 60/h to 5000/h. PyPI and GitHub release data is cached in `~/.cache/scori/` for 1 hour. OSV results are cached in memory for the duration of a single run.
|
|
105
|
+
|
|
106
|
+
### Version resolution
|
|
107
|
+
|
|
108
|
+
For pinned dependencies (`fastapi==0.115.8`), the pinned version is used directly. For unpinned dependencies (`uvicorn` with no version), scori looks up the installed version in the project's local venv (`.venv/`, `venv/`, or `env/`) before falling back to `0.0.0`.
|
|
109
|
+
|
|
110
|
+
## Roadmap
|
|
111
|
+
|
|
112
|
+
- **v0.2** — `scori report`: rich HTML with charts and history
|
|
113
|
+
- **v0.3** — support for `poetry.lock` and `uv.lock` for real transitive tree
|
|
114
|
+
|
|
115
|
+
## Contributing
|
|
116
|
+
|
|
117
|
+
PRs and issues are welcome. Local setup:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git clone https://github.com/pauloestevao795/scori
|
|
121
|
+
cd scori
|
|
122
|
+
uv sync --group dev
|
|
123
|
+
uv run pre-commit install
|
|
124
|
+
uv run pytest
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT
|
scori-0.1.0/ROADMAP.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# scori Roadmap
|
|
2
|
+
|
|
3
|
+
> *Know the cost before you update.*
|
|
4
|
+
|
|
5
|
+
scori is a free, auth-free CLI for Python that quantifies the real cost of updating a dependency as a single friction score (0–100). It complements tools like `pip-audit`, OSV-Scanner, and Dependabot — which tell you *what* to update — by answering the harder question: *should you update it now, and how much will it hurt?* The friction score is a concept not yet present in open-source dependency tooling, and scori aims to make it a first-class signal in every Python project's maintenance workflow.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Current status — v0.1.0 ✅
|
|
10
|
+
|
|
11
|
+
- [x] `scori scan`: reads `requirements.txt`, `pyproject.toml` (PEP 517/518), `setup.cfg`
|
|
12
|
+
- [x] `scori friction`: computes friction score 0–100 per dependency
|
|
13
|
+
- [x] Weighted scoring algorithm: version jump, breaking signals, transitive deps, months outdated, yanked status
|
|
14
|
+
- [x] Labels: Low / Medium / High / Critical with color-coded table output
|
|
15
|
+
- [x] CVEs column via [OSV API](https://osv.dev) — shown as `3 → 0 ✓`, separate from the score
|
|
16
|
+
- [x] Output formats: table (rich), JSON, HTML
|
|
17
|
+
- [x] Local cache in `~/.cache/scori/` with 1-hour TTL (PyPI + GitHub data)
|
|
18
|
+
- [x] `GITHUB_TOKEN` support to raise API rate limit from 60/h to 5000/h
|
|
19
|
+
- [x] Installed version resolution for unpinned deps via local venv inspection
|
|
20
|
+
|
|
21
|
+
**Known limitations in v0.1.0:**
|
|
22
|
+
|
|
23
|
+
- Unpinned dependencies without a local venv fall back to `0.0.0` (no conda/pyenv/Docker support yet)
|
|
24
|
+
- Transitive dependency count is always 0 — requires a lockfile parser (planned for v0.3)
|
|
25
|
+
- CVE count is informational only — not yet factored into the friction score
|
|
26
|
+
- `scori monitor`, `scori update`, and `scori report` are stubbed in the CLI but not implemented
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## v0.2 — Full CLI surface
|
|
31
|
+
|
|
32
|
+
Complete the commands already declared in the CLI but not yet implemented.
|
|
33
|
+
|
|
34
|
+
### `scori monitor` ✅
|
|
35
|
+
|
|
36
|
+
- [x] Poll PyPI for new releases on all project dependencies
|
|
37
|
+
- [x] Output a "updates available" table sorted by friction score (highest friction first)
|
|
38
|
+
- [x] `--watch` flag for continuous monitoring with a configurable interval
|
|
39
|
+
- [x] Highlight dependencies where a new release also fixes known CVEs (★ marker)
|
|
40
|
+
|
|
41
|
+
### `scori update` ✅
|
|
42
|
+
|
|
43
|
+
- [x] `--dry-run`: show a diff of what would change in the manifest without applying
|
|
44
|
+
- [x] `--apply`: apply updates and create an automatic backup of the original manifest
|
|
45
|
+
- [x] `--rollback`: restore the most recent backup
|
|
46
|
+
- [x] `--max-friction <label>`: only update deps at or below a given friction label (e.g. `medium`)
|
|
47
|
+
|
|
48
|
+
### `scori report`
|
|
49
|
+
|
|
50
|
+
- [ ] Standalone HTML report with a visual traffic-light indicator per dependency
|
|
51
|
+
- [ ] Structured JSON export suitable for CI/CD pipeline consumption
|
|
52
|
+
- [ ] `--ci` flag: exit with code 1 if any dependency exceeds a configurable score threshold
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## v0.3 — Smarter scoring
|
|
57
|
+
|
|
58
|
+
Improve the accuracy and depth of the friction score algorithm.
|
|
59
|
+
|
|
60
|
+
### Real transitive dependency counts
|
|
61
|
+
|
|
62
|
+
- [ ] Parse `poetry.lock` to count packages that depend on the package being updated
|
|
63
|
+
- [ ] Parse `uv.lock` for the same
|
|
64
|
+
- [ ] Use the resolved count in the score weight (currently always 0)
|
|
65
|
+
|
|
66
|
+
### CVEs in the score
|
|
67
|
+
|
|
68
|
+
- [x] Incorporate OSV CVE count directly into the weighted algorithm (up to +15 pts)
|
|
69
|
+
- [ ] Weight CVSS ≥ 9.0 CVEs more heavily than lower-severity ones
|
|
70
|
+
|
|
71
|
+
### Improved breaking signal detection
|
|
72
|
+
|
|
73
|
+
- [ ] Scan `CHANGELOG.md` from the GitHub repo in addition to release notes
|
|
74
|
+
- [ ] Detect `BREAKING CHANGE:` in Conventional Commits commit history
|
|
75
|
+
- [ ] Heuristic diff of `.pyi` type stub files between versions as an API-change signal
|
|
76
|
+
|
|
77
|
+
### Broader version resolution
|
|
78
|
+
|
|
79
|
+
- [ ] Resolve unpinned versions via `conda list --json` when inside a conda environment
|
|
80
|
+
- [ ] Support pyenv shims as a version source
|
|
81
|
+
- [ ] Optional: detect version from Docker image labels (requires Docker CLI, off by default)
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## v0.4 — Integrations
|
|
86
|
+
|
|
87
|
+
Bring scori into the workflows and tools developers already use.
|
|
88
|
+
|
|
89
|
+
### GitHub Actions
|
|
90
|
+
|
|
91
|
+
- [ ] Official `scori-action` published to the GitHub Marketplace
|
|
92
|
+
- [ ] Automatic PR comment with a friction table for any changed dependencies
|
|
93
|
+
- [ ] Dynamic badge for `README.md` showing the project's average friction score
|
|
94
|
+
|
|
95
|
+
### Pre-commit hook
|
|
96
|
+
|
|
97
|
+
- [ ] Official hook for `.pre-commit-config.yaml`
|
|
98
|
+
- [ ] Configurable threshold — block commit if any dep exceeds it
|
|
99
|
+
|
|
100
|
+
### VSCode Extension *(stretch goal)*
|
|
101
|
+
|
|
102
|
+
- [ ] Inline decoration showing the friction score per line in `requirements.txt` / `pyproject.toml`
|
|
103
|
+
- [ ] CodeLens action: "Run scori friction on this package"
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## v0.5 — Intelligence layer
|
|
108
|
+
|
|
109
|
+
Higher-level features that turn scori from a scoring tool into a maintenance advisor.
|
|
110
|
+
|
|
111
|
+
### Score history
|
|
112
|
+
|
|
113
|
+
- [ ] Track friction scores over time per project in local storage
|
|
114
|
+
- [ ] Trend chart: surface dependencies that are becoming riskier over successive runs
|
|
115
|
+
|
|
116
|
+
### Risk profiles
|
|
117
|
+
|
|
118
|
+
- [ ] Per-project `.scori.toml` configuration for custom thresholds and weights
|
|
119
|
+
- [ ] Built-in profiles: `conservative`, `balanced`, `aggressive`
|
|
120
|
+
|
|
121
|
+
### Suggested update order
|
|
122
|
+
|
|
123
|
+
- [ ] Rank dependencies by update order to minimise total migration risk
|
|
124
|
+
- [ ] Detect conflicts between simultaneous updates (e.g. shared transitive dep with incompatible constraints)
|
|
125
|
+
|
|
126
|
+
### LLM-assisted changelog summary *(opt-in)*
|
|
127
|
+
|
|
128
|
+
- [ ] Plain-language summary of what changes in a given update
|
|
129
|
+
- [ ] Supports local inference via Ollama or `OPENAI_API_KEY` — never required to use scori
|
|
130
|
+
- [ ] Off by default; enabled explicitly with `--summarise`
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Non-goals
|
|
135
|
+
|
|
136
|
+
scori has a deliberate scope. The following are explicitly out of scope:
|
|
137
|
+
|
|
138
|
+
- **Not a CVE scanner.** scori shows CVE counts as context, but `pip-audit` and OSV-Scanner do this properly. Use them alongside scori, not instead.
|
|
139
|
+
- **Not a package manager.** scori reads and optionally edits manifests, but it does not resolve or install packages.
|
|
140
|
+
- **Python only.** Supporting npm, cargo, or other ecosystems is out of scope. scori's scoring model is designed around the PyPI and GitHub release data available for Python packages.
|
|
141
|
+
- **No account required.** scori will never require a login, subscription, or mandatory API key. Optional tokens (e.g. `GITHUB_TOKEN`) may improve rate limits, but the tool is always fully functional without them.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## How to contribute
|
|
146
|
+
|
|
147
|
+
Contributions are welcome. See [`CONTRIBUTING.md`](CONTRIBUTING.md) (coming soon) for the full guide.
|
|
148
|
+
|
|
149
|
+
**Priority areas for external contributions:**
|
|
150
|
+
|
|
151
|
+
- Manifest parsers for additional formats (`conda.yml`, `Pipfile`, `Pipfile.lock`)
|
|
152
|
+
- Integration tests against well-known real-world projects
|
|
153
|
+
- CLI message internationalisation (i18n)
|
|
154
|
+
|
|
155
|
+
Issues labelled **`good first issue`** on GitHub are the recommended starting point for new contributors.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Versioning policy
|
|
160
|
+
|
|
161
|
+
scori follows [Semantic Versioning](https://semver.org).
|
|
162
|
+
|
|
163
|
+
- Patch releases (`0.x.y`) fix bugs without changing behaviour.
|
|
164
|
+
- Minor releases (`0.x`) add features in a backwards-compatible way.
|
|
165
|
+
- Breaking changes to the CLI interface will only occur in major releases.
|
|
166
|
+
- The public API (`FrictionResult`, `Dependency`, `compute()`) is considered stable from v1.0 onwards. Until then, minor releases may include breaking changes to the Python API — the CLI surface is the stable interface.
|