This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git


The following commit(s) were added to refs/heads/main by this push:
     new dc8ca86  Add a validator using the official CycloneDX package
dc8ca86 is described below

commit dc8ca863cf13304fb722b18b93107011f4fb15ed
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Sep 1 19:20:58 2025 +0100

    Add a validator using the official CycloneDX package
---
 atr/sbomtool.py |  39 +++++++-
 pyproject.toml  |   1 +
 uv.lock         | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 334 insertions(+), 3 deletions(-)

diff --git a/atr/sbomtool.py b/atr/sbomtool.py
index 88f6389..ee6efc8 100644
--- a/atr/sbomtool.py
+++ b/atr/sbomtool.py
@@ -28,11 +28,17 @@ import tempfile
 import urllib.error
 import urllib.parse
 import urllib.request
-from typing import Annotated, Any, Final, Literal
+from typing import TYPE_CHECKING, Annotated, Any, Final, Literal
 
+import cyclonedx.exception
+import cyclonedx.schema
+import cyclonedx.validation.json
 import pydantic
 import yyjson
 
+if TYPE_CHECKING:
+    from collections.abc import Iterable
+
 # TODO: Simple cache to avoid rate limiting, not thread safe
 CACHE_PATH = pathlib.Path("/tmp/sbomtool-cache.json")
 KNOWN_PURL_PREFIXES: Final[dict[str, tuple[str, str]]] = {
@@ -315,6 +321,7 @@ OutdatedAdapter = pydantic.TypeAdapter(Outdated)
 class Bundle:
     doc: yyjson.Document
     bom: Bom
+    text: str
 
 
 class SBOMQSSummary(Lax):
@@ -563,7 +570,15 @@ def main() -> None:
             else:
                 print(sbomqs_total_score(bundle.doc))
         case "validate":
-            print(bundle.doc.dumps())
+            errors = validate_cyclonedx_json(bundle)
+            if not errors:
+                print("valid")
+            else:
+                for i, e in enumerate(errors):
+                    print(e)
+                    if i > 10:
+                        print("...")
+                        break
         case "where":
             _warnings, errors = ntia_2021_conformance_issues(bundle.bom)
             for error in errors:
@@ -814,7 +829,7 @@ def patch_to_data(patch: Patch) -> list[dict[str, Any]]:
 
 def path_to_bundle(path: pathlib.Path) -> Bundle:
     text = path.read_text(encoding="utf-8")
-    return Bundle(doc=yyjson.Document(text), bom=Bom.model_validate_json(text))
+    return Bundle(doc=yyjson.Document(text), 
bom=Bom.model_validate_json(text), text=text)
 
 
 def sbomqs_total_score(value: pathlib.Path | str | yyjson.Document) -> float:
@@ -841,5 +856,23 @@ def sbomqs_total_score(value: pathlib.Path | str | 
yyjson.Document) -> float:
     return report.summary.total_score
 
 
+def validate_cyclonedx_json(bundle: Bundle) -> 
Iterable[cyclonedx.validation.json.JsonValidationError] | None:
+    json_sv = get_pointer(bundle.doc, "/specVersion")
+    sv = cyclonedx.schema.SchemaVersion.V1_6
+    if isinstance(json_sv, str):
+        sv = cyclonedx.schema.SchemaVersion.from_version(json_sv)
+    try:
+        validator = cyclonedx.validation.json.JsonStrictValidator(sv)
+        errors = validator.validate_str(bundle.text, all_errors=True)
+    except cyclonedx.exception.MissingOptionalDependencyException:
+        # Placeholder, just in case we want to handle this somehow
+        raise
+    if isinstance(errors, cyclonedx.validation.json.JsonValidationError):
+        # The VSC type checker doesn't think this can happen
+        # But pyright does
+        return [errors]
+    return errors
+
+
 if __name__ == "__main__":
     main()
diff --git a/pyproject.toml b/pyproject.toml
index 01c01ff..f8ff2d8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,6 +21,7 @@ dependencies = [
   "asyncssh>=2.20.0,<3.0.0",
   "blockbuster>=1.5.23,<2.0.0",
   "cryptography~=44.0",
+  "cyclonedx-python-lib[json-validation]>=11.0.0",
   "dkimpy @ git+https://github.com/sbp/dkimpy.git@main";,
   "dnspython>=2.7.0,<3.0.0",
   "dunamai>=1.23.0",
diff --git a/uv.lock b/uv.lock
index 9f8d07a..8b22937 100644
--- a/uv.lock
+++ b/uv.lock
@@ -141,6 +141,19 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl";,
 hash = 
"sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size 
= 107213, upload-time = "2025-08-04T08:54:24.882Z" },
 ]
 
