pydoctor-cli 2.1.0__tar.gz → 2.3.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.
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/PKG-INFO +74 -3
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/README.md +71 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/__init__.py +1 -1
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/analysis/health_score.py +48 -5
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/cache/cache_manager.py +8 -1
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/cli/main.py +228 -37
- pydoctor_cli-2.3.0/pydoctor/core/__init__.py +17 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/core/analyzer.py +68 -1
- pydoctor_cli-2.3.0/pydoctor/core/baseline.py +153 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/core/project.py +105 -78
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/core/report.py +8 -0
- pydoctor_cli-2.3.0/pydoctor/reports/docs_links.py +52 -0
- pydoctor_cli-2.3.0/pydoctor/reports/fingerprint.py +57 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/reports/json_formatter.py +45 -15
- pydoctor_cli-2.3.0/pydoctor/reports/sarif_formatter.py +187 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/reports/table_formatter.py +28 -2
- pydoctor_cli-2.3.0/pydoctor/scanners/ci_scanner.py +319 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/scanners/dependency_scanner.py +54 -9
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/scanners/env_scanner.py +30 -12
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/scanners/outdated_package_scanner.py +40 -14
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/scanners/unused_package_scanner.py +57 -8
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/scanners/vulnerability_scanner.py +10 -2
- pydoctor_cli-2.3.0/pydoctor/security/cvss.py +77 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/security/osv_client.py +140 -61
- pydoctor_cli-2.3.0/pydoctor/security/secrets.py +296 -0
- pydoctor_cli-2.3.0/pydoctor/utils/env_probe.py +145 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/utils/file_utils.py +4 -2
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/utils/parser_utils.py +24 -14
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/utils/pip_utils.py +40 -29
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor_cli.egg-info/PKG-INFO +74 -3
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor_cli.egg-info/SOURCES.txt +13 -3
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor_cli.egg-info/top_level.txt +1 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pyproject.toml +3 -3
- pydoctor_cli-2.3.0/tests/test_baseline.py +165 -0
- pydoctor_cli-2.3.0/tests/test_ci_scanner.py +196 -0
- pydoctor_cli-2.3.0/tests/test_cli_ux.py +72 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/tests/test_env_scanner.py +27 -14
- pydoctor_cli-2.3.0/tests/test_environment.py +197 -0
- pydoctor_cli-2.3.0/tests/test_github_action.py +86 -0
- pydoctor_cli-2.3.0/tests/test_osv_parsing.py +181 -0
- pydoctor_cli-2.3.0/tests/test_report_output.py +72 -0
- pydoctor_cli-2.3.0/tests/test_sarif.py +117 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/tests/test_unused_scanner.py +19 -0
- pydoctor_cli-2.1.0/pydoctor/core/__init__.py +0 -7
- pydoctor_cli-2.1.0/pydoctor/performance/__init__.py +0 -5
- pydoctor_cli-2.1.0/pydoctor/performance/executor.py +0 -132
- pydoctor_cli-2.1.0/pydoctor/performance/parallel.py +0 -40
- pydoctor_cli-2.1.0/pydoctor/scanners/ci_scanner.py +0 -141
- pydoctor_cli-2.1.0/tests/test_ci_scanner.py +0 -39
- pydoctor_cli-2.1.0/tests/test_cli_ux.py +0 -38
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/LICENSE +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/analysis/__init__.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/cache/__init__.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/cli/__init__.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/config/__init__.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/config/settings.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/reports/__init__.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/reports/terminal_colors.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/scanners/__init__.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/security/__init__.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/utils/__init__.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor/utils/subprocess_utils.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor_cli.egg-info/dependency_links.txt +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor_cli.egg-info/entry_points.txt +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/pydoctor_cli.egg-info/requires.txt +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/setup.cfg +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/tests/conftest.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/tests/test_dependency_scanner.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/tests/test_fix_command.py +0 -0
- {pydoctor_cli-2.1.0 → pydoctor_cli-2.3.0}/tests/test_vulnerability_scanner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydoctor-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Premium Python environment diagnostic CLI — your project's personal physician.
|
|
5
5
|
Author-email: Favour Bamgboye <demiladebamgboye@gmail.com>
|
|
6
6
|
Maintainer-email: Favour Bamgboye <demiladebamgboye@gmail.com>
|
|
@@ -206,9 +206,9 @@ License: Apache License
|
|
|
206
206
|
See the License for the specific language governing permissions and
|
|
207
207
|
limitations under the License.
|
|
208
208
|
|
|
209
|
-
Project-URL: Homepage, https://pydoctor.
|
|
209
|
+
Project-URL: Homepage, https://pydoctor-docs.thally.app
|
|
210
210
|
Project-URL: Repository, https://github.com/iamAgbaCoder/pydoctor-cli
|
|
211
|
-
Project-URL: Documentation, https://pydoctor.
|
|
211
|
+
Project-URL: Documentation, https://pydoctor-docs.thally.app
|
|
212
212
|
Project-URL: Bug Tracker, https://github.com/iamAgbaCoder/pydoctor-cli/issues
|
|
213
213
|
Project-URL: Changelog, https://github.com/iamAgbaCoder/pydoctor-cli/releases
|
|
214
214
|
Keywords: pydoctor,diagnosis,dependency-checker,vulnerability-scanner,python-health,cli-tool,security-audit,static-analysis
|
|
@@ -253,6 +253,7 @@ Dynamic: license-file
|
|
|
253
253
|
|
|
254
254
|
**PyDoctor** is a standalone, developer-friendly diagnostic assistant for Python environments. It acts like a personal physician for your code — scanning your project and environment to prescribe fixes for misconfigurations, dependencies, and security vulnerabilities.
|
|
255
255
|
|
|
256
|
+
[](https://pepy.tech/projects/pydoctor-cli)
|
|
256
257
|
[](https://github.com/iamAgbaCoder/pydoctor-cli/actions)
|
|
257
258
|
[](https://pypi.org/project/pydoctor-cli/)
|
|
258
259
|
[](https://pypi.org/project/pydoctor-cli/)
|
|
@@ -299,6 +300,16 @@ pipx install pydoctor-cli
|
|
|
299
300
|
pip install pydoctor-cli
|
|
300
301
|
```
|
|
301
302
|
|
|
303
|
+
> Install `pydoctor-cli`, not `pydoctor`: that is an unrelated project (Twisted's API doc generator) that installs a command with the same name.
|
|
304
|
+
|
|
305
|
+
PyDoctor scans your **project's** Python, wherever PyDoctor itself is installed. It uses the active virtualenv, or a `.venv`/`venv` in the project, and tells you which interpreter it chose. To choose one yourself:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
pydoctor diagnose --python .venv/bin/python # or set PYDOCTOR_PYTHON
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
📖 Full documentation: **https://pydoctor-docs.thally.app**
|
|
312
|
+
|
|
302
313
|
---
|
|
303
314
|
|
|
304
315
|
## ⚡ Usage
|
|
@@ -311,6 +322,44 @@ Run a comprehensive check-up on your project.
|
|
|
311
322
|
pydoctor diagnose
|
|
312
323
|
```
|
|
313
324
|
|
|
325
|
+
### In CI: GitHub Action
|
|
326
|
+
|
|
327
|
+
Findings appear as code-scanning alerts on pull requests, and the job fails on new problems:
|
|
328
|
+
|
|
329
|
+
```yaml
|
|
330
|
+
permissions:
|
|
331
|
+
contents: read
|
|
332
|
+
security-events: write
|
|
333
|
+
|
|
334
|
+
steps:
|
|
335
|
+
- uses: actions/checkout@v4
|
|
336
|
+
- uses: actions/setup-python@v5
|
|
337
|
+
with: { python-version: "3.12" }
|
|
338
|
+
- run: pip install -r requirements.txt
|
|
339
|
+
- uses: iamAgbaCoder/pydoctor-cli@v2
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Set `ci-only: "true"` to run only the secrets and workflow checks. Any CI system can use `pydoctor check --sarif pydoctor.sarif` directly.
|
|
343
|
+
|
|
344
|
+
### Adopting it in an existing project: baselines
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
pydoctor baseline # accept today's issues; commit .pydoctor-baseline.json
|
|
348
|
+
pydoctor check # now fails only on new issues
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Problems with the scan itself (unreadable environment, unreachable vulnerability database) are never baselined.
|
|
352
|
+
|
|
353
|
+
### pre-commit
|
|
354
|
+
|
|
355
|
+
```yaml
|
|
356
|
+
repos:
|
|
357
|
+
- repo: https://github.com/iamAgbaCoder/pydoctor-cli
|
|
358
|
+
rev: v2.3.0
|
|
359
|
+
hooks:
|
|
360
|
+
- id: pydoctor-ci # blocks commits that add secrets or risky workflows
|
|
361
|
+
```
|
|
362
|
+
|
|
314
363
|
### Automated Fixes
|
|
315
364
|
|
|
316
365
|
Let PyDoctor perform the heavy lifting. It will offer to install missing packages, upgrade outdated ones, and clean up your environment.
|
|
@@ -323,6 +372,28 @@ pydoctor fix
|
|
|
323
372
|
|
|
324
373
|
## Release History
|
|
325
374
|
|
|
375
|
+
### v2.3.0
|
|
376
|
+
Built for CI: adopt pydoctor in an existing project without a wall of failures, and see findings where you review code.
|
|
377
|
+
|
|
378
|
+
- **Baselines:** `pydoctor baseline` records today's issues in `.pydoctor-baseline.json`; scans then fail only on new ones and tell you when baselined issues are fixed. Fingerprints survive unrelated edits (a secret moved down by new lines stays known) and package upgrades. `--ci` updates only the CI-guard entries. `--baseline FILE` / `--no-baseline` on every scan command. Broken scans are never baselined.
|
|
379
|
+
- **SARIF output:** `--sarif FILE` on every scan command (SARIF 2.1.0, schema-validated). Vulnerabilities carry their CVSS score as security severity and point at the line that declares the package; secrets and workflow findings point at their file and line.
|
|
380
|
+
- **GitHub Action:** `uses: iamAgbaCoder/pydoctor-cli@v2` installs pydoctor in isolation, uploads findings to code scanning, and fails the job on new problems.
|
|
381
|
+
- **pre-commit hooks:** `pydoctor-ci` blocks commits that add secrets, `.env` files or risky workflows; `pydoctor-check` runs the full scan at pre-push.
|
|
382
|
+
- **Docs links:** every issue links to its page on pydoctor-docs.thally.app (`docs_url` in JSON; shown in the terminal for errors).
|
|
383
|
+
|
|
384
|
+
### v2.2.0
|
|
385
|
+
Results you can trust: every fix in this release removes a case where PyDoctor reported a problem as fine, or got a severity wrong.
|
|
386
|
+
|
|
387
|
+
- **Scans the right environment:** facts come from your project's interpreter, not PyDoctor's. Under pipx or `uv tool`, and in uv environments (which have no pip), 2.1.0 saw 0 packages and reported everything as OK. Works without pip (package metadata, `uv pip list --outdated`). New `--python` / `PYDOCTOR_PYTHON`; the report shows which interpreter was scanned and why.
|
|
388
|
+
- **Fails loudly:** `ENV_PACKAGES_UNAVAILABLE` (error) when the environment can't be read, `ENV_DEPS_NOT_INSTALLED` when the scanned Python has none of your dependencies, and outdated checks that can't run say so instead of "all up to date".
|
|
389
|
+
- **Virtualenv detection works:** it was always "active". The `diagnose` no-venv prompt now appears, and never blocks CI or piped runs.
|
|
390
|
+
- **Real CVSS scores:** severity is computed from the CVSS v3 vector. Previously a 3.1 (Low) and a 9.8 (Critical) vulnerability were both shown as critical. GitHub's `MODERATE` is now medium.
|
|
391
|
+
- **Right fix version:** for advisories with one fix per release line, `fix` targets your line (Django 5.1.2 → 5.1.4, not 4.2.17).
|
|
392
|
+
- **CI guard finds real leaks:** scans every committed text file, adds Stripe, OpenAI, Anthropic, Slack, Google, private-key, database-URL and `SECRET_KEY` rules, flags committed `.env` files, and checks workflows for `pull_request_target` "pwn requests", script injection and `write-all` permissions. Findings have line numbers; add `# pydoctor: ignore` to silence one. Zero findings on Flask, Django, requests, httpx, FastAPI, DRF and pip.
|
|
393
|
+
- **A score that moves:** a package's advisories count once, categories have diminishing returns, and any critical issue keeps the score below "Good". Expect different (usually higher) scores than 2.1.0.
|
|
394
|
+
- **JSON:** includes `health`; `scan-security --json` reports advisory IDs, aliases, fixed version and CVSS score.
|
|
395
|
+
- Smaller: database drivers, hashers and cache clients picked by Django settings count as used; faster advisory fetching; `--no-cache` no longer wipes the cache; circular import fixed; `github <repo>` and `docker` describe what they actually do.
|
|
396
|
+
|
|
326
397
|
### v2.1.0
|
|
327
398
|
- **`pydoctor fix` reports what really happened:** upgrades are verified against the installed version. If a pin in `pyproject.toml` blocks an upgrade, pydoctor says so and shows the constraint instead of printing "fixed".
|
|
328
399
|
- **One prompt per package:** vulnerable packages are offered once, with the advisory count, and upgraded to the lowest version that fixes every advisory.
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**PyDoctor** is a standalone, developer-friendly diagnostic assistant for Python environments. It acts like a personal physician for your code — scanning your project and environment to prescribe fixes for misconfigurations, dependencies, and security vulnerabilities.
|
|
6
6
|
|
|
7
|
+
[](https://pepy.tech/projects/pydoctor-cli)
|
|
7
8
|
[](https://github.com/iamAgbaCoder/pydoctor-cli/actions)
|
|
8
9
|
[](https://pypi.org/project/pydoctor-cli/)
|
|
9
10
|
[](https://pypi.org/project/pydoctor-cli/)
|
|
@@ -50,6 +51,16 @@ pipx install pydoctor-cli
|
|
|
50
51
|
pip install pydoctor-cli
|
|
51
52
|
```
|
|
52
53
|
|
|
54
|
+
> Install `pydoctor-cli`, not `pydoctor`: that is an unrelated project (Twisted's API doc generator) that installs a command with the same name.
|
|
55
|
+
|
|
56
|
+
PyDoctor scans your **project's** Python, wherever PyDoctor itself is installed. It uses the active virtualenv, or a `.venv`/`venv` in the project, and tells you which interpreter it chose. To choose one yourself:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pydoctor diagnose --python .venv/bin/python # or set PYDOCTOR_PYTHON
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
📖 Full documentation: **https://pydoctor-docs.thally.app**
|
|
63
|
+
|
|
53
64
|
---
|
|
54
65
|
|
|
55
66
|
## ⚡ Usage
|
|
@@ -62,6 +73,44 @@ Run a comprehensive check-up on your project.
|
|
|
62
73
|
pydoctor diagnose
|
|
63
74
|
```
|
|
64
75
|
|
|
76
|
+
### In CI: GitHub Action
|
|
77
|
+
|
|
78
|
+
Findings appear as code-scanning alerts on pull requests, and the job fails on new problems:
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
permissions:
|
|
82
|
+
contents: read
|
|
83
|
+
security-events: write
|
|
84
|
+
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
- uses: actions/setup-python@v5
|
|
88
|
+
with: { python-version: "3.12" }
|
|
89
|
+
- run: pip install -r requirements.txt
|
|
90
|
+
- uses: iamAgbaCoder/pydoctor-cli@v2
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Set `ci-only: "true"` to run only the secrets and workflow checks. Any CI system can use `pydoctor check --sarif pydoctor.sarif` directly.
|
|
94
|
+
|
|
95
|
+
### Adopting it in an existing project: baselines
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pydoctor baseline # accept today's issues; commit .pydoctor-baseline.json
|
|
99
|
+
pydoctor check # now fails only on new issues
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Problems with the scan itself (unreadable environment, unreachable vulnerability database) are never baselined.
|
|
103
|
+
|
|
104
|
+
### pre-commit
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
repos:
|
|
108
|
+
- repo: https://github.com/iamAgbaCoder/pydoctor-cli
|
|
109
|
+
rev: v2.3.0
|
|
110
|
+
hooks:
|
|
111
|
+
- id: pydoctor-ci # blocks commits that add secrets or risky workflows
|
|
112
|
+
```
|
|
113
|
+
|
|
65
114
|
### Automated Fixes
|
|
66
115
|
|
|
67
116
|
Let PyDoctor perform the heavy lifting. It will offer to install missing packages, upgrade outdated ones, and clean up your environment.
|
|
@@ -74,6 +123,28 @@ pydoctor fix
|
|
|
74
123
|
|
|
75
124
|
## Release History
|
|
76
125
|
|
|
126
|
+
### v2.3.0
|
|
127
|
+
Built for CI: adopt pydoctor in an existing project without a wall of failures, and see findings where you review code.
|
|
128
|
+
|
|
129
|
+
- **Baselines:** `pydoctor baseline` records today's issues in `.pydoctor-baseline.json`; scans then fail only on new ones and tell you when baselined issues are fixed. Fingerprints survive unrelated edits (a secret moved down by new lines stays known) and package upgrades. `--ci` updates only the CI-guard entries. `--baseline FILE` / `--no-baseline` on every scan command. Broken scans are never baselined.
|
|
130
|
+
- **SARIF output:** `--sarif FILE` on every scan command (SARIF 2.1.0, schema-validated). Vulnerabilities carry their CVSS score as security severity and point at the line that declares the package; secrets and workflow findings point at their file and line.
|
|
131
|
+
- **GitHub Action:** `uses: iamAgbaCoder/pydoctor-cli@v2` installs pydoctor in isolation, uploads findings to code scanning, and fails the job on new problems.
|
|
132
|
+
- **pre-commit hooks:** `pydoctor-ci` blocks commits that add secrets, `.env` files or risky workflows; `pydoctor-check` runs the full scan at pre-push.
|
|
133
|
+
- **Docs links:** every issue links to its page on pydoctor-docs.thally.app (`docs_url` in JSON; shown in the terminal for errors).
|
|
134
|
+
|
|
135
|
+
### v2.2.0
|
|
136
|
+
Results you can trust: every fix in this release removes a case where PyDoctor reported a problem as fine, or got a severity wrong.
|
|
137
|
+
|
|
138
|
+
- **Scans the right environment:** facts come from your project's interpreter, not PyDoctor's. Under pipx or `uv tool`, and in uv environments (which have no pip), 2.1.0 saw 0 packages and reported everything as OK. Works without pip (package metadata, `uv pip list --outdated`). New `--python` / `PYDOCTOR_PYTHON`; the report shows which interpreter was scanned and why.
|
|
139
|
+
- **Fails loudly:** `ENV_PACKAGES_UNAVAILABLE` (error) when the environment can't be read, `ENV_DEPS_NOT_INSTALLED` when the scanned Python has none of your dependencies, and outdated checks that can't run say so instead of "all up to date".
|
|
140
|
+
- **Virtualenv detection works:** it was always "active". The `diagnose` no-venv prompt now appears, and never blocks CI or piped runs.
|
|
141
|
+
- **Real CVSS scores:** severity is computed from the CVSS v3 vector. Previously a 3.1 (Low) and a 9.8 (Critical) vulnerability were both shown as critical. GitHub's `MODERATE` is now medium.
|
|
142
|
+
- **Right fix version:** for advisories with one fix per release line, `fix` targets your line (Django 5.1.2 → 5.1.4, not 4.2.17).
|
|
143
|
+
- **CI guard finds real leaks:** scans every committed text file, adds Stripe, OpenAI, Anthropic, Slack, Google, private-key, database-URL and `SECRET_KEY` rules, flags committed `.env` files, and checks workflows for `pull_request_target` "pwn requests", script injection and `write-all` permissions. Findings have line numbers; add `# pydoctor: ignore` to silence one. Zero findings on Flask, Django, requests, httpx, FastAPI, DRF and pip.
|
|
144
|
+
- **A score that moves:** a package's advisories count once, categories have diminishing returns, and any critical issue keeps the score below "Good". Expect different (usually higher) scores than 2.1.0.
|
|
145
|
+
- **JSON:** includes `health`; `scan-security --json` reports advisory IDs, aliases, fixed version and CVSS score.
|
|
146
|
+
- Smaller: database drivers, hashers and cache clients picked by Django settings count as used; faster advisory fetching; `--no-cache` no longer wipes the cache; circular import fixed; `github <repo>` and `docker` describe what they actually do.
|
|
147
|
+
|
|
77
148
|
### v2.1.0
|
|
78
149
|
- **`pydoctor fix` reports what really happened:** upgrades are verified against the installed version. If a pin in `pyproject.toml` blocks an upgrade, pydoctor says so and shows the constraint instead of printing "fixed".
|
|
79
150
|
- **One prompt per package:** vulnerable packages are offered once, with the advisory count, and upgraded to the lowest version that fixes every advisory.
|
|
@@ -6,6 +6,7 @@ Calculates the numerical project health score and verdict.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import math
|
|
9
10
|
from dataclasses import dataclass
|
|
10
11
|
|
|
11
12
|
from pydoctor.core.report import DiagnosisReport, Issue
|
|
@@ -18,20 +19,62 @@ class HealthMetrics:
|
|
|
18
19
|
message: str
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
# Each category can cost at most this many points. Within a category every
|
|
23
|
+
# extra issue still lowers the score, but by less (diminishing returns), so
|
|
24
|
+
# fixing issues in a project with dozens of them visibly moves the score.
|
|
25
|
+
CATEGORY_CAPS: dict[str, float] = {
|
|
26
|
+
"security": 60,
|
|
27
|
+
"ci": 50,
|
|
28
|
+
"dependencies": 40,
|
|
29
|
+
"environment": 30,
|
|
30
|
+
"outdated": 15,
|
|
31
|
+
"unused": 10,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# A project with any critical issue can't be rated Good or Excellent.
|
|
35
|
+
CRITICAL_CEILING = 69
|
|
36
|
+
|
|
37
|
+
# Issues that mean part of the scan didn't run.
|
|
38
|
+
INCOMPLETE_CODES = {"ENV_PACKAGES_UNAVAILABLE", "SEC_SCAN_INCOMPLETE"}
|
|
39
|
+
|
|
40
|
+
|
|
21
41
|
def calculate_health(report: DiagnosisReport) -> HealthMetrics:
|
|
22
42
|
"""
|
|
23
43
|
Calculate project health score (0-100) based on identified issues.
|
|
24
44
|
"""
|
|
25
45
|
score = _compute_score(report)
|
|
26
|
-
categories = list({i.category for i in report.issues})
|
|
27
|
-
|
|
46
|
+
categories = list({i.category for i in report.issues if i.code not in INCOMPLETE_CODES})
|
|
47
|
+
metrics = _get_verdict(score, categories)
|
|
48
|
+
if any(i.code in INCOMPLETE_CODES for i in report.issues):
|
|
49
|
+
metrics.verdict = "Incomplete"
|
|
50
|
+
metrics.message = (
|
|
51
|
+
"Some checks couldn't run, so this score only covers what was checked. "
|
|
52
|
+
"See the errors above."
|
|
53
|
+
)
|
|
54
|
+
return metrics
|
|
28
55
|
|
|
29
56
|
|
|
30
57
|
def _compute_score(report: DiagnosisReport) -> int:
|
|
31
|
-
|
|
58
|
+
raw: dict[str, float] = {}
|
|
59
|
+
# One upgrade usually fixes every advisory for a package, so a package
|
|
60
|
+
# costs the penalty of its worst advisory, not one penalty per advisory.
|
|
61
|
+
per_package: dict[str, int] = {}
|
|
32
62
|
for issue in report.issues:
|
|
33
|
-
|
|
34
|
-
|
|
63
|
+
penalty = _calculate_issue_penalty(issue)
|
|
64
|
+
if issue.code == "SEC_VULNERABILITY" and issue.package:
|
|
65
|
+
per_package[issue.package] = max(per_package.get(issue.package, 0), penalty)
|
|
66
|
+
continue
|
|
67
|
+
raw[issue.category] = raw.get(issue.category, 0) + penalty
|
|
68
|
+
if per_package:
|
|
69
|
+
raw["security"] = raw.get("security", 0) + sum(per_package.values())
|
|
70
|
+
lost = 0.0
|
|
71
|
+
for category, points in raw.items():
|
|
72
|
+
cap = CATEGORY_CAPS.get(category)
|
|
73
|
+
lost += cap * (1 - math.exp(-points / cap)) if cap else points
|
|
74
|
+
score = max(0, round(100 - lost))
|
|
75
|
+
if any(i.severity.lower() == "critical" for i in report.issues):
|
|
76
|
+
score = min(score, CRITICAL_CEILING)
|
|
77
|
+
return score
|
|
35
78
|
|
|
36
79
|
|
|
37
80
|
def _calculate_issue_penalty(issue: Issue) -> int:
|
|
@@ -79,7 +79,7 @@ class CacheManager:
|
|
|
79
79
|
|
|
80
80
|
return entry.get("value")
|
|
81
81
|
|
|
82
|
-
def set(self, key: str, value: Any) -> None:
|
|
82
|
+
def set(self, key: str, value: Any, save: bool = True) -> None:
|
|
83
83
|
"""
|
|
84
84
|
Store a value in the cache with the current timestamp.
|
|
85
85
|
|
|
@@ -87,8 +87,15 @@ class CacheManager:
|
|
|
87
87
|
----------
|
|
88
88
|
key: Cache key.
|
|
89
89
|
value: Any JSON-serialisable value.
|
|
90
|
+
save: Write to disk now. Pass False when setting many keys, then call
|
|
91
|
+
``save()`` once (writing the whole file per key is quadratic).
|
|
90
92
|
"""
|
|
91
93
|
self._data[key] = {"_ts": time.time(), "value": value}
|
|
94
|
+
if save:
|
|
95
|
+
self._save()
|
|
96
|
+
|
|
97
|
+
def save(self) -> None:
|
|
98
|
+
"""Persist the cache to disk."""
|
|
92
99
|
self._save()
|
|
93
100
|
|
|
94
101
|
def invalidate(self, key: str) -> None:
|