forceupdate 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.
Files changed (206) hide show
  1. forceupdate-0.1.0/.gitignore +12 -0
  2. forceupdate-0.1.0/LICENSE +21 -0
  3. forceupdate-0.1.0/PKG-INFO +79 -0
  4. forceupdate-0.1.0/README.md +43 -0
  5. forceupdate-0.1.0/pyproject.toml +80 -0
  6. forceupdate-0.1.0/src/forceupdate/__init__.py +9 -0
  7. forceupdate-0.1.0/src/forceupdate/__main__.py +5 -0
  8. forceupdate-0.1.0/src/forceupdate/advisories/__init__.py +30 -0
  9. forceupdate-0.1.0/src/forceupdate/advisories/cvss.py +95 -0
  10. forceupdate-0.1.0/src/forceupdate/advisories/osv.py +584 -0
  11. forceupdate-0.1.0/src/forceupdate/checks/__init__.py +49 -0
  12. forceupdate-0.1.0/src/forceupdate/checks/base.py +98 -0
  13. forceupdate-0.1.0/src/forceupdate/checks/deprecated.py +115 -0
  14. forceupdate-0.1.0/src/forceupdate/checks/outdated.py +183 -0
  15. forceupdate-0.1.0/src/forceupdate/checks/priority.py +82 -0
  16. forceupdate-0.1.0/src/forceupdate/checks/vulns.py +201 -0
  17. forceupdate-0.1.0/src/forceupdate/cli.py +309 -0
  18. forceupdate-0.1.0/src/forceupdate/detect/__init__.py +5 -0
  19. forceupdate-0.1.0/src/forceupdate/detect/languages.py +266 -0
  20. forceupdate-0.1.0/src/forceupdate/detect/manifests.py +478 -0
  21. forceupdate-0.1.0/src/forceupdate/engine.py +313 -0
  22. forceupdate-0.1.0/src/forceupdate/models.py +510 -0
  23. forceupdate-0.1.0/src/forceupdate/net/__init__.py +20 -0
  24. forceupdate-0.1.0/src/forceupdate/net/cache.py +217 -0
  25. forceupdate-0.1.0/src/forceupdate/net/http.py +270 -0
  26. forceupdate-0.1.0/src/forceupdate/parsers/__init__.py +43 -0
  27. forceupdate-0.1.0/src/forceupdate/parsers/base.py +22 -0
  28. forceupdate-0.1.0/src/forceupdate/parsers/json_spans.py +123 -0
  29. forceupdate-0.1.0/src/forceupdate/parsers/lockfiles.py +231 -0
  30. forceupdate-0.1.0/src/forceupdate/parsers/npm.py +1257 -0
  31. forceupdate-0.1.0/src/forceupdate/parsers/python.py +1421 -0
  32. forceupdate-0.1.0/src/forceupdate/parsers/toml_spans.py +297 -0
  33. forceupdate-0.1.0/src/forceupdate/registries/__init__.py +52 -0
  34. forceupdate-0.1.0/src/forceupdate/registries/base.py +294 -0
  35. forceupdate-0.1.0/src/forceupdate/registries/npm.py +153 -0
  36. forceupdate-0.1.0/src/forceupdate/registries/pypi.py +242 -0
  37. forceupdate-0.1.0/src/forceupdate/report/__init__.py +359 -0
  38. forceupdate-0.1.0/src/forceupdate/report/json.py +34 -0
  39. forceupdate-0.1.0/src/forceupdate/report/markdown.py +169 -0
  40. forceupdate-0.1.0/src/forceupdate/report/terminal.py +376 -0
  41. forceupdate-0.1.0/src/forceupdate/sources/__init__.py +1 -0
  42. forceupdate-0.1.0/src/forceupdate/sources/base.py +45 -0
  43. forceupdate-0.1.0/src/forceupdate/sources/local.py +183 -0
  44. forceupdate-0.1.0/src/forceupdate/update/__init__.py +8 -0
  45. forceupdate-0.1.0/src/forceupdate/update/constraints.py +317 -0
  46. forceupdate-0.1.0/src/forceupdate/update/planner.py +531 -0
  47. forceupdate-0.1.0/src/forceupdate/versions/__init__.py +40 -0
  48. forceupdate-0.1.0/src/forceupdate/versions/base.py +229 -0
  49. forceupdate-0.1.0/src/forceupdate/versions/npm.py +432 -0
  50. forceupdate-0.1.0/src/forceupdate/versions/pypi.py +220 -0
  51. forceupdate-0.1.0/tests/conftest.py +187 -0
  52. forceupdate-0.1.0/tests/fixtures/npm/@hapi__joi.json +1 -0
  53. forceupdate-0.1.0/tests/fixtures/npm/colors.json +1 -0
  54. forceupdate-0.1.0/tests/fixtures/npm/crossenv.json +1 -0
  55. forceupdate-0.1.0/tests/fixtures/npm/left-pad.json +1 -0
  56. forceupdate-0.1.0/tests/fixtures/npm/lodash.json +1 -0
  57. forceupdate-0.1.0/tests/fixtures/osv/queries.json +321 -0
  58. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-29mw-wpgm-hmr9.json +1 -0
  59. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-29vq-49wr-vm6x.json +1 -0
  60. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-2g68-c3qc-8985.json +1 -0
  61. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-35jh-r3h4-6jhm.json +1 -0
  62. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-462w-v97r-4m45.json +1 -0
  63. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-562c-5r94-xh97.json +1 -0
  64. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-5wv5-4vpf-pj6m.json +1 -0
  65. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-652x-xj99-gmcc.json +1 -0
  66. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-68rp-wp8r-4726.json +1 -0
  67. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-87hc-h4r5-73f7.json +1 -0
  68. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-9hjg-9r4m-mvj7.json +1 -0
  69. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-9wx4-h78v-vm56.json +1 -0
  70. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-9x64-5r7x-2q53.json +1 -0
  71. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-c2m4-w5hm-vqjw.json +1 -0
  72. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-cfj3-7x9c-4p3h.json +1 -0
  73. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-cpwx-vrp4-4pq7.json +1 -0
  74. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-f23m-r3pf-42rh.json +1 -0
  75. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-f9vj-2wh5-fj8j.json +1 -0
  76. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-g3rq-g295-4j3m.json +1 -0
  77. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-g6x2-hccm-hh4m.json +1 -0
  78. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-gc5v-m9x4-r6x2.json +1 -0
  79. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-gq9m-qvpx-68hc.json +1 -0
  80. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-h5c8-rqwp-cp95.json +1 -0
  81. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-h75v-3vvj-5mfj.json +1 -0
  82. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-hgf8-39gv-g3f2.json +1 -0
  83. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-j544-7q9p-6xp8.json +1 -0
  84. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-m2qf-hxjv-5gpq.json +1 -0
  85. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-mh6f-8j2x-4483.json +1 -0
  86. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-p6mc-m468-83gw.json +1 -0
  87. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-pjwm-rvh2-c87w.json +1 -0
  88. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-px8h-6qxv-m22q.json +1 -0
  89. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-q2x7-8rv6-6q7h.json +1 -0
  90. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-r5fr-rjxr-66jc.json +1 -0
  91. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-x84v-xcm2-53pg.json +1 -0
  92. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-xg9f-g7g7-2323.json +1 -0
  93. forceupdate-0.1.0/tests/fixtures/osv/vulns/GHSA-xxjr-mmjv-4gpg.json +1 -0
  94. forceupdate-0.1.0/tests/fixtures/osv/vulns/MAL-2025-20690.json +1 -0
  95. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2014-13.json +1 -0
  96. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2014-14.json +1 -0
  97. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2015-17.json +1 -0
  98. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2018-28.json +1 -0
  99. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2018-66.json +1 -0
  100. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2019-140.json +1 -0
  101. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2019-179.json +1 -0
  102. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2019-217.json +1 -0
  103. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2021-66.json +1 -0
  104. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2022-203.json +1 -0
  105. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2023-221.json +1 -0
  106. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2023-57.json +1 -0
  107. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2023-58.json +1 -0
  108. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2023-62.json +1 -0
  109. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-1065.json +1 -0
  110. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-1471.json +1 -0
  111. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-1473.json +1 -0
  112. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-1474.json +1 -0
  113. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-1475.json +1 -0
  114. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-1872.json +1 -0
  115. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-1873.json +1 -0
  116. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-2043.json +1 -0
  117. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-2044.json +1 -0
  118. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-2045.json +1 -0
  119. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-2046.json +1 -0
  120. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-2151.json +1 -0
  121. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-2275.json +1 -0
  122. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-2320.json +1 -0
  123. forceupdate-0.1.0/tests/fixtures/osv/vulns/PYSEC-2026-4312.json +1 -0
  124. forceupdate-0.1.0/tests/fixtures/pypi/aioredis.json +1 -0
  125. forceupdate-0.1.0/tests/fixtures/pypi/flask.json +1 -0
  126. forceupdate-0.1.0/tests/fixtures/pypi/jinja2.json +1 -0
  127. forceupdate-0.1.0/tests/fixtures/pypi/requests.json +1 -0
  128. forceupdate-0.1.0/tests/fixtures/pypi/werkzeug.json +1 -0
  129. forceupdate-0.1.0/tests/fixtures/record.py +160 -0
  130. forceupdate-0.1.0/tests/projects/README.md +29 -0
  131. forceupdate-0.1.0/tests/projects/npm/package-lock.json +264 -0
  132. forceupdate-0.1.0/tests/projects/npm/package.json +25 -0
  133. forceupdate-0.1.0/tests/projects/npm/vendor/local-lib/package.json +7 -0
  134. forceupdate-0.1.0/tests/projects/npm-workspace/package-lock.json +165 -0
  135. forceupdate-0.1.0/tests/projects/npm-workspace/package.json +13 -0
  136. forceupdate-0.1.0/tests/projects/npm-workspace/packages/a/package.json +8 -0
  137. forceupdate-0.1.0/tests/projects/npm-workspace/packages/b/package.json +8 -0
  138. forceupdate-0.1.0/tests/projects/npm-workspace/tools/script/package.json +7 -0
  139. forceupdate-0.1.0/tests/projects/pip/constraints.txt +2 -0
  140. forceupdate-0.1.0/tests/projects/pip/requirements-dev.txt +4 -0
  141. forceupdate-0.1.0/tests/projects/pip/requirements.txt +11 -0
  142. forceupdate-0.1.0/tests/projects/pip-tools/requirements-dev.in +2 -0
  143. forceupdate-0.1.0/tests/projects/pip-tools/requirements-dev.txt +18 -0
  144. forceupdate-0.1.0/tests/projects/pip-tools/requirements.in +3 -0
  145. forceupdate-0.1.0/tests/projects/pip-tools/requirements.txt +33 -0
  146. forceupdate-0.1.0/tests/projects/pipenv/Pipfile +14 -0
  147. forceupdate-0.1.0/tests/projects/pipenv/Pipfile.lock +490 -0
  148. forceupdate-0.1.0/tests/projects/pnpm-catalog/package.json +8 -0
  149. forceupdate-0.1.0/tests/projects/pnpm-catalog/packages/app/package.json +8 -0
  150. forceupdate-0.1.0/tests/projects/pnpm-catalog/pnpm-lock.yaml +67 -0
  151. forceupdate-0.1.0/tests/projects/pnpm-catalog/pnpm-workspace.yaml +10 -0
  152. forceupdate-0.1.0/tests/projects/pnpm9/package.json +16 -0
  153. forceupdate-0.1.0/tests/projects/pnpm9/packages/app/package.json +9 -0
  154. forceupdate-0.1.0/tests/projects/pnpm9/packages/excluded/package.json +7 -0
  155. forceupdate-0.1.0/tests/projects/pnpm9/packages/lib/package.json +10 -0
  156. forceupdate-0.1.0/tests/projects/pnpm9/pnpm-lock.yaml +175 -0
  157. forceupdate-0.1.0/tests/projects/pnpm9/pnpm-workspace.yaml +3 -0
  158. forceupdate-0.1.0/tests/projects/poetry/poetry.lock +405 -0
  159. forceupdate-0.1.0/tests/projects/poetry/pyproject.toml +21 -0
  160. forceupdate-0.1.0/tests/projects/setuptools/pyproject.toml +3 -0
  161. forceupdate-0.1.0/tests/projects/setuptools/setup.cfg +16 -0
  162. forceupdate-0.1.0/tests/projects/setuptools/setup.py +3 -0
  163. forceupdate-0.1.0/tests/projects/uv/packages/legacy/pyproject.toml +5 -0
  164. forceupdate-0.1.0/tests/projects/uv/packages/lib/pyproject.toml +9 -0
  165. forceupdate-0.1.0/tests/projects/uv/pyproject.toml +25 -0
  166. forceupdate-0.1.0/tests/projects/uv/tools/script/pyproject.toml +5 -0
  167. forceupdate-0.1.0/tests/projects/uv/uv.lock +222 -0
  168. forceupdate-0.1.0/tests/projects/vulnerable/package-lock.json +21 -0
  169. forceupdate-0.1.0/tests/projects/vulnerable/package.json +8 -0
  170. forceupdate-0.1.0/tests/projects/vulnerable/requirements.in +2 -0
  171. forceupdate-0.1.0/tests/projects/vulnerable/requirements.txt +14 -0
  172. forceupdate-0.1.0/tests/projects/yarn1/package.json +25 -0
  173. forceupdate-0.1.0/tests/projects/yarn1/vendor/local-lib/package.json +7 -0
  174. forceupdate-0.1.0/tests/projects/yarn1/yarn.lock +106 -0
  175. forceupdate-0.1.0/tests/projects/yarn4/package.json +19 -0
  176. forceupdate-0.1.0/tests/projects/yarn4/packages/lib/package.json +11 -0
  177. forceupdate-0.1.0/tests/projects/yarn4/yarn.lock +353 -0
  178. forceupdate-0.1.0/tests/test_checks.py +542 -0
  179. forceupdate-0.1.0/tests/test_checks_vulns.py +246 -0
  180. forceupdate-0.1.0/tests/test_cli.py +299 -0
  181. forceupdate-0.1.0/tests/test_conftest.py +35 -0
  182. forceupdate-0.1.0/tests/test_cvss.py +77 -0
  183. forceupdate-0.1.0/tests/test_engine.py +157 -0
  184. forceupdate-0.1.0/tests/test_engine_check.py +491 -0
  185. forceupdate-0.1.0/tests/test_json_spans.py +90 -0
  186. forceupdate-0.1.0/tests/test_languages.py +161 -0
  187. forceupdate-0.1.0/tests/test_manifests.py +352 -0
  188. forceupdate-0.1.0/tests/test_models.py +169 -0
  189. forceupdate-0.1.0/tests/test_net_cache.py +187 -0
  190. forceupdate-0.1.0/tests/test_net_http.py +278 -0
  191. forceupdate-0.1.0/tests/test_npm_projects.py +282 -0
  192. forceupdate-0.1.0/tests/test_osv.py +497 -0
  193. forceupdate-0.1.0/tests/test_parsers_npm.py +1065 -0
  194. forceupdate-0.1.0/tests/test_parsers_python.py +1172 -0
  195. forceupdate-0.1.0/tests/test_planner.py +763 -0
  196. forceupdate-0.1.0/tests/test_python_projects.py +251 -0
  197. forceupdate-0.1.0/tests/test_registries.py +229 -0
  198. forceupdate-0.1.0/tests/test_registries_npm.py +269 -0
  199. forceupdate-0.1.0/tests/test_registries_pypi.py +378 -0
  200. forceupdate-0.1.0/tests/test_report.py +384 -0
  201. forceupdate-0.1.0/tests/test_sources.py +169 -0
  202. forceupdate-0.1.0/tests/test_toml_spans.py +143 -0
  203. forceupdate-0.1.0/tests/test_update_constraints.py +184 -0
  204. forceupdate-0.1.0/tests/test_versions.py +213 -0
  205. forceupdate-0.1.0/tests/test_versions_npm.py +483 -0
  206. forceupdate-0.1.0/tests/test_versions_pypi.py +245 -0
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ .env
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .coverage
9
+ htmlcov/
10
+ dist/
11
+ build/
12
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pauzzle
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.
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.5
2
+ Name: forceupdate
3
+ Version: 0.1.0
4
+ Summary: Trouve les dépendances à mettre à jour (versions, failles, fins de vie) et fait les mises à jour : en local, ou par pull request sur GitHub, GitLab et Bitbucket.
5
+ Project-URL: Homepage, https://github.com/Pauzzle-ozz/forceupdate
6
+ Project-URL: Repository, https://github.com/Pauzzle-ozz/forceupdate
7
+ Project-URL: Issues, https://github.com/Pauzzle-ozz/forceupdate/issues
8
+ Author: Pauzzle
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: dependencies,end-of-life,osv,outdated,pull-request,update,vulnerabilities
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Environment :: Web Environment
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Natural Language :: French
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Security
25
+ Classifier: Topic :: Software Development
26
+ Classifier: Topic :: System :: Software Distribution
27
+ Classifier: Topic :: Utilities
28
+ Requires-Python: >=3.11
29
+ Requires-Dist: httpx>=0.28.1
30
+ Requires-Dist: packaging>=26.3
31
+ Requires-Dist: platformdirs>=4.13.6
32
+ Requires-Dist: pyyaml>=6.0.3
33
+ Requires-Dist: rich>=15.0.0
34
+ Requires-Dist: typer>=0.27.3
35
+ Description-Content-Type: text/markdown
36
+
37
+ # forceupdate
38
+
39
+ On donne du code à l'outil : un dossier, un fichier, ou tous ses dépôts GitHub,
40
+ GitLab et Bitbucket. Dans une interface web qui tourne sur son ordinateur, il
41
+ montre les langages et les dépendances, et dit ce qui doit être mis à jour :
42
+ versions en retard, failles, fins de vie, paquets dépréciés. On coche les mises à
43
+ jour voulues, puis il les fait : en local il réécrit les fichiers, sur une forge il
44
+ ouvre une pull request par dépôt.
45
+
46
+ En construction. L'outil sera publié gratuitement sur PyPI
47
+ (`uv tool install forceupdate`), sous licence MIT.
48
+
49
+ Déjà là, en ligne de commande, pour Python et npm : `forceupdate scan <dossier>`
50
+ dit quelles dépendances ont une faille connue (d'après [OSV](https://osv.dev),
51
+ indirectes comprises, avec la version qui la corrige), une version plus récente,
52
+ une version retirée, ou sont dépréciées.
53
+
54
+ ```bash
55
+ forceupdate scan . # rapport dans le terminal
56
+ forceupdate scan . --format json -o rapport.json # ou md ; toujours en UTF-8
57
+ forceupdate scan . --fail-on vuln # code 1 si une faille est connue
58
+ forceupdate scan . --fail-on major # code 1 si une majeure (ou pire) attend
59
+ forceupdate scan . --offline # seulement le cache, sans réseau
60
+ ```
61
+
62
+ Autres options : `--no-dev`, `--transitive`, `--no-cache` (`forceupdate scan --help`).
63
+
64
+ `forceupdate update <dossier>` montre, pour l'instant sans rien écrire, les mises à
65
+ jour qu'il ferait : vers quelle version, quelle contrainte réécrire (dans son style :
66
+ `^4.17.0` devient `^4.21.2`), quel lockfile régénérer. Correctifs, mineures et
67
+ corrections de failles sont choisis ; les majeures sont proposées à part.
68
+
69
+ ```bash
70
+ forceupdate update . # correctifs et mineures (--level minor)
71
+ forceupdate update . --level major # majeures comprises
72
+ forceupdate update . --security-only # seulement les corrections de failles
73
+ forceupdate update . --exclude "eslint*" --ecosystem npm
74
+ ```
75
+
76
+ - [docs/BRIEF.md](docs/BRIEF.md) : l'idée, les décisions prises, les pièges
77
+ - [docs/JSON_FORMAT.md](docs/JSON_FORMAT.md) : le format du rapport JSON
78
+ - [PLAN.md](PLAN.md) : le plan étape par étape, et l'avancement
79
+ - [reference/README.md](reference/README.md) : le code déjà disponible, repris d'Argos
@@ -0,0 +1,43 @@
1
+ # forceupdate
2
+
3
+ On donne du code à l'outil : un dossier, un fichier, ou tous ses dépôts GitHub,
4
+ GitLab et Bitbucket. Dans une interface web qui tourne sur son ordinateur, il
5
+ montre les langages et les dépendances, et dit ce qui doit être mis à jour :
6
+ versions en retard, failles, fins de vie, paquets dépréciés. On coche les mises à
7
+ jour voulues, puis il les fait : en local il réécrit les fichiers, sur une forge il
8
+ ouvre une pull request par dépôt.
9
+
10
+ En construction. L'outil sera publié gratuitement sur PyPI
11
+ (`uv tool install forceupdate`), sous licence MIT.
12
+
13
+ Déjà là, en ligne de commande, pour Python et npm : `forceupdate scan <dossier>`
14
+ dit quelles dépendances ont une faille connue (d'après [OSV](https://osv.dev),
15
+ indirectes comprises, avec la version qui la corrige), une version plus récente,
16
+ une version retirée, ou sont dépréciées.
17
+
18
+ ```bash
19
+ forceupdate scan . # rapport dans le terminal
20
+ forceupdate scan . --format json -o rapport.json # ou md ; toujours en UTF-8
21
+ forceupdate scan . --fail-on vuln # code 1 si une faille est connue
22
+ forceupdate scan . --fail-on major # code 1 si une majeure (ou pire) attend
23
+ forceupdate scan . --offline # seulement le cache, sans réseau
24
+ ```
25
+
26
+ Autres options : `--no-dev`, `--transitive`, `--no-cache` (`forceupdate scan --help`).
27
+
28
+ `forceupdate update <dossier>` montre, pour l'instant sans rien écrire, les mises à
29
+ jour qu'il ferait : vers quelle version, quelle contrainte réécrire (dans son style :
30
+ `^4.17.0` devient `^4.21.2`), quel lockfile régénérer. Correctifs, mineures et
31
+ corrections de failles sont choisis ; les majeures sont proposées à part.
32
+
33
+ ```bash
34
+ forceupdate update . # correctifs et mineures (--level minor)
35
+ forceupdate update . --level major # majeures comprises
36
+ forceupdate update . --security-only # seulement les corrections de failles
37
+ forceupdate update . --exclude "eslint*" --ecosystem npm
38
+ ```
39
+
40
+ - [docs/BRIEF.md](docs/BRIEF.md) : l'idée, les décisions prises, les pièges
41
+ - [docs/JSON_FORMAT.md](docs/JSON_FORMAT.md) : le format du rapport JSON
42
+ - [PLAN.md](PLAN.md) : le plan étape par étape, et l'avancement
43
+ - [reference/README.md](reference/README.md) : le code déjà disponible, repris d'Argos
@@ -0,0 +1,80 @@
1
+ [project]
2
+ name = "forceupdate"
3
+ version = "0.1.0"
4
+ description = "Trouve les dépendances à mettre à jour (versions, failles, fins de vie) et fait les mises à jour : en local, ou par pull request sur GitHub, GitLab et Bitbucket."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.11"
9
+ authors = [{ name = "Pauzzle" }]
10
+ keywords = [
11
+ "dependencies",
12
+ "update",
13
+ "outdated",
14
+ "vulnerabilities",
15
+ "osv",
16
+ "end-of-life",
17
+ "pull-request",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 2 - Pre-Alpha",
21
+ "Environment :: Console",
22
+ "Environment :: Web Environment",
23
+ "Intended Audience :: Developers",
24
+ "Natural Language :: French",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3 :: Only",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Programming Language :: Python :: 3.13",
31
+ "Programming Language :: Python :: 3.14",
32
+ "Topic :: Security",
33
+ "Topic :: Software Development",
34
+ "Topic :: System :: Software Distribution",
35
+ "Topic :: Utilities",
36
+ ]
37
+ dependencies = [
38
+ "httpx>=0.28.1",
39
+ "packaging>=26.3",
40
+ "platformdirs>=4.13.6",
41
+ "pyyaml>=6.0.3",
42
+ "rich>=15.0.0",
43
+ "typer>=0.27.3",
44
+ ]
45
+
46
+ [project.urls]
47
+ Homepage = "https://github.com/Pauzzle-ozz/forceupdate"
48
+ Repository = "https://github.com/Pauzzle-ozz/forceupdate"
49
+ Issues = "https://github.com/Pauzzle-ozz/forceupdate/issues"
50
+
51
+ [project.scripts]
52
+ forceupdate = "forceupdate.cli:app"
53
+
54
+ [dependency-groups]
55
+ dev = [
56
+ "pytest>=9.1.1",
57
+ "respx>=0.23.1",
58
+ "ruff>=0.17.3",
59
+ ]
60
+
61
+ [build-system]
62
+ requires = ["hatchling>=1.27"]
63
+ build-backend = "hatchling.build"
64
+
65
+ [tool.hatch.build.targets.wheel]
66
+ packages = ["src/forceupdate"]
67
+
68
+ [tool.hatch.build.targets.sdist]
69
+ include = ["/src", "/tests"]
70
+
71
+ [tool.ruff]
72
+ line-length = 100
73
+ extend-exclude = ["reference"]
74
+
75
+ [tool.ruff.lint]
76
+ select = ["E", "W", "F", "I", "UP", "B", "SIM", "RUF"]
77
+
78
+ [tool.pytest.ini_options]
79
+ testpaths = ["tests"]
80
+ addopts = ["-ra", "-p", "pytester"]
@@ -0,0 +1,9 @@
1
+ """forceupdate : trouve les dépendances à mettre à jour, et fait les mises à jour.
2
+
3
+ La version vient des métadonnées du paquet installé : elle n'est écrite qu'à un
4
+ seul endroit, dans ``pyproject.toml``.
5
+ """
6
+
7
+ from importlib.metadata import version
8
+
9
+ __version__ = version("forceupdate")
@@ -0,0 +1,5 @@
1
+ """``python -m forceupdate`` : la même chose que la commande ``forceupdate``."""
2
+
3
+ from forceupdate.cli import app
4
+
5
+ app(prog_name="forceupdate")
@@ -0,0 +1,30 @@
1
+ """Les avis de sécurité : les failles connues d'une version de paquet (type 2 du brief).
2
+
3
+ - ``osv.py`` : le client d'OSV (``Osv``), et ``Advisory``, un avis réduit à ce qui sert,
4
+ avec la version qui corrige la faille (``Advisory.fixed_in``) ;
5
+ - ``cvss.py`` : le score CVSS 3 d'un vecteur, et la gravité qui va avec.
6
+
7
+ Les constats qu'on en tire sont dans ``checks/vulns.py``, qui ne parle pas au réseau.
8
+ """
9
+
10
+ from forceupdate.advisories.osv import (
11
+ Advisory,
12
+ Affected,
13
+ AffectedRange,
14
+ Osv,
15
+ OsvError,
16
+ Query,
17
+ Vulnerabilities,
18
+ parse_vuln,
19
+ )
20
+
21
+ __all__ = [
22
+ "Advisory",
23
+ "Affected",
24
+ "AffectedRange",
25
+ "Osv",
26
+ "OsvError",
27
+ "Query",
28
+ "Vulnerabilities",
29
+ "parse_vuln",
30
+ ]
@@ -0,0 +1,95 @@
1
+ """Le score CVSS d'une faille, calculé depuis son vecteur, sans dépendance.
2
+
3
+ OSV donne la gravité d'une faille sous forme de vecteur (``CVSS:3.1/AV:N/AC:L/…``), pas de
4
+ score. Le calcul suit la spécification de FIRST (CVSS 3.1, section 7.1), y compris son
5
+ arrondi (annexe A), qui évite les erreurs de calcul en virgule flottante : un arrondi naïf
6
+ (``ceil(x * 10) / 10``) donne parfois un dixième de trop. Les vecteurs CVSS 3.0 ont les mêmes
7
+ formules ; seul leur arrondi diffère, et ne change le score que dans ces cas d'erreur.
8
+
9
+ CVSS 4.0 n'est pas calculé (il faut les tables de la spécification) : OSV le donne rarement
10
+ seul, et GitHub ajoute alors sa propre gravité, que lit ``advisories.osv``.
11
+
12
+ Repris de ``reference/argos_sca_parsers.py`` (``_cvss_base_score_from_vector``,
13
+ ``_cvss_to_severity``), puis adapté : ``Severity`` de ``models`` (``ARGOS (1)``), métriques
14
+ lues dans n'importe quel ordre, arrondi de la spécification, ``None`` pour un vecteur
15
+ illisible au lieu de 0.
16
+ """
17
+
18
+ import re
19
+
20
+ from forceupdate.models import Severity
21
+
22
+ _PREFIX = re.compile(r"^CVSS:3\.[01]/")
23
+ _LEADING_SCORE = re.compile(r"^(\d{1,2}\.\d)/")
24
+ """Un score écrit devant le vecteur (``9.8/CVSS:3.1/…``), comme certaines bases le font."""
25
+
26
+ _WEIGHTS: dict[str, dict[str, float]] = {
27
+ "AV": {"N": 0.85, "A": 0.62, "L": 0.55, "P": 0.2},
28
+ "AC": {"L": 0.77, "H": 0.44},
29
+ "UI": {"N": 0.85, "R": 0.62},
30
+ "C": {"H": 0.56, "L": 0.22, "N": 0.0},
31
+ "I": {"H": 0.56, "L": 0.22, "N": 0.0},
32
+ "A": {"H": 0.56, "L": 0.22, "N": 0.0},
33
+ }
34
+ _PRIVILEGES = {"N": 0.85, "L": 0.62, "H": 0.27}
35
+ _PRIVILEGES_CHANGED = {"N": 0.85, "L": 0.68, "H": 0.5}
36
+ """PR pèse plus quand la faille sort de son périmètre (``S:C``)."""
37
+
38
+
39
+ def base_score(vector: str) -> float | None:
40
+ """Le score de base (0 à 10) d'un vecteur CVSS 3.0 ou 3.1 ; ``None`` s'il est illisible.
41
+
42
+ Les métriques temporelles et environnementales (``E:``, ``RL:``…) sont ignorées : le
43
+ score de base est celui que publient les avis.
44
+ """
45
+ vector = vector.strip()
46
+ leading = _LEADING_SCORE.match(vector)
47
+ if leading:
48
+ return float(leading.group(1))
49
+ if not _PREFIX.match(vector):
50
+ return None
51
+ metrics: dict[str, str] = {}
52
+ for part in vector.split("/")[1:]:
53
+ name, _, value = part.partition(":")
54
+ metrics.setdefault(name, value)
55
+ try:
56
+ scope_changed = {"U": False, "C": True}[metrics["S"]]
57
+ privileges = (_PRIVILEGES_CHANGED if scope_changed else _PRIVILEGES)[metrics["PR"]]
58
+ weights = {name: _WEIGHTS[name][metrics[name]] for name in _WEIGHTS}
59
+ except KeyError:
60
+ return None
61
+ iss = 1 - (1 - weights["C"]) * (1 - weights["I"]) * (1 - weights["A"])
62
+ impact = (7.52 * (iss - 0.029) - 3.25 * (iss - 0.02) ** 15) if scope_changed else 6.42 * iss
63
+ if impact <= 0:
64
+ return 0.0
65
+ exploitability = 8.22 * weights["AV"] * weights["AC"] * privileges * weights["UI"]
66
+ total = impact + exploitability
67
+ if scope_changed:
68
+ total *= 1.08
69
+ return _roundup(min(total, 10.0))
70
+
71
+
72
+ def _roundup(value: float) -> float:
73
+ """Le plus petit nombre à une décimale qui est au moins ``value`` (CVSS 3.1, annexe A).
74
+
75
+ Calculé en entiers : ``ceil(4.000000000000001 * 10) / 10`` donnerait 4.1 au lieu de 4.0.
76
+ """
77
+ integer = round(value * 100_000)
78
+ if integer % 10_000 == 0:
79
+ return integer / 100_000
80
+ return (integer // 10_000 + 1) / 10
81
+
82
+
83
+ def severity(score: float) -> Severity:
84
+ """La gravité d'un score CVSS, selon l'échelle de la spécification : critique à partir de
85
+ 9, élevée à partir de 7, moyenne à partir de 4, faible au-dessus de 0. Un score nul
86
+ (aucun impact) donne ``INFO``."""
87
+ if score >= 9.0:
88
+ return Severity.CRITICAL
89
+ if score >= 7.0:
90
+ return Severity.HIGH
91
+ if score >= 4.0:
92
+ return Severity.MEDIUM
93
+ if score > 0.0:
94
+ return Severity.LOW
95
+ return Severity.INFO