+[[package]]
+name = "arrow"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "python-dateutil" },
+    { name = "types-python-dateutil" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz";,
 hash = 
"sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85", size 
= 131960, upload-time = "2023-09-30T22:11:18.25Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl";,
 hash = 
"sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size 
= 66419, upload-time = "2023-09-30T22:11:16.072Z" },
+]
+
 [[package]]
 name = "asfpy"
 version = "0.55"
@@ -217,6 +230,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/0b/01/dccc277c014f171f61a6047bb22c684e16c7f2db6bb5c8cce1feaf41ec55/blockbuster-1.5.25-py3-none-any.whl";,
 hash = 
"sha256:cb06229762273e0f5f3accdaed3d2c5a3b61b055e38843de202311ede21bb0f5", size 
= 13196, upload-time = "2025-07-14T16:00:19.396Z" },
 ]
 
+[[package]]
+name = "boolean-py"
+version = "5.0"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/c4/cf/85379f13b76f3a69bca86b60237978af17d6aa0bc5998978c3b8cf05abb2/boolean_py-5.0.tar.gz";,
 hash = 
"sha256:60cbc4bad079753721d32649545505362c754e121570ada4658b852a3a318d95", size 
= 37047, upload-time = "2025-04-03T10:39:49.734Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/e5/ca/78d423b324b8d77900030fa59c4aa9054261ef0925631cd2501dd015b7b7/boolean_py-5.0-py3-none-any.whl";,
 hash = 
"sha256:ef28a70bd43115208441b53a045d1549e2f0ec6e3d08a9d142cbc41c1938e8d9", size 
= 26577, upload-time = "2025-04-03T10:39:48.449Z" },
+]
+
 [[package]]
 name = "certifi"
 version = "2025.8.3"
@@ -347,6 +369,27 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/63/51/ef6c5628e46092f0a54c7cee69acc827adc6b6aab57b55d344fefbdf28f1/cssbeautifier-1.15.4-py3-none-any.whl";,
 hash = 
"sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98", size 
= 123667, upload-time = "2025-02-27T17:53:43.594Z" },
 ]
 
+[[package]]
+name = "cyclonedx-python-lib"
+version = "11.0.0"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "license-expression" },
+    { name = "packageurl-python" },
+    { name = "py-serializable" },
+    { name = "sortedcontainers" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/86/62/c279a8470746be835c0860152737b61b9b7237df59b6c5c009a8d2ae6e40/cyclonedx_python_lib-11.0.0.tar.gz";,
 hash = 
"sha256:d51d456c01cfc42378a2282ef35858fc809162ef13d8cce2af6c4d16f788217e", size 
= 1041433, upload-time = "2025-07-21T10:53:49.353Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/41/59/b9887a55047a11eda49fa4f643ada302b8d757520810972ed5544f6a251b/cyclonedx_python_lib-11.0.0-py3-none-any.whl";,
 hash = 
"sha256:4bcaebf381daf63c8a9badc7a7132704ed461b6822c4cc513beeb28ff95d7858", size 
= 381983, upload-time = "2025-07-21T10:53:47.443Z" },
+]
+
+[package.optional-dependencies]
+json-validation = [
+    { name = "jsonschema", extra = ["format-nongpl"] },
+    { name = "referencing" },
+]
+
 [[package]]
 name = "decouple-types"
 version = "1.0.2"
@@ -356,6 +399,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/a5/7e/63f0d7e0c77b552184446befe454eb8841933eeebbdcbc39de6334133259/decouple_types-1.0.2-py3-none-any.whl";,
 hash = 
"sha256:70ef264b67c57057bbf54db6e6df7c4c477a6370174e8c79548f5acde6c14fdb", size 
= 3067, upload-time = "2025-03-07T09:08:45.376Z" },
 ]
 
+[[package]]
+name = "defusedxml"
+version = "0.7.1"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz";,
 hash = 
"sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size 
= 75520, upload-time = "2021-03-08T10:59:26.269Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl";,
 hash = 
"sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size 
= 25604, upload-time = "2021-03-08T10:59:24.45Z" },
+]
+
 [[package]]
 name = "distlib"
 version = "0.4.0"
@@ -487,6 +539,15 @@ version = "0.1.4"
 source = { registry = "https://pypi.org/simple"; }
 sdist = { url = 
"https://files.pythonhosted.org/packages/e6/79/d4f20e91327c98096d605646bdc6a5ffedae820f38d378d3515c42ec5e60/forbiddenfruit-0.1.4.tar.gz";,
 hash = 
"sha256:e3f7e66561a29ae129aac139a85d610dbf3dd896128187ed5454b6421f624253", size 
= 43756, upload-time = "2021-01-16T21:03:35.401Z" }
 
+[[package]]
+name = "fqdn"
+version = "1.5.1"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz";,
 hash = 
"sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size 
= 6015, upload-time = "2021-03-11T07:16:29.08Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl";,
 hash = 
"sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size 
= 9121, upload-time = "2021-03-11T07:16:28.351Z" },
+]
+
 [[package]]
 name = "frozenlist"
 version = "1.7.0"
@@ -647,6 +708,18 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl";,
 hash = 
"sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size 
= 6050, upload-time = "2025-03-19T20:10:01.071Z" },
 ]
 
+[[package]]
+name = "isoduration"
+version = "20.11.0"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "arrow" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz";,
 hash = 
"sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size 
= 11649, upload-time = "2020-11-01T11:00:00.312Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl";,
 hash = 
"sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size 
= 11321, upload-time = "2020-11-01T10:59:58.02Z" },
+]
+
 [[package]]
 name = "itsdangerous"
 version = "2.2.0"
@@ -690,6 +763,64 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/41/9f/3500910d5a98549e3098807493851eeef2b89cdd3032227558a104dfe926/json5-0.12.0-py3-none-any.whl";,
 hash = 
"sha256:6d37aa6c08b0609f16e1ec5ff94697e2cbbfbad5ac112afa05794da9ab7810db", size 
= 36079, upload-time = "2025-04-03T16:33:11.927Z" },
 ]
 
+[[package]]
+name = "jsonpointer"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz";,
 hash = 
"sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size 
= 9114, upload-time = "2024-06-10T19:24:42.462Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl";,
 hash = 
"sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size 
= 7595, upload-time = "2024-06-10T19:24:40.698Z" },
+]
+
+[[package]]
+name = "jsonschema"
+version = "4.25.1"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "attrs" },
+    { name = "jsonschema-specifications" },
+    { name = "referencing" },
+    { name = "rpds-py" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz";,
 hash = 
"sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size 
= 357342, upload-time = "2025-08-18T17:03:50.038Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl";,
 hash = 
"sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size 
= 90040, upload-time = "2025-08-18T17:03:48.373Z" },
+]
+
+[package.optional-dependencies]
+format-nongpl = [
+    { name = "fqdn" },
+    { name = "idna" },
+    { name = "isoduration" },
+    { name = "jsonpointer" },
+    { name = "rfc3339-validator" },
+    { name = "rfc3986-validator" },
+    { name = "rfc3987-syntax" },
+    { name = "uri-template" },
+    { name = "webcolors" },
+]
+
+[[package]]
+name = "jsonschema-specifications"
+version = "2025.4.1"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "referencing" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz";,
 hash = 
"sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size 
= 15513, upload-time = "2025-04-23T12:34:07.418Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl";,
 hash = 
"sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size 
= 18437, upload-time = "2025-04-23T12:34:05.422Z" },
+]
+
+[[package]]
+name = "lark"
+version = "1.2.2"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/af/60/bc7622aefb2aee1c0b4ba23c1446d3e30225c8770b38d7aedbfb65ca9d5a/lark-1.2.2.tar.gz";,
 hash = 
"sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80", size 
= 252132, upload-time = "2024-08-13T19:49:00.652Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl";,
 hash = 
"sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c", size 
= 111036, upload-time = "2024-08-13T19:48:58.603Z" },
+]
+
 [[package]]
 name = "ldap3"
 version = "2.10.2rc2"
@@ -703,6 +834,18 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/4d/85/d48f33b0b41aecd6cab6d88f806f83b06f38a7334fefa1d4bd2101a6a36c/ldap3-2.10.2rc2-py3-none-any.whl";,
 hash = 
"sha256:6986d0484c926daca557b4ad1dc08d3a9a776cf949591d485b6346a83efc0e69", size 
= 488198, upload-time = "2025-04-19T19:25:55.492Z" },
 ]
 
+[[package]]
+name = "license-expression"
+version = "30.4.4"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "boolean-py" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/40/71/d89bb0e71b1415453980fd32315f2a037aad9f7f70f695c7cec7035feb13/license_expression-30.4.4.tar.gz";,
 hash = 
"sha256:73448f0aacd8d0808895bdc4b2c8e01a8d67646e4188f887375398c761f340fd", size 
= 186402, upload-time = "2025-07-22T11:13:32.17Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/af/40/791891d4c0c4dab4c5e187c17261cedc26285fd41541577f900470a45a4d/license_expression-30.4.4-py3-none-any.whl";,
 hash = 
"sha256:421788fdcadb41f049d2dc934ce666626265aeccefddd25e162a26f23bcbf8a4", size 
= 120615, upload-time = "2025-07-22T11:13:31.217Z" },
+]
+
 [[package]]
 name = "mako"
 version = "1.3.10"
@@ -824,6 +967,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl";,
 hash = 
"sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size 
= 22314, upload-time = "2024-06-04T18:44:08.352Z" },
 ]
 
+[[package]]
+name = "packageurl-python"
+version = "0.17.5"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/3a/f0/de0ac00a4484c0d87b71e3d9985518278d89797fa725e90abd3453bccb42/packageurl_python-0.17.5.tar.gz";,
 hash = 
"sha256:a7be3f3ba70d705f738ace9bf6124f31920245a49fa69d4b416da7037dd2de61", size 
= 43832, upload-time = "2025-08-06T14:08:20.235Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/be/78/9dbb7d2ef240d20caf6f79c0f66866737c9d0959601fd783ff635d1d019d/packageurl_python-0.17.5-py3-none-any.whl";,
 hash = 
"sha256:f0e55452ab37b5c192c443de1458e3f3b4d8ac27f747df6e8c48adeab081d321", size 
= 30544, upload-time = "2025-08-06T14:08:19.055Z" },
+]
+
 [[package]]
 name = "packaging"
 version = "25.0"
@@ -955,6 +1107,18 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl";,
 hash = 
"sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size 
= 12663, upload-time = "2025-06-09T22:56:04.484Z" },
 ]
 
+[[package]]
+name = "py-serializable"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "defusedxml" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/73/21/d250cfca8ff30c2e5a7447bc13861541126ce9bd4426cd5d0c9f08b5547d/py_serializable-2.1.0.tar.gz";,
 hash = 
"sha256:9d5db56154a867a9b897c0163b33a793c804c80cee984116d02d49e4578fc103", size 
= 52368, upload-time = "2025-07-21T09:56:48.07Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/9b/bf/7595e817906a29453ba4d99394e781b6fabe55d21f3c15d240f85dd06bb1/py_serializable-2.1.0-py3-none-any.whl";,
 hash = 
"sha256:b56d5d686b5a03ba4f4db5e769dc32336e142fc3bd4d68a8c25579ebb0a67304", size 
= 23045, upload-time = "2025-07-21T09:56:46.848Z" },
+]
+
 [[package]]
 name = "pyasn1"
 version = "0.6.1"
@@ -1159,6 +1323,18 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl";,
 hash = 
"sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size 
= 15157, upload-time = "2025-07-16T04:29:24.929Z" },
 ]
 
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "six" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz";,
 hash = 
"sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size 
= 342432, upload-time = "2024-03-01T18:36:20.211Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl";,
 hash = 
"sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size 
= 229892, upload-time = "2024-03-01T18:36:18.57Z" },
+]
+
 [[package]]
 name = "python-decouple"
 version = "3.8"
@@ -1260,6 +1436,19 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/ef/e3/d5f5c3e24dcf2314e9b5f2049bd07b272d1087a93a8ba324f10fbeb012fc/quart_wtforms-1.0.3-py3-none-any.whl";,
 hash = 
"sha256:7d08214c9606d40d2b35c78a8c1eecc2913b03b09cd256d233c82c29c8526fab", size 
= 12399, upload-time = "2024-10-05T20:02:39.167Z" },
 ]
 
+[[package]]
+name = "referencing"
+version = "0.36.2"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "attrs" },
+    { name = "rpds-py" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz";,
 hash = 
"sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size 
= 74744, upload-time = "2025-01-25T08:48:16.138Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl";,
 hash = 
"sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size 
= 26775, upload-time = "2025-01-25T08:48:14.241Z" },
+]
+
 [[package]]
 name = "regex"
 version = "2025.7.34"
@@ -1297,6 +1486,39 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl";,
 hash = 
"sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size 
= 64847, upload-time = "2025-06-09T16:43:05.728Z" },
 ]
 
+[[package]]
+name = "rfc3339-validator"
+version = "0.1.4"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "six" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz";,
 hash = 
"sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size 
= 5513, upload-time = "2021-05-12T16:37:54.178Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl";,
 hash = 
"sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size 
= 3490, upload-time = "2021-05-12T16:37:52.536Z" },
+]
+
+[[package]]
+name = "rfc3986-validator"
+version = "0.1.1"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz";,
 hash = 
"sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size 
= 6760, upload-time = "2019-10-28T16:00:19.144Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl";,
 hash = 
"sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size 
= 4242, upload-time = "2019-10-28T16:00:13.976Z" },
+]
+
+[[package]]
+name = "rfc3987-syntax"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "lark" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz";,
 hash = 
"sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size 
= 14239, upload-time = "2025-07-18T01:05:05.015Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl";,
 hash = 
"sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size 
= 8046, upload-time = "2025-07-18T01:05:03.843Z" },
+]
+
 [[package]]
 name = "rich"
 version = "14.0.0"
@@ -1310,6 +1532,43 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl";,
 hash = 
"sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size 
= 243229, upload-time = "2025-03-30T14:15:12.283Z" },
 ]
 
+[[package]]
+name = "rpds-py"
+version = "0.27.1"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz";,
 hash = 
"sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size 
= 27479, upload-time = "2025-08-27T12:16:36.024Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl";,
 hash = 
"sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size 
= 361741, upload-time = "2025-08-27T12:13:31.039Z" },
+    { url = 
"https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl";,
 hash = 
"sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size 
= 345574, upload-time = "2025-08-27T12:13:32.902Z" },
+    { url = 
"https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl";,
 hash = 
"sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size 
= 385051, upload-time = "2025-08-27T12:13:34.228Z" },
+    { url = 
"https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl";,
 hash = 
"sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size 
= 398395, upload-time = "2025-08-27T12:13:36.132Z" },
+    { url = 
"https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl";,
 hash = 
"sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size 
= 524334, upload-time = "2025-08-27T12:13:37.562Z" },
+    { url = 
"https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl";,
 hash = 
"sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size 
= 407691, upload-time = "2025-08-27T12:13:38.94Z" },
+    { url = 
"https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl";,
 hash = 
"sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size 
= 386868, upload-time = "2025-08-27T12:13:40.192Z" },
+    { url = 
"https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl";,
 hash = 
"sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size 
= 405469, upload-time = "2025-08-27T12:13:41.496Z" },
+    { url = 
"https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl";,
 hash = 
"sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size 
= 422125, upload-time = "2025-08-27T12:13:42.802Z" },
+    { url = 
"https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl";,
 hash = 
"sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size 
= 562341, upload-time = "2025-08-27T12:13:44.472Z" },
+    { url = 
"https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl";,
 hash = 
"sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size 
= 592511, upload-time = "2025-08-27T12:13:45.898Z" },
+    { url = 
"https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl";,
 hash = 
"sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size 
= 557736, upload-time = "2025-08-27T12:13:47.408Z" },
+    { url = 
"https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl";,
 hash = 
"sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size 
= 221462, upload-time = "2025-08-27T12:13:48.742Z" },
+    { url = 
"https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl";,
 hash = 
"sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size 
= 232034, upload-time = "2025-08-27T12:13:50.11Z" },
+    { url = 
"https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl";,
 hash = 
"sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size 
= 222392, upload-time = "2025-08-27T12:13:52.587Z" },
+    { url = 
"https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl";,
 hash = 
"sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size 
= 358355, upload-time = "2025-08-27T12:13:54.012Z" },
+    { url = 
"https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl";,
 hash = 
"sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size 
= 342138, upload-time = "2025-08-27T12:13:55.791Z" },
+    { url = 
"https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl";,
 hash = 
"sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size 
= 380247, upload-time = "2025-08-27T12:13:57.683Z" },
+    { url = 
"https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl";,
 hash = 
"sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size 
= 390699, upload-time = "2025-08-27T12:13:59.137Z" },
+    { url = 
"https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl";,
 hash = 
"sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size 
= 521852, upload-time = "2025-08-27T12:14:00.583Z" },
+    { url = 
"https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl";,
 hash = 
"sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size 
= 402582, upload-time = "2025-08-27T12:14:02.034Z" },
+    { url = 
"https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl";,
 hash = 
"sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size 
= 384126, upload-time = "2025-08-27T12:14:03.437Z" },
+    { url = 
"https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl";,
 hash = 
"sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size 
= 399486, upload-time = "2025-08-27T12:14:05.443Z" },
+    { url = 
"https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl";,
 hash = 
"sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size 
= 414832, upload-time = "2025-08-27T12:14:06.902Z" },
+    { url = 
"https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl";,
 hash = 
"sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size 
= 557249, upload-time = "2025-08-27T12:14:08.37Z" },
+    { url = 
"https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl";,
 hash = 
"sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size 
= 587356, upload-time = "2025-08-27T12:14:10.034Z" },
+    { url = 
"https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl";,
 hash = 
"sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size 
= 555300, upload-time = "2025-08-27T12:14:11.783Z" },
+    { url = 
"https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl";,
 hash = 
"sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size 
= 216714, upload-time = "2025-08-27T12:14:13.629Z" },
+    { url = 
"https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl";,
 hash = 
"sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size 
= 228943, upload-time = "2025-08-27T12:14:14.937Z" },
+]
+
 [[package]]
 name = "ruff"
 version = "0.12.8"
@@ -1353,6 +1612,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl";,
 hash = 
"sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size 
= 10235, upload-time = "2024-02-25T23:20:01.196Z" },
 ]
 
+[[package]]
+name = "sortedcontainers"
+version = "2.4.0"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz";,
 hash = 
"sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size 
= 30594, upload-time = "2021-05-16T22:03:42.897Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl";,
 hash = 
"sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size 
= 29575, upload-time = "2021-05-16T22:03:41.177Z" },
+]
+
 [[package]]
 name = "sqlalchemy"
 version = "2.0.42"
@@ -1412,6 +1680,7 @@ dependencies = [
     { name = "asyncssh" },
     { name = "blockbuster" },
     { name = "cryptography" },
+    { name = "cyclonedx-python-lib", extra = ["json-validation"] },
     { name = "dkimpy" },
     { name = "dnspython" },
     { name = "dunamai" },
@@ -1464,6 +1733,7 @@ requires-dist = [
     { name = "asyncssh", specifier = ">=2.20.0,<3.0.0" },
     { name = "blockbuster", specifier = ">=1.5.23,<2.0.0" },
     { name = "cryptography", specifier = "~=44.0" },
+    { name = "cyclonedx-python-lib", extras = ["json-validation"], specifier = 
">=11.0.0" },
     { name = "dkimpy", git = "https://github.com/sbp/dkimpy.git?rev=main"; },
     { name = "dnspython", specifier = ">=2.7.0,<3.0.0" },
     { name = "dunamai", specifier = ">=1.23.0" },
@@ -1524,6 +1794,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/28/78/0d8ffa40e9ec6cbbabe4d93675092fea1cadc4c280495375fc1f2fa42793/types_aiofiles-24.1.0.20250809-py3-none-any.whl";,
 hash = 
"sha256:657c83f876047ffc242b34bfcd9167f201d1b02e914ee854f16e589aa95c0d45", size 
= 14300, upload-time = "2025-08-09T03:14:40.438Z" },
 ]
 
+[[package]]
+name = "types-python-dateutil"
+version = "2.9.0.20250822"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/0c/0a/775f8551665992204c756be326f3575abba58c4a3a52eef9909ef4536428/types_python_dateutil-2.9.0.20250822.tar.gz";,
 hash = 
"sha256:84c92c34bd8e68b117bff742bc00b692a1e8531262d4507b33afcc9f7716cd53", size 
= 16084, upload-time = "2025-08-22T03:02:00.613Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/ab/d9/a29dfa84363e88b053bf85a8b7f212a04f0d7343a4d24933baa45c06e08b/types_python_dateutil-2.9.0.20250822-py3-none-any.whl";,
 hash = 
"sha256:849d52b737e10a6dc6621d2bd7940ec7c65fcb69e6aa2882acf4e56b2b508ddc", size 
= 17892, upload-time = "2025-08-22T03:01:59.436Z" },
+]
+
 [[package]]
 name = "typing-extensions"
 version = "4.14.1"
@@ -1545,6 +1824,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl";,
 hash = 
"sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size 
= 14552, upload-time = "2025-05-21T18:55:22.152Z" },
 ]
 
+[[package]]
+name = "uri-template"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz";,
 hash = 
"sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size 
= 21678, upload-time = "2023-06-21T01:49:05.374Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl";,
 hash = 
"sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size 
= 11140, upload-time = "2023-06-21T01:49:03.467Z" },
+]
+
 [[package]]
 name = "urllib3"
 version = "2.5.0"
@@ -1591,6 +1879,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/a8/b4/c57b99518fadf431f3ef47a610839e46e5f8abf9814f969859d1c65c02c7/watchfiles-1.0.5-cp313-cp313-win_amd64.whl";,
 hash = 
"sha256:f436601594f15bf406518af922a89dcaab416568edb6f65c4e5bbbad1ea45c11", size 
= 291087, upload-time = "2025-04-08T10:35:52.458Z" },
 ]
 
+[[package]]
+name = "webcolors"
+version = "24.11.1"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/7b/29/061ec845fb58521848f3739e466efd8250b4b7b98c1b6c5bf4d40b419b7e/webcolors-24.11.1.tar.gz";,
 hash = 
"sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6", size 
= 45064, upload-time = "2024-11-11T07:43:24.224Z" }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/60/e8/c0e05e4684d13459f93d312077a9a2efbe04d59c393bc2b8802248c908d4/webcolors-24.11.1-py3-none-any.whl";,
 hash = 
"sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9", size 
= 14934, upload-time = "2024-11-11T07:43:22.529Z" },
+]
+
 [[package]]
 name = "werkzeug"
 version = "3.1.3"


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to