diff --git a/.github/workflows/generate-protos.yml b/.github/workflows/generate-protos.yml index 8b819333311..2f06f98294d 100644 --- a/.github/workflows/generate-protos.yml +++ b/.github/workflows/generate-protos.yml @@ -4,7 +4,7 @@ on: push: branches: [ master ] paths: - - 'gcp/api/v1/osv_service_v1.proto' + - 'proto/v1/osv_service_v1.proto' - 'osv/importfinding.proto' - 'osv/osv-schema/proto/vulnerability.proto' workflow_dispatch: @@ -41,9 +41,7 @@ jobs: run: pip install poetry==2.3.3 - name: Set up poetry - run: | - poetry install - cd gcp/api && poetry install + run: poetry install - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 diff --git a/.gitignore b/.gitignore index e6b838c720d..e469574491f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ build/lib hurl-scripts/ temp/* **/tmp/** -gcp/api/v1/osv/** .hypothesis go/api-devserver go/api diff --git a/.gitmodules b/.gitmodules index eb3647740ea..239d4d23e28 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "gcp/api/googleapis"] - path = gcp/api/googleapis - url = https://github.com/googleapis/googleapis [submodule "docker/worker/osv-test"] path = gcp/workers/worker/osv-test url = https://github.com/oliverchang/osv-test.git diff --git a/AGENTS.md b/AGENTS.md index e9e1a8f5d3c..5b03493196a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -250,7 +250,7 @@ Contains deployment setups, workers running in GKE, Cloud Functions, and the use - **Status**: **Active (Go)**. - Serves the public OSV gRPC API server (transcoded to HTTP/JSON REST via ESPv2). - **Deployment Target**: **Google Cloud Run** (managed via Cloud Deploy pipeline `osv-api` deploying to `osv-grpc-backend`). -- *Note*: Fully migrated from Python to Go. The legacy Python implementation remains in `gcp/api/` but is retired. +- *Note*: Fully migrated from Python to Go. Protobuf definitions and descriptor files are located under `proto/v1/`. ### 2. Website (`gcp/website/`) - **Status**: **Active**. diff --git a/Makefile b/Makefile index 898a02491d5..75f5477166a 100644 --- a/Makefile +++ b/Makefile @@ -44,17 +44,10 @@ go-tests: ## Run Go services tests cd go && ./run_tests.sh api-server-tests: ## Run Go API server integration tests - test -f $(HOME)/.config/gcloud/application_default_credentials.json || (echo "GCP Application Default Credentials not set, try 'gcloud auth application-default login'"; exit 1) - cd go && go build -o ./api ./cmd/api - cd gcp/api && docker build -f Dockerfile.esp -t osv/esp:latest . - cd gcp/api && OSV_USE_GO_BACKEND=1 ./run_tests.sh $(HOME)/.config/gcloud/application_default_credentials.json - cd gcp/api && OSV_USE_GO_BACKEND=1 ./run_tests_e2e.sh $(HOME)/.config/gcloud/application_default_credentials.json + ./tools/apitester/run_tests.sh update-api-snapshots: ## Update API query snapshots - test -f $(HOME)/.config/gcloud/application_default_credentials.json || (echo "GCP Application Default Credentials not set, try 'gcloud auth application-default login'"; exit 1) - cd go && go build -o ./api ./cmd/api - cd gcp/api && docker build -f Dockerfile.esp -t osv/esp:latest . - cd gcp/api && UPDATE_SNAPS=true OSV_USE_GO_BACKEND=1 ./run_tests_e2e.sh $(HOME)/.config/gcloud/application_default_credentials.json + UPDATE_SNAPS=true ./tools/apitester/run_tests.sh lint: ## Run linters and format checks GOTOOLCHAIN=auto $(run-cmd) tools/lint_and_format.sh @@ -63,33 +56,33 @@ build-osv-protos: cd osv && $(run-cmd) python -m grpc_tools.protoc --python_out=. --mypy_out=. --proto_path=. --proto_path=osv-schema/proto vulnerability.proto importfinding.proto build-api-protos: - cd gcp/api/v1 && $(run-cmd) python -m grpc_tools.protoc \ + cd proto/v1 && protoc \ --include_imports \ --include_source_info \ - --proto_path=googleapis \ + --proto_path=.. \ --proto_path=. \ - --proto_path=osv \ - --proto_path=osv/osv-schema/proto \ + --proto_path=../../osv \ + --proto_path=../../osv/osv-schema/proto \ --descriptor_set_out=api_descriptor.pb \ - --python_out=.. \ - --grpc_python_out=.. \ - --mypy_out=.. \ vulnerability.proto importfinding.proto osv_service_v1.proto cd osv && protoc \ --proto_path=. \ --go_out=paths=source_relative:../bindings/go/api \ importfinding.proto - cd gcp/api/v1 && protoc \ - --proto_path=googleapis \ + cd proto/v1 && protoc \ + --proto_path=.. \ --proto_path=. \ - --proto_path=osv \ - --proto_path=osv/osv-schema/proto \ - --go_out=paths=source_relative:../../../bindings/go/api \ - --go-grpc_out=paths=source_relative:../../../bindings/go/api \ + --proto_path=../../osv \ + --proto_path=../../osv/osv-schema/proto \ + --go_out=paths=source_relative:../../bindings/go/api \ + --go-grpc_out=paths=source_relative:../../bindings/go/api \ osv_service_v1.proto build-protos: build-osv-protos build-api-protos ## Build all protocol buffers +build-swagger: ## Build Swagger/OpenAPI documentation + ./docs/build_swagger.sh + build-website-frontend: cd website/frontend3 && pnpm install && pnpm run build cd website/blog && hugo --buildFuture -d ../dist/static/blog @@ -127,26 +120,15 @@ run-go-website-prod: stage-website-assets # Run with `make run-api-server ARGS=--no-backend` to launch esp without backend. run-api-server: ## Run local Go API server with ESPv2 proxy test -f $(HOME)/.config/gcloud/application_default_credentials.json || (echo "GCP Application Default Credentials not set, try 'gcloud auth application-default login'"; exit 1) - docker inspect osv/esp:latest >/dev/null 2>&1 || (cd gcp/api && docker build -f Dockerfile.esp -t osv/esp:latest .) + docker inspect osv/esp:latest >/dev/null 2>&1 || docker build -f docker/esp/Dockerfile -t osv/esp:latest docker/esp @cd go && go build -o ./api-devserver ./cmd/api-devserver && (GOOGLE_CLOUD_PROJECT=oss-vdb OSV_VULNERABILITIES_BUCKET=osv-vulnerabilities ./api-devserver $(ARGS); EXIT_CODE=$$?; rm -f ./api-devserver; exit $$EXIT_CODE) # Run the Go developer server orchestrator against the staging/test environment. run-api-server-test: test -f $(HOME)/.config/gcloud/application_default_credentials.json || (echo "GCP Application Default Credentials not set, try 'gcloud auth application-default login'"; exit 1) - docker inspect osv/esp:latest >/dev/null 2>&1 || (cd gcp/api && docker build -f Dockerfile.esp -t osv/esp:latest .) + docker inspect osv/esp:latest >/dev/null 2>&1 || docker build -f docker/esp/Dockerfile -t osv/esp:latest docker/esp @cd go && go build -o ./api-devserver ./cmd/api-devserver && (GOOGLE_CLOUD_PROJECT=oss-vdb-test OSV_VULNERABILITIES_BUCKET=osv-test-vulnerabilities ./api-devserver $(ARGS); EXIT_CODE=$$?; rm -f ./api-devserver; exit $$EXIT_CODE) -# Legacy Python API server targets -run-python-api-server: - test -f $(HOME)/.config/gcloud/application_default_credentials.json || (echo "GCP Application Default Credentials not set, try 'gcloud auth application-default login'"; exit 1) - cd gcp/api && docker build -f Dockerfile.esp -t osv/esp:latest . - cd gcp/api && $(install-cmd) && GOOGLE_CLOUD_PROJECT=oss-vdb OSV_VULNERABILITIES_BUCKET=osv-vulnerabilities $(run-cmd) python test_server.py $(HOME)/.config/gcloud/application_default_credentials.json $(ARGS) - -run-python-api-server-test: - test -f $(HOME)/.config/gcloud/application_default_credentials.json || (echo "GCP Application Default Credentials not set, try 'gcloud auth application-default login'"; exit 1) - cd gcp/api && docker build -f Dockerfile.esp -t osv/esp:latest . - cd gcp/api && $(install-cmd) && GOOGLE_CLOUD_PROJECT=oss-vdb-test OSV_VULNERABILITIES_BUCKET=osv-test-vulnerabilities $(run-cmd) python test_server.py $(HOME)/.config/gcloud/application_default_credentials.json $(ARGS) - # TODO: API integration tests. all-tests: lib-tests worker-tests importer-tests recoverer-tests website-tests vulnfeed-tests bindings-tests go-tests ## Run all tests diff --git a/README.md b/README.md index 72d532f0799..61f2ec4027b 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,8 @@ consists of: |-----------------|------| | `bindings/` | Language bindings for the [OSV API](https://google.github.io/osv.dev/api/) (currently Go only) | | `deployment/` | Terraform & Cloud Deploy config files
A few Cloud Build config yamls | -| `docker/` | CI docker files (`ci`, `deployment`, `terraform`)
`worker-base` docker image for `gcp/workers/worker` | -| `docs/` | Jekyll files for https://google.github.io/osv.dev/
`build_swagger.py` and `tools.go` | -| `gcp/api` | OSV API server files (including files for the local ESP server)
protobuf files in `/v1`| +| `docker/` | CI docker files (`ci`, `deployment`, `terraform`, `esp`)
`worker-base` docker image for `gcp/workers/worker` | +| `docs/` | Jekyll files for https://google.github.io/osv.dev/
`build_swagger.sh` and `tools.go` | | `gcp/datastore` | The datastore index file (`index.yaml`) | | `gcp/functions` | The Cloud Function for publishing PyPI vulnerabilities (maintained, but not developed) | | `gcp/indexer` | The determine version `indexer` | diff --git a/bindings/go/api/importfinding.pb.go b/bindings/go/api/importfinding.pb.go index 561a2fc4956..a8000881649 100644 --- a/bindings/go/api/importfinding.pb.go +++ b/bindings/go/api/importfinding.pb.go @@ -14,8 +14,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v3.21.12 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: importfinding.proto package api diff --git a/bindings/go/api/osv_service_v1.pb.go b/bindings/go/api/osv_service_v1.pb.go index ec55a4e7912..f05ba4b204a 100644 --- a/bindings/go/api/osv_service_v1.pb.go +++ b/bindings/go/api/osv_service_v1.pb.go @@ -14,8 +14,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v3.21.12 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: osv_service_v1.proto package api diff --git a/bindings/go/api/osv_service_v1_grpc.pb.go b/bindings/go/api/osv_service_v1_grpc.pb.go index c635aa5b007..554c916356c 100644 --- a/bindings/go/api/osv_service_v1_grpc.pb.go +++ b/bindings/go/api/osv_service_v1_grpc.pb.go @@ -14,8 +14,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.6.1 -// - protoc v3.21.12 +// - protoc-gen-go-grpc v1.6.2 +// - protoc v7.35.1 // source: osv_service_v1.proto package api diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 2b5efa3ab3d..dd82e954428 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -32,11 +32,6 @@ steps: args: ['gcloud', 'builds', 'submit', '--region=${LOCATION}', '--config=gcp/website/cloudbuild.yaml', '.'] waitFor: ['init'] -- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:slim' - id: 'api-tests' - args: ['gcloud', 'builds', 'submit', '--region=${LOCATION}', '--config=gcp/api/cloudbuild.yaml', '.'] - waitFor: ['init'] - - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:slim' id: 'vulnfeeds-tests' args: ['gcloud', 'builds', 'submit', '--region=${LOCATION}', '--config=vulnfeeds/cloudbuild.yaml', '.'] diff --git a/deployment/terraform/environments/oss-vdb-test/api/api_descriptor.pb b/deployment/terraform/environments/oss-vdb-test/api/api_descriptor.pb index 23f7a563ce7..3c387c05707 120000 --- a/deployment/terraform/environments/oss-vdb-test/api/api_descriptor.pb +++ b/deployment/terraform/environments/oss-vdb-test/api/api_descriptor.pb @@ -1 +1 @@ -../../../../../gcp/api/v1/api_descriptor.pb \ No newline at end of file +../../../../../proto/v1/api_descriptor.pb \ No newline at end of file diff --git a/deployment/terraform/environments/oss-vdb/api/api_descriptor.pb b/deployment/terraform/environments/oss-vdb/api/api_descriptor.pb index 23f7a563ce7..3c387c05707 120000 --- a/deployment/terraform/environments/oss-vdb/api/api_descriptor.pb +++ b/deployment/terraform/environments/oss-vdb/api/api_descriptor.pb @@ -1 +1 @@ -../../../../../gcp/api/v1/api_descriptor.pb \ No newline at end of file +../../../../../proto/v1/api_descriptor.pb \ No newline at end of file diff --git a/gcp/api/Dockerfile.esp b/docker/esp/Dockerfile similarity index 100% rename from gcp/api/Dockerfile.esp rename to docker/esp/Dockerfile diff --git a/docs/README.md b/docs/README.md index 91c51a7cbfd..89fe16498fd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -60,5 +60,5 @@ go mod download ### Generation ```bash -python3 ./build_swagger.py +./build_swagger.sh ``` diff --git a/docs/build_swagger.py b/docs/build_swagger.py deleted file mode 100644 index 810e0751e9f..00000000000 --- a/docs/build_swagger.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Documentation builder.""" - -import json -import os -import shutil -import subprocess - -_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -_GENERATED_FILENAME = 'v1/osv_service_v1.swagger.json' - - -def main(): - api_dir = os.path.join(_ROOT_DIR, 'gcp', 'api') - v1_api_dir = os.path.join(api_dir, 'v1') - googleapis_dir = os.path.join(api_dir, 'googleapis') - service_proto_path = os.path.join(v1_api_dir, 'osv_service_v1.proto') - - # Add OSV dependencies. - osv_path = os.path.join(api_dir, 'osv') - if os.path.exists(osv_path): - shutil.rmtree(osv_path) - - shutil.copytree(os.path.join(_ROOT_DIR, 'osv'), osv_path) - - subprocess.run([ - 'protoc', - '-I', - api_dir, - '-I', - v1_api_dir, - '-I', - googleapis_dir, - '--openapiv2_out', - '.', - '--openapiv2_opt', - 'logtostderr=true', - service_proto_path, - ], - check=True) - - with open(_GENERATED_FILENAME) as f: - spec = json.load(f) - - spec['host'] = 'api.osv.dev' - spec['info']['title'] = 'OSV' - spec['info']['version'] = '1.0' - - with open(_GENERATED_FILENAME, 'w') as f: - f.write(json.dumps(spec, indent=2)) - - shutil.move(_GENERATED_FILENAME, os.path.basename(_GENERATED_FILENAME)) - - -if __name__ == '__main__': - main() diff --git a/docs/build_swagger.sh b/docs/build_swagger.sh new file mode 100755 index 00000000000..cae0b76ab09 --- /dev/null +++ b/docs/build_swagger.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +DOCS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(dirname "$DOCS_DIR")" +TARGET_FILE="$DOCS_DIR/osv_service_v1.swagger.json" +GENERATED_FILE="$DOCS_DIR/v1/osv_service_v1.swagger.json" + +protoc \ + -I "$ROOT_DIR/proto" \ + -I "$ROOT_DIR/proto/v1" \ + -I "$ROOT_DIR/osv" \ + -I "$ROOT_DIR/osv/osv-schema/proto" \ + --openapiv2_out="$DOCS_DIR" \ + --openapiv2_opt=logtostderr=true \ + "$ROOT_DIR/proto/v1/osv_service_v1.proto" + +# Set OpenAPI host and metadata +jq '.host = "api.osv.dev" | .info.title = "OSV" | .info.version = "1.0"' "$GENERATED_FILE" > "$TARGET_FILE" + +rm -f "$GENERATED_FILE" +rmdir "$DOCS_DIR/v1" 2>/dev/null || true diff --git a/docs/contributing/architecture.md b/docs/contributing/architecture.md index 19b150bc39f..a600404d6d1 100644 --- a/docs/contributing/architecture.md +++ b/docs/contributing/architecture.md @@ -43,7 +43,7 @@ workloads. The [API server] runs on [Cloud Run], and is served by [Cloud Endpoints]. -[API server]: https://github.com/google/osv.dev/tree/master/gcp/api +[API server]: https://github.com/google/osv.dev/tree/master/go/cmd/api [Cloud Run]: https://cloud.google.com/run [Cloud Endpoints]: https://cloud.google.com/endpoints diff --git a/docs/osv_service_v1.swagger.json b/docs/osv_service_v1.swagger.json index 15a65829671..3e74ff2bce0 100644 --- a/docs/osv_service_v1.swagger.json +++ b/docs/osv_service_v1.swagger.json @@ -4,6 +4,11 @@ "title": "OSV", "version": "1.0" }, + "tags": [ + { + "name": "OSV" + } + ], "consumes": [ "application/json" ], @@ -31,7 +36,7 @@ }, "parameters": [ { - "name": "body", + "name": "query", "in": "body", "required": true, "schema": { @@ -46,7 +51,7 @@ }, "/v1/querybatch": { "post": { - "summary": "Query vulnerabilities (batched) for given package versions and commits.\nThis currently allows a maximum of 1000 package versions to be included in a single query.", + "summary": "Query vulnerabilities (batched) for given package versions and commits.\nThis currently allows a maximum of 1000 package versions to be included in\na single query.", "operationId": "OSV_QueryAffectedBatch", "responses": { "200": { @@ -64,7 +69,7 @@ }, "parameters": [ { - "name": "body", + "name": "query", "in": "body", "required": true, "schema": { @@ -128,7 +133,7 @@ }, "parameters": [ { - "name": "body", + "name": "query", "in": "body", "required": true, "schema": { @@ -140,19 +145,69 @@ "OSV" ] } + }, + "/v1experimental/importfindings/{source}": { + "get": { + "summary": "Get import findings per source.", + "operationId": "OSV_ImportFindings", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ImportFindingList" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "source", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "OSV" + ] + } } }, "definitions": { + "FileHashHashType": { + "type": "string", + "enum": [ + "MD5" + ], + "default": "MD5" + }, + "SeveritySource": { + "type": "string", + "enum": [ + "SOURCE_UNSPECIFIED", + "NVD", + "CNA", + "SELF" + ], + "default": "SOURCE_UNSPECIFIED", + "description": "Source of the severity." + }, "osvAffected": { "type": "object", "properties": { "package": { "$ref": "#/definitions/osvPackage", - "description": "Required. Package information." + "description": "Optional. Package information." }, "ranges": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/osvRange" }, "description": "Required. Range information." @@ -164,17 +219,18 @@ }, "description": "Optional. List of affected versions." }, - "ecosystemSpecific": { + "ecosystem_specific": { "type": "object", "description": "Optional. JSON object holding additional information about the\nvulnerability as defined by the ecosystem for which the record applies." }, - "databaseSpecific": { + "database_specific": { "type": "object", "description": "Optional. JSON object holding additional information about the\nvulnerability as defined by the database for which the record applies." }, "severity": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/osvSeverity" }, "description": "Optional. Severity of the vulnerability for this package." @@ -234,13 +290,58 @@ "type": "string", "description": "The limit to apply to the range." }, - "lastAffected": { + "last_affected": { "type": "string", "description": "The last affected version." } }, "description": "Version events." }, + "osvImportFinding": { + "type": "object", + "properties": { + "bug_id": { + "type": "string" + }, + "source": { + "type": "string" + }, + "findings": { + "type": "array", + "items": { + "$ref": "#/definitions/osvImportFindingType" + } + }, + "first_seen": { + "type": "string", + "format": "date-time" + }, + "last_attempt": { + "type": "string", + "format": "date-time" + } + }, + "description": "An importfinding entry.\nThe protobuf representation is *NOT* stable and only used for implementing\nthe JSON based API." + }, + "osvImportFindingType": { + "type": "string", + "enum": [ + "IMPORT_FINDING_TYPE_NONE", + "IMPORT_FINDING_TYPE_DELETED", + "IMPORT_FINDING_TYPE_INVALID_JSON", + "IMPORT_FINDING_TYPE_INVALID_PACKAGE", + "IMPORT_FINDING_TYPE_INVALID_PURL", + "IMPORT_FINDING_TYPE_INVALID_VERSION", + "IMPORT_FINDING_TYPE_INVALID_COMMIT", + "IMPORT_FINDING_TYPE_INVALID_RANGE", + "IMPORT_FINDING_TYPE_INVALID_RECORD", + "IMPORT_FINDING_TYPE_INVALID_ALIASES", + "IMPORT_FINDING_TYPE_INVALID_UPSTREAM", + "IMPORT_FINDING_TYPE_INVALID_RELATED", + "IMPORT_FINDING_TYPE_BAD_ALIASED_CVE" + ], + "default": "IMPORT_FINDING_TYPE_NONE" + }, "osvPackage": { "type": "object", "properties": { @@ -250,7 +351,7 @@ }, "ecosystem": { "type": "string", - "description": "Required. The ecosystem for this package. \nFor the complete list of valid ecosystem names, see\n." + "description": "Required. The ecosystem for this package.\nFor the complete list of valid ecosystem names, see\n." }, "purl": { "type": "string", @@ -273,9 +374,14 @@ "events": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/osvEvent" }, "description": "Required. Version event information." + }, + "database_specific": { + "type": "object", + "description": "Optional. JSON object holding additional information about the\nvulnerability as defined by the database for which the record applies." } }, "description": "Affected ranges." @@ -309,15 +415,20 @@ "type": "string", "enum": [ "NONE", - "WEB", "ADVISORY", - "REPORT", + "ARTICLE", + "DETECTION", + "DISCUSSION", + "EVIDENCE", "FIX", + "GIT", + "INTRODUCED", "PACKAGE", - "ARTICLE", - "EVIDENCE" + "REPORT", + "WEB" ], - "default": "NONE" + "default": "NONE", + "description": " - NONE: Next ID = 12." }, "osvSeverity": { "type": "object", @@ -329,6 +440,10 @@ "score": { "type": "string", "description": "The quantitative score." + }, + "source": { + "$ref": "#/definitions/SeveritySource", + "description": "Optional. The source of this severity assessment." } } }, @@ -336,9 +451,10 @@ "type": "string", "enum": [ "UNSPECIFIED", - "CVSS_V4", "CVSS_V3", - "CVSS_V2" + "CVSS_V2", + "CVSS_V4", + "Ubuntu" ], "default": "UNSPECIFIED", "description": "Type of the severity." @@ -346,13 +462,13 @@ "osvVulnerability": { "type": "object", "properties": { - "schemaVersion": { + "schema_version": { "type": "string", "description": "The OSV schema version." }, "id": { "type": "string", - "description": "The `id` field is a unique identifier for the vulnerability entry. It is a\nstring of the format `-`, where `DB` names the database and\n`ENTRYID` is in the format used by the database. For example:\n\u201cOSV-2020-111\u201d, \u201cCVE-2021-3114\u201d, or \u201cGHSA-vp9c-fpxx-744v\u201d." + "description": "The `id` field is a unique identifier for the vulnerability entry. It is a\nstring of the format `-`, where `DB` names the database and\n`ENTRYID` is in the format used by the database. For example:\n“OSV-2020-111”, “CVE-2021-3114”, or “GHSA-vp9c-fpxx-744v”." }, "published": { "type": "string", @@ -383,6 +499,13 @@ }, "description": "Optional. List of IDs of closely related vulnerabilities, such as the same\nproblem in alternate ecosystems." }, + "upstream": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Optional. List of IDs of upstream vulnerabilities of a vulnerability." + }, "summary": { "type": "string", "description": "Required. One line human readable summary for the vulnerability. It is\nrecommended to keep this under 120 characters." @@ -394,6 +517,7 @@ "affected": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/osvAffected" }, "description": "Required. Affected commit ranges and versions." @@ -401,17 +525,19 @@ "references": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/osvReference" }, - "description": "Optional. URLs to more information/advisories (including the\nscheme e.g \"https://\")." + "description": "Optional. URLs to more information/advisories (including the\nscheme e.g. \"https://\")." }, - "databaseSpecific": { + "database_specific": { "type": "object", "description": "Optional. JSON object holding additional information about the\nvulnerability as defined by the database for which the record applies." }, "severity": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/osvSeverity" }, "description": "Optional. Severity of the vulnerability." @@ -419,6 +545,7 @@ "credits": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/osvCredit" }, "description": "Optional. Credits for the vulnerability." @@ -429,14 +556,11 @@ "protobufAny": { "type": "object", "properties": { - "typeUrl": { + "@type": { "type": "string" - }, - "value": { - "type": "string", - "format": "byte" } - } + }, + "additionalProperties": {} }, "protobufNullValue": { "type": "string", @@ -444,7 +568,7 @@ "NULL_VALUE" ], "default": "NULL_VALUE", - "description": "`NullValue` is a singleton enumeration to represent the null value for the\n`Value` type union.\n\n The JSON representation for `NullValue` is JSON `null`.\n\n - NULL_VALUE: Null value." + "description": "Represents a JSON `null`.\n\n`NullValue` is a sentinel, using an enum with only one value to represent\nthe null value for the `Value` type union.\n\nA field of type `NullValue` with any value other than `0` is considered\ninvalid. Most ProtoJSON serializers will emit a Value with a `null_value` set\nas a JSON `null` regardless of the integer value, and so will round trip to\na `0` value.\n\n - NULL_VALUE: Null value." }, "rpcStatus": { "type": "object", @@ -459,6 +583,7 @@ "details": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/protobufAny" } } @@ -470,6 +595,7 @@ "queries": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/v1Query" }, "description": "The queries that form this batch query." @@ -483,6 +609,7 @@ "results": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/v1VulnerabilityList" } } @@ -492,12 +619,12 @@ "v1FileHash": { "type": "object", "properties": { - "filePath": { + "file_path": { "type": "string", "description": "The file path inside the repository, relative to the repository root." }, - "hashType": { - "type": "string" + "hash_type": { + "$ref": "#/definitions/FileHashHashType" }, "hash": { "type": "string", @@ -506,6 +633,19 @@ }, "description": "Information about the files in the repository\nto identify the version." }, + "v1ImportFindingList": { + "type": "object", + "properties": { + "invalid_records": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/osvImportFinding" + } + } + }, + "description": "Result of ImportFindings." + }, "v1Query": { "type": "object", "properties": { @@ -521,7 +661,7 @@ "$ref": "#/definitions/osvPackage", "description": "The package to query against. When a `commit` hash is given, this is\noptional." }, - "pageToken": { + "page_token": { "type": "string" } }, @@ -539,13 +679,23 @@ "$ref": "#/definitions/v1VersionRepositoryInformation", "description": "Information about the upstream repository." }, - "osvIdentifier": { + "osv_identifier": { "$ref": "#/definitions/osvPackage", "description": "The OSV identifier." }, "cpe23": { "type": "string", "description": "CPE 2.3." + }, + "minimum_file_matches": { + "type": "string", + "format": "int64", + "description": "Definite number of files that have matched." + }, + "estimated_diff_files": { + "type": "string", + "format": "int64", + "description": "Estimated number of files that are different." } }, "description": "Match information for the provided VersionQuery." @@ -556,6 +706,7 @@ "matches": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/v1VersionMatch" } } @@ -569,9 +720,10 @@ "type": "string", "description": "The name of the dependency. Can be empty." }, - "fileHashes": { + "file_hashes": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/v1FileHash" } } @@ -588,11 +740,6 @@ "type": "string", "description": "Source address of the repository." }, - "commit": { - "type": "string", - "format": "byte", - "description": "Commit hash." - }, "tag": { "type": "string", "title": "Commit tag" @@ -600,6 +747,10 @@ "version": { "type": "string", "title": "Parsed version from commit tag" + }, + "commit": { + "type": "string", + "description": "Commit hash hex." } } }, @@ -617,10 +768,11 @@ "vulns": { "type": "array", "items": { + "type": "object", "$ref": "#/definitions/osvVulnerability" } }, - "nextPageToken": { + "next_page_token": { "type": "string" } }, diff --git a/gcp/api/.gcloudignore b/gcp/api/.gcloudignore deleted file mode 100644 index 8f19994ae20..00000000000 --- a/gcp/api/.gcloudignore +++ /dev/null @@ -1 +0,0 @@ -Pipfile* diff --git a/gcp/api/.gitignore b/gcp/api/.gitignore deleted file mode 100644 index ffaf121fb6a..00000000000 --- a/gcp/api/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -ENV/ -backend.log -esp.log -requirements.txt diff --git a/gcp/api/Dockerfile b/gcp/api/Dockerfile deleted file mode 100644 index 8532f0ffd57..00000000000 --- a/gcp/api/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM python:3.13.3-slim@sha256:56a11364ffe0fee3bd60af6d6d5209eba8a99c2c16dc4c7c5861dc06261503cc - -# Generation 1 of cloud run overrides the HOME environment variable, causing -# poetry to run in the incorrect environment, as it defaults to using $HOME/.cache/virtualenvs/... -# -# This forces it to create the virtualenv in the same directory as the project, avoiding this issue. -ENV POETRY_VIRTUALENVS_IN_PROJECT=true -ENV POETRY_HOME "/opt/poetry" -COPY docker/poetry/requirements.txt ./poetry-requirements.txt -RUN python3 -m venv $POETRY_HOME && \ - $POETRY_HOME/bin/pip install --require-hashes -r ./poetry-requirements.txt && \ - ln -s $POETRY_HOME/bin/poetry /usr/local/bin/poetry - -# TODO(ochang): Just copy the entire project (needs a clean checkout). -COPY pyproject.toml poetry.lock README.md /osv/ -COPY osv /osv/osv -COPY gcp/api /osv/gcp/api - -WORKDIR /osv/gcp/api -RUN poetry install - -ENTRYPOINT ["poetry", "run", "python", "/osv/gcp/api/server.py"] diff --git a/gcp/api/README.md b/gcp/api/README.md deleted file mode 100644 index 5ff4bf6a83f..00000000000 --- a/gcp/api/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# API server - -The API server runs on Cloud Run with Cloud Endpoints. - -The Cloud Endpoints configuration is managed through terraform. - -Deployments of API backend is done through Cloud Deploy (`deployment/clouddeploy/osv-api`). - -# Contributing: - -Note: Running the API locally (via `make run-api-server` and `make api-server-tests`) requires GCP authentication to fetch the configuration from the `oss-vdb` project. See also `v1/README.md`. - -## Writing tests -To speed up running tests: - -1. `make run-api-server` at the root of this repository to start the test server. -2. `poetry shell` in this directory -3. `python -m unittest integration_test.py` in this directory. - -This avoids the need to start the test server (and waiting for it) every run. \ No newline at end of file diff --git a/gcp/api/api_descriptor.pb b/gcp/api/api_descriptor.pb deleted file mode 100644 index 978383e6c07..00000000000 Binary files a/gcp/api/api_descriptor.pb and /dev/null differ diff --git a/gcp/api/cloudbuild.yaml b/gcp/api/cloudbuild.yaml deleted file mode 100644 index e0b5cb62c06..00000000000 --- a/gcp/api/cloudbuild.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# api test runner -# This should be triggered on changes to the following: -# gcp/api/poetry.lock -# gcp/api/run_tests.sh -# gcp/api/*.py -# gcp/api/**/*.py -# osv/*.py -# osv/**/*.py - -steps: -- name: 'gcr.io/cloud-builders/git' - id: 'init' - args: ['submodule', 'update', '--init'] - # if this is invoked from another cloud build, this will fail as it is not a git repo - # the invoking cloud build file should run this step. - allowFailure: true -- name: 'gcr.io/oss-vdb/ci' - id: 'sync' - dir: gcp/api - args: ['poetry', 'sync'] - waitFor: ['-'] - -- name: 'gcr.io/oss-vdb/ci' - id: 'build-go-backend' - dir: go - args: ['go', 'build', '-o', 'api', './cmd/api'] - waitFor: ['init'] - -- name: 'gcr.io/oss-vdb/ci' - id: 'api-tests' - dir: gcp/api - #TODO: Update test scripts to support not supplying a credential. - args: ['bash', '-ex', 'run_tests.sh', '/workspace/dummy.json'] - waitFor: ['build-go-backend', 'sync'] - -timeout: 7200s -serviceAccount: 'projects/oss-vdb/serviceAccounts/api-e2e-tester@oss-vdb.iam.gserviceaccount.com' -options: - logging: CLOUD_LOGGING_ONLY - env: - - CLOUDBUILD=1 - - OSV_USE_GO_BACKEND=1 diff --git a/gcp/api/cursor.py b/gcp/api/cursor.py deleted file mode 100644 index db7ed77a64c..00000000000 --- a/gcp/api/cursor.py +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright 2024 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""OSV API server cursor implementation.""" - -import base64 -import dataclasses -from enum import Enum -from typing import Self -import typing - -from google.cloud import ndb -import google.cloud.ndb.exceptions as ndb_exceptions - -_FIRST_PAGE_TOKEN = base64.urlsafe_b64encode(b'FIRST_PAGE_TOKEN').decode() -# Use ':' as the separator as it doesn't appear in urlsafe_b64encode -# (which is what is used for both _FIRST_PAGE_TOKEN, and ndb.Cursor.urlsafe()) -_METADATA_SEPARATOR = ':' -CURSOR_LAST_ID = 'last_id' - - -@dataclasses.dataclass -class QueryCursorMetadata: - """Metadata for the query cursor.""" - # The last vulnerability ID seen in a query. - # This is used to avoid reprocessing the same vulnerability across pages - # when multiple AffectedVersions entities exist for it. - last_id: str | None = None - - -class _QueryCursorState(Enum): - """ - Stores the 3 states a query cursor can be in. - """ - - # The cursor has reached the end, no need to return to the user - ENDED = 0 - # The cursor is at the very start of the query, no cursor needs to be - # set when querying ndb - STARTED = 1 - # ndb.Cursor is set and in progress - IN_PROGRESS = 2 - - -class QueryCursor: - """ - Custom cursor class that wraps the ndb cursor. - Allows us to represent the "starting" cursor. - - The default state is ENDED with no ndb.Cursor. - - This type could have 3 states encoded in _QueryCursorState. - If the current state is IN_PROGRESS, self.cursor will not be None. - - Attributes: - query_number: This cursor is specifically for the Nth ndb datastore - query in the current query request. (Starts from 1) - ndb_cursor: Get the internal ndb_cursor. This could be None. - ended: Whether this cursor is for a query that has finished returning data. - """ - - def __init__(self): - self._ndb_cursor: ndb.Cursor | None = None - self._cursor_state: _QueryCursorState = _QueryCursorState.ENDED - self.metadata: QueryCursorMetadata = QueryCursorMetadata() - # The first query is numbered 1. This is because the query counter is - # incremented **before** the query and the query number being used. - self.query_number: int = 1 - - @classmethod - def from_page_token(cls, page_token: str | None) -> Self: - """Generate a query cursor from a url safe page token.""" - qc = cls() - - if not page_token: - qc._cursor_state = _QueryCursorState.STARTED - qc._ndb_cursor = None - return qc - - split_values = page_token.split(_METADATA_SEPARATOR) - metadata_dict = {} - if len(split_values) >= 2: - page_token = split_values[1] - try: - qc.query_number = int(split_values[0]) - except ValueError as e: - raise ValueError('Invalid page token.') from e - for enc_meta in split_values[2:]: - meta = base64.urlsafe_b64decode(enc_meta).decode(errors='ignore') - k, _, v = meta.partition('=') - metadata_dict[k] = v - - try: - qc.metadata = QueryCursorMetadata(**metadata_dict) - except TypeError as e: - # Unexpected metadata field. - raise ValueError('Invalid page token.') from e - - if not page_token or page_token == _FIRST_PAGE_TOKEN: - qc._cursor_state = _QueryCursorState.STARTED - qc._ndb_cursor = None - return qc - - qc._ndb_cursor = ndb.Cursor(urlsafe=page_token) - qc._cursor_state = _QueryCursorState.IN_PROGRESS - return qc - - def update_from_iterator(self, it: ndb.QueryIterator) -> None: - """ - Update the current cursor from the value of the ndb.iterator passed in. - - Args: - it: the iterator to take the cursor position from. - """ - try: - self._ndb_cursor = typing.cast(ndb.Cursor, it.cursor_after()) - self._cursor_state = _QueryCursorState.IN_PROGRESS - except ndb_exceptions.BadArgumentError as e: - # This exception can happen when iterator has not begun iterating - # and it.next() is the very first element. - # - # In those cases, `cursor_after()`` would not be 'after' any element, - # throwing this exception. - - # We represent this by setting the state to STARTED. - if 'There is no cursor currently' in str(e): - self._ndb_cursor = None - self._cursor_state = _QueryCursorState.STARTED - else: - raise - - @property - def ndb_cursor(self) -> ndb.Cursor | None: - """The inner ndb cursor, could be None""" - if self._cursor_state == _QueryCursorState.IN_PROGRESS: - return self._ndb_cursor - - return None - - @property - def ended(self) -> bool: - """ - Whether the cursor has finished or not. - """ - return self._cursor_state == _QueryCursorState.ENDED - - def url_safe_encode(self) -> str | None: - """ - Create a url safe page token to pass back to the API caller. - """ - cursor_part: str = '' - match self._cursor_state: - case _QueryCursorState.STARTED: - cursor_part = _FIRST_PAGE_TOKEN - case _QueryCursorState.IN_PROGRESS: - # Assume that IN_PROGRESS means self.cursor is always set. - # Loudly throw an exception if this is not the case - cursor_part = self._ndb_cursor.urlsafe().decode() # type: ignore - case _QueryCursorState.ENDED: - # If ENDED, we want to return None to not include - # a token in the response - return None - - meta_dict = dataclasses.asdict(self.metadata) - meta_items = (f'{k}={v}' for k, v in meta_dict.items() if v is not None) - meta = ( - base64.urlsafe_b64encode(item.encode()).decode() for item in meta_items) - return _METADATA_SEPARATOR.join( - [str(self.query_number), cursor_part, *meta]) diff --git a/gcp/api/fixtures/IntegrationTests_api_query_response.txt b/gcp/api/fixtures/IntegrationTests_api_query_response.txt deleted file mode 100644 index 2104a2792d9..00000000000 --- a/gcp/api/fixtures/IntegrationTests_api_query_response.txt +++ /dev/null @@ -1,244 +0,0 @@ -[ '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils", "purl": ' - '"pkg:cargo/crossbeam-utils"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils", "purl": ' - '"pkg:cargo/crossbeam-utils"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils", "purl": ' - '"pkg:pypi/numpy"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils", "purl": ' - '"pkg:pypi/numpy"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils", "purl": ' - '"pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils", "purl": ' - '"pkg:pypi/numpy@8.24.0"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils"}, "version": ' - '"0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "crossbeam-utils"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy", "purl": "pkg:pypi/numpy"}, ' - '"version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy", "purl": "pkg:pypi/numpy"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:pypi/numpy@8.24.0"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "name": "numpy"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "purl": "pkg:cargo/crossbeam-utils"}, ' - '"version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "purl": "pkg:cargo/crossbeam-utils"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "purl": "pkg:cargo/crossbeam-utils@0.8.5"}, ' - '"version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "purl": "pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "purl": "pkg:pypi/numpy"}, "version": ' - '"0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "purl": "pkg:pypi/numpy"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "purl": "pkg:pypi/numpy@8.24.0"}, "version": ' - '"0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io", "purl": "pkg:pypi/numpy@8.24.0"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"ecosystem": "crates.io"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils", "purl": "pkg:cargo/crossbeam-utils"}, ' - '"version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils", "purl": "pkg:cargo/crossbeam-utils"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils", "purl": "pkg:cargo/crossbeam-utils@0.8.5"}, ' - '"version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils", "purl": "pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils", "purl": "pkg:pypi/numpy"}, "version": ' - '"0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils", "purl": "pkg:pypi/numpy"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils", "purl": "pkg:pypi/numpy@8.24.0"}, "version": ' - '"0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils", "purl": "pkg:pypi/numpy@8.24.0"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "crossbeam-utils"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy", "purl": "pkg:cargo/crossbeam-utils"}, "version": ' - '"0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy", "purl": "pkg:cargo/crossbeam-utils"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy", "purl": "pkg:cargo/crossbeam-utils@0.8.5"}, "version": ' - '"0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy", "purl": "pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy", "purl": "pkg:pypi/numpy"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy", "purl": "pkg:pypi/numpy"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy", "purl": "pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy", "purl": "pkg:pypi/numpy@8.24.0"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"name": "numpy"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"purl": "pkg:cargo/crossbeam-utils"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"purl": "pkg:cargo/crossbeam-utils"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"purl": "pkg:cargo/crossbeam-utils@0.8.5"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"purl": "pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"purl": "pkg:pypi/numpy"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"purl": "pkg:pypi/numpy"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"purl": "pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "package": ' - '{"purl": "pkg:pypi/numpy@8.24.0"}}\n', - '400:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391", "version": ' - '"0.8.5"}\n', - '200:{"commit": "d374094d8c49b6b7d288f307e11217ec5a502391"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils", ' - '"purl": "pkg:cargo/crossbeam-utils"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils", ' - '"purl": "pkg:cargo/crossbeam-utils"}}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils", ' - '"purl": "pkg:cargo/crossbeam-utils@0.8.5"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils", ' - '"purl": "pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils", ' - '"purl": "pkg:pypi/numpy"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils", ' - '"purl": "pkg:pypi/numpy"}}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils", ' - '"purl": "pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils", ' - '"purl": "pkg:pypi/numpy@8.24.0"}}\n', - '200:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils"}, ' - '"version": "0.8.5"}\n', - '200:{"package": {"ecosystem": "crates.io", "name": "crossbeam-utils"}}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils"}}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:pypi/numpy"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:pypi/numpy"}}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "name": "numpy", "purl": ' - '"pkg:pypi/numpy@8.24.0"}}\n', - '200:{"package": {"ecosystem": "crates.io", "name": "numpy"}, "version": ' - '"0.8.5"}\n', - '200:{"package": {"ecosystem": "crates.io", "name": "numpy"}}\n', - '400:{"package": {"ecosystem": "crates.io", "purl": ' - '"pkg:cargo/crossbeam-utils"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "purl": ' - '"pkg:cargo/crossbeam-utils"}}\n', - '400:{"package": {"ecosystem": "crates.io", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"package": {"ecosystem": "crates.io", "purl": "pkg:pypi/numpy"}, ' - '"version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "purl": "pkg:pypi/numpy"}}\n', - '400:{"package": {"ecosystem": "crates.io", "purl": ' - '"pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io", "purl": ' - '"pkg:pypi/numpy@8.24.0"}}\n', - '400:{"package": {"ecosystem": "crates.io"}, "version": "0.8.5"}\n', - '400:{"package": {"ecosystem": "crates.io"}}\n', - '400:{"package": {"name": "crossbeam-utils", "purl": ' - '"pkg:cargo/crossbeam-utils"}, "version": "0.8.5"}\n', - '400:{"package": {"name": "crossbeam-utils", "purl": ' - '"pkg:cargo/crossbeam-utils"}}\n', - '400:{"package": {"name": "crossbeam-utils", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}, "version": "0.8.5"}\n', - '400:{"package": {"name": "crossbeam-utils", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"package": {"name": "crossbeam-utils", "purl": "pkg:pypi/numpy"}, ' - '"version": "0.8.5"}\n', - '400:{"package": {"name": "crossbeam-utils", "purl": "pkg:pypi/numpy"}}\n', - '400:{"package": {"name": "crossbeam-utils", "purl": ' - '"pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '400:{"package": {"name": "crossbeam-utils", "purl": ' - '"pkg:pypi/numpy@8.24.0"}}\n', - '200:{"package": {"name": "crossbeam-utils"}, "version": "0.8.5"}\n', - '400:{"package": {"name": "crossbeam-utils"}}\n', - '400:{"package": {"name": "numpy", "purl": "pkg:cargo/crossbeam-utils"}, ' - '"version": "0.8.5"}\n', - '400:{"package": {"name": "numpy", "purl": "pkg:cargo/crossbeam-utils"}}\n', - '400:{"package": {"name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}, "version": "0.8.5"}\n', - '400:{"package": {"name": "numpy", "purl": ' - '"pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '400:{"package": {"name": "numpy", "purl": "pkg:pypi/numpy"}, "version": ' - '"0.8.5"}\n', - '400:{"package": {"name": "numpy", "purl": "pkg:pypi/numpy"}}\n', - '400:{"package": {"name": "numpy", "purl": "pkg:pypi/numpy@8.24.0"}, ' - '"version": "0.8.5"}\n', - '400:{"package": {"name": "numpy", "purl": "pkg:pypi/numpy@8.24.0"}}\n', - '200:{"package": {"name": "numpy"}, "version": "0.8.5"}\n', - '400:{"package": {"name": "numpy"}}\n', - '200:{"package": {"purl": "pkg:cargo/crossbeam-utils"}, "version": ' - '"0.8.5"}\n', - '200:{"package": {"purl": "pkg:cargo/crossbeam-utils"}}\n', - '400:{"package": {"purl": "pkg:cargo/crossbeam-utils@0.8.5"}, "version": ' - '"0.8.5"}\n', - '200:{"package": {"purl": "pkg:cargo/crossbeam-utils@0.8.5"}}\n', - '200:{"package": {"purl": "pkg:pypi/numpy"}, "version": "0.8.5"}\n', - '200:{"package": {"purl": "pkg:pypi/numpy"}}\n', - '400:{"package": {"purl": "pkg:pypi/numpy@8.24.0"}, "version": "0.8.5"}\n', - '200:{"package": {"purl": "pkg:pypi/numpy@8.24.0"}}\n', - '400:{"version": "0.8.5"}\n', - '400:{}\n'] \ No newline at end of file diff --git a/gcp/api/googleapis b/gcp/api/googleapis deleted file mode 160000 index 300fb478d03..00000000000 --- a/gcp/api/googleapis +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 300fb478d0334baeefd134eba6ea30277477509b diff --git a/gcp/api/importfinding_pb2.py b/gcp/api/importfinding_pb2.py deleted file mode 100644 index 57d8d9ab9cd..00000000000 --- a/gcp/api/importfinding_pb2.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: importfinding.proto -# Protobuf Python Version: 6.31.1 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'importfinding.proto' -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13importfinding.proto\x12\x03osv\x1a\x1fgoogle/protobuf/timestamp.proto\"\xdd\x01\n\rImportFinding\x12\x16\n\x06\x62ug_id\x18\x01 \x01(\tR\x06\x62ug_id\x12\x0e\n\x06source\x18\x02 \x01(\t\x12(\n\x08\x66indings\x18\x03 \x03(\x0e\x32\x16.osv.ImportFindingType\x12:\n\nfirst_seen\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\nfirst_seen\x12>\n\x0clast_attempt\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x0clast_attempt*\x8c\x04\n\x11ImportFindingType\x12\x1c\n\x18IMPORT_FINDING_TYPE_NONE\x10\x00\x12\x1f\n\x1bIMPORT_FINDING_TYPE_DELETED\x10\x01\x12$\n IMPORT_FINDING_TYPE_INVALID_JSON\x10\x02\x12\'\n#IMPORT_FINDING_TYPE_INVALID_PACKAGE\x10\x03\x12$\n IMPORT_FINDING_TYPE_INVALID_PURL\x10\x04\x12\'\n#IMPORT_FINDING_TYPE_INVALID_VERSION\x10\x05\x12&\n\"IMPORT_FINDING_TYPE_INVALID_COMMIT\x10\x06\x12%\n!IMPORT_FINDING_TYPE_INVALID_RANGE\x10\x07\x12&\n\"IMPORT_FINDING_TYPE_INVALID_RECORD\x10\x08\x12\'\n#IMPORT_FINDING_TYPE_INVALID_ALIASES\x10\t\x12(\n$IMPORT_FINDING_TYPE_INVALID_UPSTREAM\x10\n\x12\'\n#IMPORT_FINDING_TYPE_INVALID_RELATED\x10\x0b\x12\'\n#IMPORT_FINDING_TYPE_BAD_ALIASED_CVE\x10\x0c\x42\"Z osv.dev/bindings/go/internal/apib\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'importfinding_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'Z osv.dev/bindings/go/internal/api' - _globals['_IMPORTFINDINGTYPE']._serialized_start=286 - _globals['_IMPORTFINDINGTYPE']._serialized_end=810 - _globals['_IMPORTFINDING']._serialized_start=62 - _globals['_IMPORTFINDING']._serialized_end=283 -# @@protoc_insertion_point(module_scope) diff --git a/gcp/api/importfinding_pb2.pyi b/gcp/api/importfinding_pb2.pyi deleted file mode 100644 index d3dbb9d5502..00000000000 --- a/gcp/api/importfinding_pb2.pyi +++ /dev/null @@ -1,109 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -Copyright 2024 Google LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import abc as _abc -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import timestamp_pb2 as _timestamp_pb2 -from google.protobuf.internal import containers as _containers -from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper -import builtins as _builtins -import sys -import typing as _typing - -if sys.version_info >= (3, 10): - from typing import TypeAlias as _TypeAlias -else: - from typing_extensions import TypeAlias as _TypeAlias - -DESCRIPTOR: _descriptor.FileDescriptor - -class _ImportFindingType: - ValueType = _typing.NewType("ValueType", _builtins.int) - V: _TypeAlias = ValueType # noqa: Y015 - -class _ImportFindingTypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[_ImportFindingType.ValueType], _builtins.type): - DESCRIPTOR: _descriptor.EnumDescriptor - IMPORT_FINDING_TYPE_NONE: _ImportFindingType.ValueType # 0 - IMPORT_FINDING_TYPE_DELETED: _ImportFindingType.ValueType # 1 - IMPORT_FINDING_TYPE_INVALID_JSON: _ImportFindingType.ValueType # 2 - IMPORT_FINDING_TYPE_INVALID_PACKAGE: _ImportFindingType.ValueType # 3 - IMPORT_FINDING_TYPE_INVALID_PURL: _ImportFindingType.ValueType # 4 - IMPORT_FINDING_TYPE_INVALID_VERSION: _ImportFindingType.ValueType # 5 - IMPORT_FINDING_TYPE_INVALID_COMMIT: _ImportFindingType.ValueType # 6 - IMPORT_FINDING_TYPE_INVALID_RANGE: _ImportFindingType.ValueType # 7 - IMPORT_FINDING_TYPE_INVALID_RECORD: _ImportFindingType.ValueType # 8 - IMPORT_FINDING_TYPE_INVALID_ALIASES: _ImportFindingType.ValueType # 9 - IMPORT_FINDING_TYPE_INVALID_UPSTREAM: _ImportFindingType.ValueType # 10 - IMPORT_FINDING_TYPE_INVALID_RELATED: _ImportFindingType.ValueType # 11 - IMPORT_FINDING_TYPE_BAD_ALIASED_CVE: _ImportFindingType.ValueType # 12 - -class ImportFindingType(_ImportFindingType, metaclass=_ImportFindingTypeEnumTypeWrapper): ... - -IMPORT_FINDING_TYPE_NONE: ImportFindingType.ValueType # 0 -IMPORT_FINDING_TYPE_DELETED: ImportFindingType.ValueType # 1 -IMPORT_FINDING_TYPE_INVALID_JSON: ImportFindingType.ValueType # 2 -IMPORT_FINDING_TYPE_INVALID_PACKAGE: ImportFindingType.ValueType # 3 -IMPORT_FINDING_TYPE_INVALID_PURL: ImportFindingType.ValueType # 4 -IMPORT_FINDING_TYPE_INVALID_VERSION: ImportFindingType.ValueType # 5 -IMPORT_FINDING_TYPE_INVALID_COMMIT: ImportFindingType.ValueType # 6 -IMPORT_FINDING_TYPE_INVALID_RANGE: ImportFindingType.ValueType # 7 -IMPORT_FINDING_TYPE_INVALID_RECORD: ImportFindingType.ValueType # 8 -IMPORT_FINDING_TYPE_INVALID_ALIASES: ImportFindingType.ValueType # 9 -IMPORT_FINDING_TYPE_INVALID_UPSTREAM: ImportFindingType.ValueType # 10 -IMPORT_FINDING_TYPE_INVALID_RELATED: ImportFindingType.ValueType # 11 -IMPORT_FINDING_TYPE_BAD_ALIASED_CVE: ImportFindingType.ValueType # 12 -Global___ImportFindingType: _TypeAlias = ImportFindingType # noqa: Y015 - -@_typing.final -class ImportFinding(_message.Message): - """An importfinding entry. - The protobuf representation is *NOT* stable and only used for implementing - the JSON based API. - """ - - DESCRIPTOR: _descriptor.Descriptor - - BUG_ID_FIELD_NUMBER: _builtins.int - SOURCE_FIELD_NUMBER: _builtins.int - FINDINGS_FIELD_NUMBER: _builtins.int - FIRST_SEEN_FIELD_NUMBER: _builtins.int - LAST_ATTEMPT_FIELD_NUMBER: _builtins.int - bug_id: _builtins.str - source: _builtins.str - @_builtins.property - def findings(self) -> _containers.RepeatedScalarFieldContainer[Global___ImportFindingType.ValueType]: ... - @_builtins.property - def first_seen(self) -> _timestamp_pb2.Timestamp: ... - @_builtins.property - def last_attempt(self) -> _timestamp_pb2.Timestamp: ... - def __init__( - self, - *, - bug_id: _builtins.str = ..., - source: _builtins.str = ..., - findings: _abc.Iterable[Global___ImportFindingType.ValueType] | None = ..., - first_seen: _timestamp_pb2.Timestamp | None = ..., - last_attempt: _timestamp_pb2.Timestamp | None = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["first_seen", b"first_seen", "last_attempt", b"last_attempt"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["bug_id", b"bug_id", "findings", b"findings", "first_seen", b"first_seen", "last_attempt", b"last_attempt", "source", b"source"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___ImportFinding: _TypeAlias = ImportFinding # noqa: Y015 diff --git a/gcp/api/importfinding_pb2_grpc.py b/gcp/api/importfinding_pb2_grpc.py deleted file mode 100644 index 8438d315f9f..00000000000 --- a/gcp/api/importfinding_pb2_grpc.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc -import warnings - - -GRPC_GENERATED_VERSION = '1.75.0' -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f'The grpc package installed is at version {GRPC_VERSION},' - + f' but the generated code in importfinding_pb2_grpc.py depends on' - + f' grpcio>={GRPC_GENERATED_VERSION}.' - + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' - + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - ) diff --git a/gcp/api/integration_tests.py b/gcp/api/integration_tests.py deleted file mode 100644 index 8fc913f6eed..00000000000 --- a/gcp/api/integration_tests.py +++ /dev/null @@ -1,1025 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""API server integration tests.""" - -import copy -import functools -import json -import itertools -import os -import sys -import subprocess -import time -import unittest - -import requests - -import test_server -from osv import tests - -_PORT = 8080 -_TIMEOUT = 15 # Timeout for HTTP(S) requests -_LONG_TESTS = os.getenv('LONG_TESTS') -_TEST_DATA_DIR = 'fixtures' -_BASE_QUERY = '/v1/query' - - -def _api(): - if os.getenv('CLOUDBUILD'): - host = test_server.get_cloudbuild_esp_host() - else: - host = 'localhost' - - return f'http://{host}:{_PORT}' - - -class IntegrationTests(unittest.TestCase, - tests.ExpectationTest(_TEST_DATA_DIR)): - """Server integration tests.""" - _VULN_744 = { - 'published': '2020-07-04T00:00:01.948828Z', - 'schema_version': '1.7.3', - 'affected': [{ - 'database_specific': { - 'source': 'https://github.com/google/oss-fuzz-vulns/' - 'blob/main/vulns/mruby/OSV-2020-744.yaml' - }, - 'ecosystem_specific': { - 'severity': 'HIGH' - }, - 'package': { - 'ecosystem': 'OSS-Fuzz', - 'name': 'mruby', - 'purl': 'pkg:generic/mruby' - }, - 'ranges': [{ - 'events': [{ - 'introduced': '9cdf439db52b66447b4e37c61179d54fad6c8f33' - }, { - 'fixed': '97319697c8f9f6ff27b32589947e1918e3015503' - }], - 'repo': 'https://github.com/mruby/mruby', - 'type': 'GIT' - }], - 'versions': ['2.1.2', '2.1.2-rc', '2.1.2-rc2'] - }], - 'details': 'OSS-Fuzz report: ' - 'https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23801\n' - '\n' - '```\n' - 'Crash type: Heap-double-free\n' - 'Crash state:\n' - 'mrb_default_allocf\n' - 'mrb_free\n' - 'obj_free\n```\n', - 'id': 'OSV-2020-744', - 'references': [{ - 'type': 'REPORT', - 'url': 'https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23801', - }], - 'summary': 'Heap-double-free in mrb_default_allocf', - } - - def _get(self, vuln_id): - """Get a vulnerability.""" - response = requests.get(_api() + '/v1/vulns/' + vuln_id, timeout=_TIMEOUT) - return response.json() - - def setUp(self): - self.maxDiff = None # pylint: disable=invalid-name - - def assert_vuln_equal(self, expected, actual): - """Assert that the vulnerability is equal.""" - self.remove_modified(expected) - self.remove_modified(actual) - self.assertDictEqual(expected, actual) - - def assert_results_equal(self, expected, actual): - """Assert that results are equal. - - Args: - expected: dictionary representation of the expected vulnerability. - actual: dictionary representation of the actual vulnerability. - """ - # Single query results. - for vuln in expected.get('vulns', []): - self.remove_modified(vuln) - - for vuln in actual.get('vulns', []): - self.remove_modified(vuln) - - # Batch query results. - for batch_result in actual.get('results', []): - for vuln in batch_result.get('vulns', {}): - # Ensure that batch queries include the timestamp. - self.remove_modified(vuln, check_exists=True) - - self.assertDictEqual(expected, actual) - - def remove_modified(self, vuln, check_exists=False): - """Remove lastModified for comparison.""" - if 'modified' in vuln: - del vuln['modified'] - elif check_exists: - raise ValueError('Missing modified timestamp') - - def test_get(self): - """Test getting a vulnerability.""" - response = requests.get(_api() + '/v1/vulns/OSV-2020-744', timeout=_TIMEOUT) - self.assert_vuln_equal(self._VULN_744, response.json()) - - def test_get_with_multiple(self): - """Test getting a vulnerability with multiple packages.""" - go_2020_0015 = self._get('GO-2020-0015') - response = requests.get(_api() + '/v1/vulns/GO-2020-0015', timeout=_TIMEOUT) - self.assert_vuln_equal(go_2020_0015, response.json()) - - def test_query_commit(self): - """Test querying by commit.""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'commit': '60e572dbf7b4ded66b488f54773f66aaf6184321', - }), - timeout=_TIMEOUT) - osv_2023_890 = self._get('OSV-2023-890') - cve_2024_2002 = self._get('CVE-2024-2002') - - self.assert_results_equal({'vulns': [cve_2024_2002, osv_2023_890]}, - response.json()) - - def test_query_version(self): - """Test querying by version.""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '2.1.2-rc', - 'package': { - 'name': 'mruby', - 'ecosystem': 'OSS-Fuzz', - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': [self._VULN_744]}, response.json()) - - # NOTE(michaelkedar): version queries without ecosystem specified is not - # officially supported. Since our change to matching logic, this test - # would now return >50 vulnerabilities across 4 ecosystems. - # response = requests.post( - # _api() + _BASE_QUERY, - # data=json.dumps({ - # 'version': '2.1.2-rc', - # 'package': { - # 'name': 'mruby', - # } - # }), - # timeout=_TIMEOUT) - - # self.assert_results_equal({'vulns': [self._VULN_744]}, response.json()) - # self.assertEqual( - # response.text, - # '{"code":3,"message":"Ecosystem not specified"}') - - def test_query_debian(self): - """Test querying Debian with sub ecosystem versions""" - dsa_2665_1 = self._get('DSA-710-1') - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.0.2-1', - 'package': { - 'name': 'gtkhtml', - 'ecosystem': 'Debian', - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': [dsa_2665_1]}, response.json()) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.0.2-1', - 'package': { - 'name': 'gtkhtml', - 'ecosystem': 'Debian:3.0', - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': [dsa_2665_1]}, response.json()) - - # The vulnerbility does not exist in 4.0 release, so this should return - # with nothing - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.0.2-1', - 'package': { - 'name': 'gtkhtml', - 'ecosystem': 'Debian:4.0', - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({}, response.json()) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.0.2-1', - 'package': { - 'name': 'gtkhtml', - 'ecosystem': 'Debian:9', - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({}, response.json()) - - def test_query_semver(self): - """Test queries by SemVer.""" - - package = 'github.com/gin-gonic/gin' - ecosystem = 'Go' - go_2020_0001 = self._get('GO-2020-0001') - go_2021_0052 = self._get('GO-2021-0052') - ghsa_6vm3_jj99_7229 = self._get('GHSA-6vm3-jj99-7229') - ghsa_869c_j7wc_8jqv = self._get('GHSA-869c-j7wc-8jqv') - ghsa_h395_qcrw_5vmq = self._get('GHSA-h395-qcrw-5vmq') - ghsa_3vp4_m3rf_835h = self._get('GHSA-3vp4-m3rf-835h') - - expected_vulns = [ - ghsa_6vm3_jj99_7229, - ghsa_869c_j7wc_8jqv, - go_2020_0001, - ghsa_h395_qcrw_5vmq, - go_2021_0052, - ghsa_3vp4_m3rf_835h, - ] - expected_vulns.sort(key=lambda x: x['id']) - - # Test that a SemVer (believed to be vulnerable) version and an ecosystem - # returns expected vulnerabilities. - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.1.4', - 'package': { - 'name': package, - 'ecosystem': ecosystem, - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': expected_vulns}, response.json()) - - # Test that a SemVer with a (believed to be vulnerable) version and no - # ecosystem returns expected vulnerabilities to test the fallback logic to - # try semver matching in the case that an ecosystem isn't specified. - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.1.4', - 'package': { - 'name': package, - 'ecosystem': 'Go' - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': expected_vulns}, response.json()) - - def test_query_comparing_version(self): - """Test queries by comparing versions.""" - - package = 'linux-firmware' - ecosystem = 'AlmaLinux:8' - alsa_2023_7109 = self._get('ALSA-2023:7109') - alsa_2024_3178 = self._get('ALSA-2024:3178') - alsa_2024_4262 = self._get('ALSA-2024:4262') - alsa_2024_7481 = self._get('ALSA-2024:7481') - - expected_vulns = [ - alsa_2023_7109, - alsa_2024_3178, - alsa_2024_4262, - alsa_2024_7481, - ] - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '20230404-117.git2e92a49f.el8_8.alma.1', - 'package': { - 'name': package, - 'ecosystem': ecosystem, - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': expected_vulns}, response.json()) - - # Tests empty response - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '99999999-117.git2e92a49f.el8_8.alma.1', - 'package': { - 'name': package, - 'ecosystem': ecosystem, - } - }), - timeout=_TIMEOUT) - self.assertEqual(0, len(response.json())) - - def test_malicious_package_matching(self): - """"Test malicious package query""" - # Test matching by affected ranges - mal_2022_7426 = self._get('MAL-2022-7426') - - expected_vulns = [ - mal_2022_7426, - ] - - package = 'pymocks' - ecosystem = 'PyPI' - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '0.0.1', - 'package': { - 'name': package, - 'ecosystem': ecosystem, - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': expected_vulns}, response.json()) - - # Test matching by affected versions - mal_2024_4618 = self._get('MAL-2024-4618') - - expected_vulns = [ - mal_2024_4618, - ] - - package = 'psbuiId' - ecosystem = 'NuGet' - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.1.1-beta', - 'package': { - 'name': package, - 'ecosystem': ecosystem, - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': expected_vulns}, response.json()) - - def test_query_invalid_ecosystem(self): - """Test a query with an invalid ecosystem fails validation.""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.0.0', - 'package': { - 'name': 'a_package_name_of_no_consequence', - 'ecosystem': 'Bogus', - } - }), - timeout=_TIMEOUT) - - self.assert_results_equal({ - 'code': 3, - 'message': 'invalid ecosystem' - }, response.json()) - - def test_query_unknown_purl_invalid_semver(self): - """Test an unknown purl query with an invalid semver""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'package': { - 'purl': - 'pkg:golang/github.com/' + - 'tianon/gosu@(devel)?package-id=656546dcfdff37ca', - } - }), - timeout=_TIMEOUT) - - self.assert_results_equal({}, response.json()) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'package': { - 'purl': - 'pkg:yeet/github.com/' + - 'tianon/gosu@(devel)?package-id=656546dcfdff37ca', - } - }), - timeout=_TIMEOUT) - - self.assert_results_equal({}, response.json()) - - def test_query_semver_no_vulns(self): - """Test queries by SemVer with no vulnerabilities.""" - package = 'github.com/justinas/nosurf' - ecosystem = 'Go' - - # Test that a SemVer with a (believed to be non-vulnerable) version and an - # ecosystem returns no vulnerabilities. - # (This version does not exist) - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '1.2.1', - 'package': { - 'name': package, - 'ecosystem': ecosystem, - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({}, response.json()) - - def test_query_semver_multiple_package(self): - """Test query by SemVer (with multiple packages).""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '2.4.0', - 'package': { - 'name': 'gopkg.in/yaml.v2', - 'ecosystem': 'Go', - } - }), - timeout=_TIMEOUT) - - self.assert_results_equal({}, response.json()) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '2.4.0', - 'package': { - 'name': 'github.com/go-yaml/yaml', - 'ecosystem': 'Go', - } - }), - timeout=_TIMEOUT) - - response_json = response.json() - self.assertCountEqual(['GO-2021-0061', 'GO-2020-0036'], - [vuln['id'] for vuln in response_json['vulns']]) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '7.1.1', - 'package': { - 'name': 'ws', - 'ecosystem': 'npm', - } - }), - timeout=_TIMEOUT) - - response_json = response.json() - self.assertEqual(3, len(response_json['vulns'])) - self.assertCountEqual([ - 'GHSA-6fc8-4gx4-v693', - 'GHSA-3h5v-q93c-6h6q', - 'GHSA-96hv-2xvq-fx4p', - ], [vuln['id'] for vuln in response_json['vulns']]) - - def test_query_purl(self): - """Test querying by PURL.""" - expected = [ - self._get('GHSA-qc84-gqf4-9926'), - self._get('RUSTSEC-2022-0041') - ] - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'version': '0.8.6', - 'package': { - 'purl': 'pkg:cargo/crossbeam-utils', - } - }), - timeout=_TIMEOUT) - - self.assert_results_equal({'vulns': expected}, response.json()) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps( - {'package': { - 'purl': 'pkg:cargo/crossbeam-utils@0.8.6', - }}), - timeout=_TIMEOUT) - - self.assert_results_equal({'vulns': expected}, response.json()) - - another_expected = [self._get('GHSA-j8xg-fqg3-53r7')] - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({'package': { - 'purl': 'pkg:npm/word-wrap@1.2.2', - }}), - timeout=_TIMEOUT) - - self.assert_results_equal({'vulns': another_expected}, response.json()) - - expected_deb = [ - self._get('DEBIAN-CVE-2018-25047'), - self._get('DEBIAN-CVE-2023-28447'), - self._get('DEBIAN-CVE-2024-35226'), - self._get('DEBIAN-CVE-2026-62992'), - self._get('DSA-5830-1'), - ] - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps( - {'package': { - 'purl': 'pkg:deb/debian/smarty4@4.1.1-1', - }}), - timeout=_TIMEOUT) - - self.assert_results_equal({'vulns': expected_deb}, response.json()) - - # Source arch should return the same as above - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'package': { - 'purl': 'pkg:deb/debian/smarty4@4.1.1-1?arch=source', - } - }), - timeout=_TIMEOUT) - - self.assert_results_equal({'vulns': expected_deb}, response.json()) - - # A non source arch should also return the same item - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps( - {'package': { - 'purl': 'pkg:deb/debian/smarty4@4.1.1-1?arch=x64', - }}), - timeout=_TIMEOUT) - - self.assert_results_equal({'vulns': expected_deb}, response.json()) - - # A non arch qualifier should be ignored - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'package': { - 'purl': ('pkg:deb/debian/smarty4@4.1.1-1?' - 'randomqualifier=1234'), - } - }), - timeout=_TIMEOUT) - self.assert_results_equal({'vulns': expected_deb}, response.json()) - - def test_query_purl_with_version_trailing_zeroes(self): - """Test purl with trailing zeroes in version.""" - expected = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({'package': { - 'purl': 'pkg:pypi/cryptography@3.1', - }}), - timeout=_TIMEOUT) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({'package': { - 'purl': 'pkg:pypi/cryptography@3.1.0', - }}), - timeout=_TIMEOUT) - - self.assert_results_equal(expected.json(), response.json()) - - expected = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({'package': { - 'purl': 'pkg:nuget/SkiaSharp@2.80.3', - }}), - timeout=_TIMEOUT) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({'package': { - 'purl': 'pkg:nuget/SkiaSharp@2.80.3.0', - }}), - timeout=_TIMEOUT) - - self.assert_results_equal(expected.json(), response.json()) - - expected = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({'package': { - 'purl': 'pkg:pypi/django@4.2', - }}), - timeout=_TIMEOUT) - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({'package': { - 'purl': 'pkg:pypi/django@4.2.0', - }}), - timeout=_TIMEOUT) - - self.assert_results_equal(expected.json(), response.json()) - - def test_query_with_redundant_ecosystem(self): - """Test purl with redundant ecosystem raises error""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - "package": { - "ecosystem": "PyPI", - "purl": "pkg:pypi/mlflow@0.4.0", - } - }), - timeout=_TIMEOUT) - self.assert_results_equal( - { - 'code': 3, - 'message': 'ecosystem specified in a PURL query' - }, response.json()) - - def test_query_with_redundant_version(self): - """Test purl with redundant version raises error""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - "version": "0.4.0", - "package": { - "purl": "pkg:pypi/mlflow@0.4.0", - } - }), - timeout=_TIMEOUT) - self.assert_results_equal( - { - 'code': 3, - 'message': 'version specified in params and PURL query' - }, response.json()) - - def test_query_with_redundant_package_name(self): - """Test purl with redundant name raises error""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps( - {"package": { - "name": "mlflow", - "purl": "pkg:pypi/mlflow@0.4.0", - }}), - timeout=_TIMEOUT) - self.assert_results_equal( - { - 'code': 3, - 'message': 'name specified in a PURL query' - }, response.json()) - - def test_get_vuln_by_alias_not_in_db(self): - """ Test search by ID for a vuln not in db, but alias is""" - response = requests.get( - _api() + '/v1/vulns/SNYK-JS-ADMINHUBPAGEEMAILS-5666437', - timeout=_TIMEOUT) - self.assert_results_equal( - { - 'code': 5, - 'message': - 'Vulnerability not found, but the following aliases were: ' - 'MAL-2024-2291' - }, response.json()) - - def test_query_batch(self): - """Test batch query.""" - response = requests.post( - _api() + '/v1/querybatch', - data=json.dumps({ - 'queries': [{ - 'version': '0.8.6', - 'package': { - 'purl': 'pkg:cargo/crossbeam-utils', - } - }, { - 'version': '2.4.0', - 'package': { - 'name': 'gopkg.in/yaml.v2', - 'ecosystem': 'Go', - } - }, { - 'commit': '233cb49903fa17637bd51f4a16b4ca61e0750f24', - }], - }), - timeout=_TIMEOUT) - - self.assert_results_equal( - { - 'results': [ - { - 'vulns': [{ - 'id': 'GHSA-qc84-gqf4-9926', - }, { - 'id': 'RUSTSEC-2022-0041', - }] - }, - {}, - { - 'vulns': [{ - 'id': 'CVE-2020-36401', - }, { - 'id': 'CVE-2021-4110', - }, { - 'id': 'CVE-2021-4188', - }, { - 'id': 'CVE-2021-46023', - }, { - 'id': 'CVE-2022-0080', - }, { - 'id': 'CVE-2022-0240', - }, { - 'id': 'CVE-2022-0326', - }, { - 'id': 'CVE-2022-0481', - }, { - 'id': 'CVE-2022-0525', - }, { - 'id': 'CVE-2022-0570', - }, { - 'id': 'CVE-2022-0614', - }, { - 'id': 'CVE-2022-0623', - }, { - 'id': 'CVE-2022-0630', - }, { - 'id': 'CVE-2022-0631', - }, { - 'id': 'CVE-2022-0632', - }, { - 'id': 'CVE-2022-0717', - }, { - 'id': 'CVE-2022-0890', - }, { - 'id': 'CVE-2022-1071', - }, { - 'id': 'CVE-2022-1106', - }, { - 'id': 'CVE-2022-1201', - }, { - 'id': 'CVE-2022-1212', - }, { - 'id': 'CVE-2022-1276', - }, { - 'id': 'CVE-2022-1286', - }, { - 'id': 'CVE-2022-1427', - }, { - 'id': 'CVE-2022-1934', - }, { - 'id': 'CVE-2025-12875', - }, { - 'id': 'CVE-2025-13120', - }, { - 'id': 'CVE-2025-7207', - }, { - 'id': 'CVE-2026-1979', - }, { - 'id': 'OSV-2020-744', - }] - }, - ] - }, response.json()) - - @unittest.skipIf( - os.getenv('LOW_MAX_THRESH', '0') != '1', "Run this test locally with " + - "MAX_VULN_LISTED_PRE_EXCEEDED at a lower value (around 10)") - def test_query_pagination(self): - """Test query by package with pagination.""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps( - {'package': { - 'ecosystem': 'PyPI', - 'name': 'tensorflow', - }}), - timeout=_TIMEOUT) - - result = response.json() - vulns_first = set(v['id'] for v in result['vulns']) - self.assertIn('next_page_token', result) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'package': { - 'ecosystem': 'PyPI', - 'name': 'tensorflow', - }, - 'page_token': result['next_page_token'], - }), - timeout=_TIMEOUT) - - result = response.json() - vulns_second = set(v['id'] for v in result['vulns']) - - self.assertEqual(set(), vulns_first.intersection(vulns_second)) - - @unittest.skipIf( - os.getenv('LOW_MAX_THRESH', '0') != '1', "Run this test locally with " + - "MAX_VULN_LISTED_PRE_EXCEEDED at a lower value (around 10)") - def test_query_pagination_no_ecosystem(self): - """Test query with pagination but no ecosystem.""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'package': { - 'name': 'django', - }, - # Test with a version that is ambiguous whether it - # belongs to semver or generic version - 'version': '5.0.1', - }), - timeout=_TIMEOUT) - - result = response.json() - vulns_first = set(v['id'] for v in result['vulns']) - self.assertIn('next_page_token', result) - self.assertTrue(str.startswith(result['next_page_token'], '2:')) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'package': { - 'name': 'django', - }, - 'version': '5.0.1', - 'page_token': result['next_page_token'], - }), - timeout=_TIMEOUT) - - result = response.json() - vulns_second = set(v['id'] for v in result['vulns']) - - self.assertIn('next_page_token', result) - # There is not enough django vulns to simultaneously test multiple pages, - # and pass the other tests - # self.assertTrue(str.startswith(result['next_page_token'], '1:')) - self.assertEqual(set(), vulns_first.intersection(vulns_second)) - - @unittest.skipIf( - os.getenv('LOW_MAX_THRESH', '0') != '1', "Run this test locally with " + - "MAX_VULN_LISTED_PRE_EXCEEDED at a lower value (around 10)") - def test_query_package_purl_paging(self): - """Test query by package (purl).""" - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({'package': { - 'purl': 'pkg:pypi/tensorflow', - }}), - timeout=_TIMEOUT) - result = response.json() - vulns_first = set(v['id'] for v in result['vulns']) - self.assertIn('next_page_token', result) - - response = requests.post( - _api() + _BASE_QUERY, - data=json.dumps({ - 'package': { - 'purl': 'pkg:pypi/tensorflow', - }, - 'page_token': result['next_page_token'], - }), - timeout=_TIMEOUT) - - result = response.json() - vulns_second = set(v['id'] for v in result['vulns']) - - self.assertEqual(set(), vulns_first.intersection(vulns_second)) - - @unittest.skipUnless( - _LONG_TESTS, "Takes around 45 seconds running locally," + - "enable when making a big change") - def test_all_possible_queries(self): - """Test all combinations of valid and invalid queries""" - semver_package = {'package': {'purl': 'pkg:cargo/crossbeam-utils'}} - - semver_package_with_version = { - 'package': { - 'purl': 'pkg:cargo/crossbeam-utils@0.8.5' - } - } - - nonsemver_package = {'package': {'purl': 'pkg:pypi/numpy'}} - - nonsemver_package_with_version = { - 'package': { - 'purl': 'pkg:pypi/numpy@8.24.0' - } - } - - pkg_ecosystem = [{'package': {'ecosystem': 'crates.io'}}, {}] - - pkg_name = [{ - 'package': { - 'name': 'crossbeam-utils' - } - }, { - 'package': { - 'name': 'numpy' - } - }, {}] - - pkg_version = [{'version': '0.8.5'}, {}] - - commit = [{'commit': 'd374094d8c49b6b7d288f307e11217ec5a502391'}, {}] - - purl_fields = [ - semver_package, semver_package_with_version, nonsemver_package, - nonsemver_package_with_version, {} - ] - - product = itertools.product(purl_fields, commit, pkg_version, pkg_name, - pkg_ecosystem) - - # itertools.product will produce duplicates, use set over the json to de-dup - combined_product = set() - for elem in product: - # Deep copy elem required since merge merges - # in-place to the first argument - elem = copy.deepcopy(elem) - functools.reduce(merge, elem) - combined_product.add(json.dumps(elem[0], sort_keys=True)) - - self.assertEqual(len(combined_product), 120) - - actual_lines = [] - for query in sorted(list(combined_product)): - response = requests.post( - _api() + _BASE_QUERY, data=query, timeout=_TIMEOUT) - - # No possible queries should cause a server error - self.assertLess(response.status_code, 500) - actual_lines.append(str(response.status_code) + ':' + query + '\n') - - self.expect_lines_equal('api_query_response', actual_lines) - - -# From: https://stackoverflow.com/questions/7204805/how-to-merge-dictionaries-of-dictionaries # pylint: disable=line-too-long -def merge(a: dict, b: dict, path=None): - """Merge two nested dictionaries""" - if path is None: - path = [] - - for key in b: - if key in a: - if isinstance(a[key], dict) and isinstance(b[key], dict): - merge(a[key], b[key], path + [str(key)]) - elif a[key] != b[key]: - # pylint: disable=broad-exception-raised - raise Exception('Conflict at ' + '.'.join(path + [str(key)])) - else: - a[key] = b[key] - return a - - -def print_logs(filename): - """Print logs.""" - if not os.path.exists(filename): - return - - print(filename + ':') - with open(filename) as f: - print(f.read()) - - -if __name__ == '__main__': - if len(sys.argv) < 2: - print( - f'Usage: {sys.argv[0]} path/to/credential.json [...optional specific tests]' - ) - sys.exit(1) - - subprocess.run( - ['docker', 'pull', 'gcr.io/endpoints-release/endpoints-runtime:2'], - check=True) - - credential_path = sys.argv.pop(1) - server = test_server.start(credential_path, port=_PORT) - time.sleep(10) - - try: - unittest.main(argv=sys.argv) - finally: - server.stop() diff --git a/gcp/api/osv_service_v1_pb2.py b/gcp/api/osv_service_v1_pb2.py deleted file mode 100644 index bf69db4a79a..00000000000 --- a/gcp/api/osv_service_v1_pb2.py +++ /dev/null @@ -1,84 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: osv_service_v1.proto -# Protobuf Python Version: 6.31.1 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'osv_service_v1.proto' -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import vulnerability_pb2 as vulnerability__pb2 -import importfinding_pb2 as importfinding__pb2 -from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14osv_service_v1.proto\x12\x06osv.v1\x1a\x13vulnerability.proto\x1a\x13importfinding.proto\x1a\x1cgoogle/api/annotations.proto\"`\n\x11VulnerabilityList\x12!\n\x05vulns\x18\x01 \x03(\x0b\x32\x12.osv.Vulnerability\x12(\n\x0fnext_page_token\x18\x02 \x01(\tR\x0fnext_page_token\"D\n\x16\x42\x61tchVulnerabilityList\x12*\n\x07results\x18\x01 \x03(\x0b\x32\x19.osv.v1.VulnerabilityList\"t\n\x05Query\x12\x10\n\x06\x63ommit\x18\x01 \x01(\tH\x00\x12\x11\n\x07version\x18\x02 \x01(\tH\x00\x12\x1d\n\x07package\x18\x04 \x01(\x0b\x32\x0c.osv.Package\x12\x1e\n\npage_token\x18\x05 \x01(\tR\npage_tokenB\x07\n\x05param\",\n\nBatchQuery\x12\x1e\n\x07queries\x18\x01 \x03(\x0b\x32\r.osv.v1.Query\"#\n\x15GetVulnByIdParameters\x12\n\n\x02id\x18\x01 \x01(\t\"7\n\x17QueryAffectedParameters\x12\x1c\n\x05query\x18\x01 \x01(\x0b\x32\r.osv.v1.Query\"A\n\x1cQueryAffectedBatchParameters\x12!\n\x05query\x18\x01 \x01(\x0b\x32\x12.osv.v1.BatchQuery\"A\n\x1a\x44\x65termineVersionParameters\x12#\n\x05query\x18\x01 \x01(\x0b\x32\x14.osv.v1.VersionQuery\"*\n\x18ImportFindingsParameters\x12\x0e\n\x06source\x18\x01 \x01(\t\"P\n\x0cVersionQuery\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x0b\x66ile_hashes\x18\x02 \x03(\x0b\x32\x10.osv.v1.FileHashR\x0b\x66ile_hashes\"\x84\x01\n\x08\x46ileHash\x12\x1c\n\tfile_path\x18\x01 \x01(\tR\tfile_path\x12\x37\n\thash_type\x18\x02 \x01(\x0e\x32\x19.osv.v1.FileHash.HashTypeR\thash_type\x12\x0c\n\x04hash\x18\x03 \x01(\x0c\"\x13\n\x08HashType\x12\x07\n\x03MD5\x10\x00\"9\n\x10VersionMatchList\x12%\n\x07matches\x18\x01 \x03(\x0b\x32\x14.osv.v1.VersionMatch\"Q\n\x11ImportFindingList\x12<\n\x0finvalid_records\x18\x01 \x03(\x0b\x32\x12.osv.ImportFindingR\x0finvalid_records\"\x83\x02\n\x0cVersionMatch\x12\r\n\x05score\x18\x01 \x01(\x01\x12\x37\n\trepo_info\x18\x02 \x01(\x0b\x32$.osv.v1.VersionRepositoryInformation\x12\x34\n\x0eosv_identifier\x18\x03 \x01(\x0b\x32\x0c.osv.PackageR\x0eosv_identifier\x12\r\n\x05\x63pe23\x18\x05 \x01(\t\x12\x32\n\x14minimum_file_matches\x18\x06 \x01(\x03R\x14minimum_file_matches\x12\x32\n\x14\x65stimated_diff_files\x18\x07 \x01(\x03R\x14\x65stimated_diff_files\"\xc0\x01\n\x1cVersionRepositoryInformation\x12;\n\x04type\x18\x01 \x01(\x0e\x32-.osv.v1.VersionRepositoryInformation.RepoType\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x0b\n\x03tag\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x06 \x01(\t\"$\n\x08RepoType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x07\n\x03GIT\x10\x01\x32\xc5\x04\n\x03OSV\x12X\n\x0bGetVulnById\x12\x1d.osv.v1.GetVulnByIdParameters\x1a\x12.osv.Vulnerability\"\x16\x82\xd3\xe4\x93\x02\x10\x12\x0e/v1/vulns/{id}\x12\x65\n\rQueryAffected\x12\x1f.osv.v1.QueryAffectedParameters\x1a\x19.osv.v1.VulnerabilityList\"\x18\x82\xd3\xe4\x93\x02\x12\"\t/v1/query:\x05query\x12y\n\x12QueryAffectedBatch\x12$.osv.v1.QueryAffectedBatchParameters\x1a\x1e.osv.v1.BatchVulnerabilityList\"\x1d\x82\xd3\xe4\x93\x02\x17\"\x0e/v1/querybatch:\x05query\x12\x81\x01\n\x10\x44\x65termineVersion\x12\".osv.v1.DetermineVersionParameters\x1a\x18.osv.v1.VersionMatchList\"/\x82\xd3\xe4\x93\x02)\" /v1experimental/determineversion:\x05query\x12~\n\x0eImportFindings\x12 .osv.v1.ImportFindingsParameters\x1a\x19.osv.v1.ImportFindingList\"/\x82\xd3\xe4\x93\x02)\x12\'/v1experimental/importfindings/{source}B\"Z osv.dev/bindings/go/internal/apib\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'osv_service_v1_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'Z osv.dev/bindings/go/internal/api' - _globals['_OSV'].methods_by_name['GetVulnById']._loaded_options = None - _globals['_OSV'].methods_by_name['GetVulnById']._serialized_options = b'\202\323\344\223\002\020\022\016/v1/vulns/{id}' - _globals['_OSV'].methods_by_name['QueryAffected']._loaded_options = None - _globals['_OSV'].methods_by_name['QueryAffected']._serialized_options = b'\202\323\344\223\002\022\"\t/v1/query:\005query' - _globals['_OSV'].methods_by_name['QueryAffectedBatch']._loaded_options = None - _globals['_OSV'].methods_by_name['QueryAffectedBatch']._serialized_options = b'\202\323\344\223\002\027\"\016/v1/querybatch:\005query' - _globals['_OSV'].methods_by_name['DetermineVersion']._loaded_options = None - _globals['_OSV'].methods_by_name['DetermineVersion']._serialized_options = b'\202\323\344\223\002)\" /v1experimental/determineversion:\005query' - _globals['_OSV'].methods_by_name['ImportFindings']._loaded_options = None - _globals['_OSV'].methods_by_name['ImportFindings']._serialized_options = b'\202\323\344\223\002)\022\'/v1experimental/importfindings/{source}' - _globals['_VULNERABILITYLIST']._serialized_start=104 - _globals['_VULNERABILITYLIST']._serialized_end=200 - _globals['_BATCHVULNERABILITYLIST']._serialized_start=202 - _globals['_BATCHVULNERABILITYLIST']._serialized_end=270 - _globals['_QUERY']._serialized_start=272 - _globals['_QUERY']._serialized_end=388 - _globals['_BATCHQUERY']._serialized_start=390 - _globals['_BATCHQUERY']._serialized_end=434 - _globals['_GETVULNBYIDPARAMETERS']._serialized_start=436 - _globals['_GETVULNBYIDPARAMETERS']._serialized_end=471 - _globals['_QUERYAFFECTEDPARAMETERS']._serialized_start=473 - _globals['_QUERYAFFECTEDPARAMETERS']._serialized_end=528 - _globals['_QUERYAFFECTEDBATCHPARAMETERS']._serialized_start=530 - _globals['_QUERYAFFECTEDBATCHPARAMETERS']._serialized_end=595 - _globals['_DETERMINEVERSIONPARAMETERS']._serialized_start=597 - _globals['_DETERMINEVERSIONPARAMETERS']._serialized_end=662 - _globals['_IMPORTFINDINGSPARAMETERS']._serialized_start=664 - _globals['_IMPORTFINDINGSPARAMETERS']._serialized_end=706 - _globals['_VERSIONQUERY']._serialized_start=708 - _globals['_VERSIONQUERY']._serialized_end=788 - _globals['_FILEHASH']._serialized_start=791 - _globals['_FILEHASH']._serialized_end=923 - _globals['_FILEHASH_HASHTYPE']._serialized_start=904 - _globals['_FILEHASH_HASHTYPE']._serialized_end=923 - _globals['_VERSIONMATCHLIST']._serialized_start=925 - _globals['_VERSIONMATCHLIST']._serialized_end=982 - _globals['_IMPORTFINDINGLIST']._serialized_start=984 - _globals['_IMPORTFINDINGLIST']._serialized_end=1065 - _globals['_VERSIONMATCH']._serialized_start=1068 - _globals['_VERSIONMATCH']._serialized_end=1327 - _globals['_VERSIONREPOSITORYINFORMATION']._serialized_start=1330 - _globals['_VERSIONREPOSITORYINFORMATION']._serialized_end=1522 - _globals['_VERSIONREPOSITORYINFORMATION_REPOTYPE']._serialized_start=1486 - _globals['_VERSIONREPOSITORYINFORMATION_REPOTYPE']._serialized_end=1522 - _globals['_OSV']._serialized_start=1525 - _globals['_OSV']._serialized_end=2106 -# @@protoc_insertion_point(module_scope) diff --git a/gcp/api/osv_service_v1_pb2.pyi b/gcp/api/osv_service_v1_pb2.pyi deleted file mode 100644 index e10d7ea35c0..00000000000 --- a/gcp/api/osv_service_v1_pb2.pyi +++ /dev/null @@ -1,426 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -Copyright 2021 Google LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import abc as _abc -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf.internal import containers as _containers -from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper -import builtins as _builtins -import importfinding_pb2 as _importfinding_pb2 -import sys -import typing as _typing -import vulnerability_pb2 as _vulnerability_pb2 - -if sys.version_info >= (3, 10): - from typing import TypeAlias as _TypeAlias -else: - from typing_extensions import TypeAlias as _TypeAlias - -DESCRIPTOR: _descriptor.FileDescriptor - -@_typing.final -class VulnerabilityList(_message.Message): - """A list of Vulnerability entries.""" - - DESCRIPTOR: _descriptor.Descriptor - - VULNS_FIELD_NUMBER: _builtins.int - NEXT_PAGE_TOKEN_FIELD_NUMBER: _builtins.int - next_page_token: _builtins.str - @_builtins.property - def vulns(self) -> _containers.RepeatedCompositeFieldContainer[_vulnerability_pb2.Vulnerability]: ... - def __init__( - self, - *, - vulns: _abc.Iterable[_vulnerability_pb2.Vulnerability] | None = ..., - next_page_token: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["next_page_token", b"next_page_token", "vulns", b"vulns"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___VulnerabilityList: _TypeAlias = VulnerabilityList # noqa: Y015 - -@_typing.final -class BatchVulnerabilityList(_message.Message): - """Batched lists of Vulnerability entries.""" - - DESCRIPTOR: _descriptor.Descriptor - - RESULTS_FIELD_NUMBER: _builtins.int - @_builtins.property - def results(self) -> _containers.RepeatedCompositeFieldContainer[Global___VulnerabilityList]: ... - def __init__( - self, - *, - results: _abc.Iterable[Global___VulnerabilityList] | None = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["results", b"results"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___BatchVulnerabilityList: _TypeAlias = BatchVulnerabilityList # noqa: Y015 - -@_typing.final -class Query(_message.Message): - """Query format.""" - - DESCRIPTOR: _descriptor.Descriptor - - COMMIT_FIELD_NUMBER: _builtins.int - VERSION_FIELD_NUMBER: _builtins.int - PACKAGE_FIELD_NUMBER: _builtins.int - PAGE_TOKEN_FIELD_NUMBER: _builtins.int - commit: _builtins.str - """The commit hash to query for. If specified, `version` should not be set.""" - version: _builtins.str - """The version string to query for. A fuzzy match is done against upstream - versions. If specified, `commit` should not be set. - """ - page_token: _builtins.str - @_builtins.property - def package(self) -> _vulnerability_pb2.Package: - """The package to query against. When a `commit` hash is given, this is - optional. - """ - - def __init__( - self, - *, - commit: _builtins.str = ..., - version: _builtins.str = ..., - package: _vulnerability_pb2.Package | None = ..., - page_token: _builtins.str = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["commit", b"commit", "package", b"package", "param", b"param", "version", b"version"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["commit", b"commit", "package", b"package", "page_token", b"page_token", "param", b"param", "version", b"version"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - _WhichOneofReturnType_param: _TypeAlias = _typing.Literal["commit", "version"] # noqa: Y015 - _WhichOneofArgType_param: _TypeAlias = _typing.Literal["param", b"param"] # noqa: Y015 - def WhichOneof(self, oneof_group: _WhichOneofArgType_param) -> _WhichOneofReturnType_param | None: ... - -Global___Query: _TypeAlias = Query # noqa: Y015 - -@_typing.final -class BatchQuery(_message.Message): - """Batch query format.""" - - DESCRIPTOR: _descriptor.Descriptor - - QUERIES_FIELD_NUMBER: _builtins.int - @_builtins.property - def queries(self) -> _containers.RepeatedCompositeFieldContainer[Global___Query]: - """The queries that form this batch query.""" - - def __init__( - self, - *, - queries: _abc.Iterable[Global___Query] | None = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["queries", b"queries"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___BatchQuery: _TypeAlias = BatchQuery # noqa: Y015 - -@_typing.final -class GetVulnByIdParameters(_message.Message): - """Parameters for GetBugById.""" - - DESCRIPTOR: _descriptor.Descriptor - - ID_FIELD_NUMBER: _builtins.int - id: _builtins.str - def __init__( - self, - *, - id: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["id", b"id"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___GetVulnByIdParameters: _TypeAlias = GetVulnByIdParameters # noqa: Y015 - -@_typing.final -class QueryAffectedParameters(_message.Message): - """Parameters for QueryAffected.""" - - DESCRIPTOR: _descriptor.Descriptor - - QUERY_FIELD_NUMBER: _builtins.int - @_builtins.property - def query(self) -> Global___Query: ... - def __init__( - self, - *, - query: Global___Query | None = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["query", b"query"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["query", b"query"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___QueryAffectedParameters: _TypeAlias = QueryAffectedParameters # noqa: Y015 - -@_typing.final -class QueryAffectedBatchParameters(_message.Message): - """Parameters for QueryAffectedBatch.""" - - DESCRIPTOR: _descriptor.Descriptor - - QUERY_FIELD_NUMBER: _builtins.int - @_builtins.property - def query(self) -> Global___BatchQuery: ... - def __init__( - self, - *, - query: Global___BatchQuery | None = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["query", b"query"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["query", b"query"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___QueryAffectedBatchParameters: _TypeAlias = QueryAffectedBatchParameters # noqa: Y015 - -@_typing.final -class DetermineVersionParameters(_message.Message): - """Parameters for DetermineVersion.""" - - DESCRIPTOR: _descriptor.Descriptor - - QUERY_FIELD_NUMBER: _builtins.int - @_builtins.property - def query(self) -> Global___VersionQuery: ... - def __init__( - self, - *, - query: Global___VersionQuery | None = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["query", b"query"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["query", b"query"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___DetermineVersionParameters: _TypeAlias = DetermineVersionParameters # noqa: Y015 - -@_typing.final -class ImportFindingsParameters(_message.Message): - """Parameters for ImportFindings.""" - - DESCRIPTOR: _descriptor.Descriptor - - SOURCE_FIELD_NUMBER: _builtins.int - source: _builtins.str - def __init__( - self, - *, - source: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["source", b"source"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___ImportFindingsParameters: _TypeAlias = ImportFindingsParameters # noqa: Y015 - -@_typing.final -class VersionQuery(_message.Message): - """The version query.""" - - DESCRIPTOR: _descriptor.Descriptor - - NAME_FIELD_NUMBER: _builtins.int - FILE_HASHES_FIELD_NUMBER: _builtins.int - name: _builtins.str - """The name of the dependency. Can be empty.""" - @_builtins.property - def file_hashes(self) -> _containers.RepeatedCompositeFieldContainer[Global___FileHash]: ... - def __init__( - self, - *, - name: _builtins.str = ..., - file_hashes: _abc.Iterable[Global___FileHash] | None = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["file_hashes", b"file_hashes", "name", b"name"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___VersionQuery: _TypeAlias = VersionQuery # noqa: Y015 - -@_typing.final -class FileHash(_message.Message): - """Information about the files in the repository - to identify the version. - """ - - DESCRIPTOR: _descriptor.Descriptor - - class _HashType: - ValueType = _typing.NewType("ValueType", _builtins.int) - V: _TypeAlias = ValueType # noqa: Y015 - - class _HashTypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[FileHash._HashType.ValueType], _builtins.type): - DESCRIPTOR: _descriptor.EnumDescriptor - MD5: FileHash._HashType.ValueType # 0 - - class HashType(_HashType, metaclass=_HashTypeEnumTypeWrapper): ... - MD5: FileHash.HashType.ValueType # 0 - - FILE_PATH_FIELD_NUMBER: _builtins.int - HASH_TYPE_FIELD_NUMBER: _builtins.int - HASH_FIELD_NUMBER: _builtins.int - file_path: _builtins.str - """The file path inside the repository, relative to the repository root.""" - hash_type: Global___FileHash.HashType.ValueType - hash: _builtins.bytes - def __init__( - self, - *, - file_path: _builtins.str = ..., - hash_type: Global___FileHash.HashType.ValueType = ..., - hash: _builtins.bytes = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["file_path", b"file_path", "hash", b"hash", "hash_type", b"hash_type"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___FileHash: _TypeAlias = FileHash # noqa: Y015 - -@_typing.final -class VersionMatchList(_message.Message): - """Result of DetmineVersion.""" - - DESCRIPTOR: _descriptor.Descriptor - - MATCHES_FIELD_NUMBER: _builtins.int - @_builtins.property - def matches(self) -> _containers.RepeatedCompositeFieldContainer[Global___VersionMatch]: ... - def __init__( - self, - *, - matches: _abc.Iterable[Global___VersionMatch] | None = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["matches", b"matches"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___VersionMatchList: _TypeAlias = VersionMatchList # noqa: Y015 - -@_typing.final -class ImportFindingList(_message.Message): - """Result of ImportFindings.""" - - DESCRIPTOR: _descriptor.Descriptor - - INVALID_RECORDS_FIELD_NUMBER: _builtins.int - @_builtins.property - def invalid_records(self) -> _containers.RepeatedCompositeFieldContainer[_importfinding_pb2.ImportFinding]: ... - def __init__( - self, - *, - invalid_records: _abc.Iterable[_importfinding_pb2.ImportFinding] | None = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["invalid_records", b"invalid_records"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___ImportFindingList: _TypeAlias = ImportFindingList # noqa: Y015 - -@_typing.final -class VersionMatch(_message.Message): - """Match information for the provided VersionQuery.""" - - DESCRIPTOR: _descriptor.Descriptor - - SCORE_FIELD_NUMBER: _builtins.int - REPO_INFO_FIELD_NUMBER: _builtins.int - OSV_IDENTIFIER_FIELD_NUMBER: _builtins.int - CPE23_FIELD_NUMBER: _builtins.int - MINIMUM_FILE_MATCHES_FIELD_NUMBER: _builtins.int - ESTIMATED_DIFF_FILES_FIELD_NUMBER: _builtins.int - score: _builtins.float - """Score in the interval (0.0, 1.0] with 1.0 being a perfect match.""" - cpe23: _builtins.str - """CPE 2.3.""" - minimum_file_matches: _builtins.int - """Definite number of files that have matched.""" - estimated_diff_files: _builtins.int - """Estimated number of files that are different.""" - @_builtins.property - def repo_info(self) -> Global___VersionRepositoryInformation: - """Information about the upstream repository.""" - - @_builtins.property - def osv_identifier(self) -> _vulnerability_pb2.Package: - """The OSV identifier.""" - - def __init__( - self, - *, - score: _builtins.float = ..., - repo_info: Global___VersionRepositoryInformation | None = ..., - osv_identifier: _vulnerability_pb2.Package | None = ..., - cpe23: _builtins.str = ..., - minimum_file_matches: _builtins.int = ..., - estimated_diff_files: _builtins.int = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["osv_identifier", b"osv_identifier", "repo_info", b"repo_info"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["cpe23", b"cpe23", "estimated_diff_files", b"estimated_diff_files", "minimum_file_matches", b"minimum_file_matches", "osv_identifier", b"osv_identifier", "repo_info", b"repo_info", "score", b"score"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___VersionMatch: _TypeAlias = VersionMatch # noqa: Y015 - -@_typing.final -class VersionRepositoryInformation(_message.Message): - DESCRIPTOR: _descriptor.Descriptor - - class _RepoType: - ValueType = _typing.NewType("ValueType", _builtins.int) - V: _TypeAlias = ValueType # noqa: Y015 - - class _RepoTypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[VersionRepositoryInformation._RepoType.ValueType], _builtins.type): - DESCRIPTOR: _descriptor.EnumDescriptor - UNSPECIFIED: VersionRepositoryInformation._RepoType.ValueType # 0 - GIT: VersionRepositoryInformation._RepoType.ValueType # 1 - - class RepoType(_RepoType, metaclass=_RepoTypeEnumTypeWrapper): ... - UNSPECIFIED: VersionRepositoryInformation.RepoType.ValueType # 0 - GIT: VersionRepositoryInformation.RepoType.ValueType # 1 - - TYPE_FIELD_NUMBER: _builtins.int - ADDRESS_FIELD_NUMBER: _builtins.int - TAG_FIELD_NUMBER: _builtins.int - VERSION_FIELD_NUMBER: _builtins.int - COMMIT_FIELD_NUMBER: _builtins.int - type: Global___VersionRepositoryInformation.RepoType.ValueType - address: _builtins.str - """Source address of the repository.""" - tag: _builtins.str - """Commit tag""" - version: _builtins.str - """Parsed version from commit tag""" - commit: _builtins.str - """Commit hash hex.""" - def __init__( - self, - *, - type: Global___VersionRepositoryInformation.RepoType.ValueType = ..., - address: _builtins.str = ..., - tag: _builtins.str = ..., - version: _builtins.str = ..., - commit: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["address", b"address", "commit", b"commit", "tag", b"tag", "type", b"type", "version", b"version"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___VersionRepositoryInformation: _TypeAlias = VersionRepositoryInformation # noqa: Y015 diff --git a/gcp/api/osv_service_v1_pb2_grpc.py b/gcp/api/osv_service_v1_pb2_grpc.py deleted file mode 100644 index 0af1ebf811c..00000000000 --- a/gcp/api/osv_service_v1_pb2_grpc.py +++ /dev/null @@ -1,281 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc -import warnings - -import osv_service_v1_pb2 as osv__service__v1__pb2 -import vulnerability_pb2 as vulnerability__pb2 - -GRPC_GENERATED_VERSION = '1.75.0' -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f'The grpc package installed is at version {GRPC_VERSION},' - + f' but the generated code in osv_service_v1_pb2_grpc.py depends on' - + f' grpcio>={GRPC_GENERATED_VERSION}.' - + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' - + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - ) - - -class OSVStub(object): - """Open source vulnerability database. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.GetVulnById = channel.unary_unary( - '/osv.v1.OSV/GetVulnById', - request_serializer=osv__service__v1__pb2.GetVulnByIdParameters.SerializeToString, - response_deserializer=vulnerability__pb2.Vulnerability.FromString, - _registered_method=True) - self.QueryAffected = channel.unary_unary( - '/osv.v1.OSV/QueryAffected', - request_serializer=osv__service__v1__pb2.QueryAffectedParameters.SerializeToString, - response_deserializer=osv__service__v1__pb2.VulnerabilityList.FromString, - _registered_method=True) - self.QueryAffectedBatch = channel.unary_unary( - '/osv.v1.OSV/QueryAffectedBatch', - request_serializer=osv__service__v1__pb2.QueryAffectedBatchParameters.SerializeToString, - response_deserializer=osv__service__v1__pb2.BatchVulnerabilityList.FromString, - _registered_method=True) - self.DetermineVersion = channel.unary_unary( - '/osv.v1.OSV/DetermineVersion', - request_serializer=osv__service__v1__pb2.DetermineVersionParameters.SerializeToString, - response_deserializer=osv__service__v1__pb2.VersionMatchList.FromString, - _registered_method=True) - self.ImportFindings = channel.unary_unary( - '/osv.v1.OSV/ImportFindings', - request_serializer=osv__service__v1__pb2.ImportFindingsParameters.SerializeToString, - response_deserializer=osv__service__v1__pb2.ImportFindingList.FromString, - _registered_method=True) - - -class OSVServicer(object): - """Open source vulnerability database. - """ - - def GetVulnById(self, request, context): - """Return a `Vulnerability` object for a given OSV ID. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def QueryAffected(self, request, context): - """Query vulnerabilities for a particular project at a given commit or - version. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def QueryAffectedBatch(self, request, context): - """Query vulnerabilities (batched) for given package versions and commits. - This currently allows a maximum of 1000 package versions to be included in - a single query. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DetermineVersion(self, request, context): - """Determine the version of the provided hash values. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ImportFindings(self, request, context): - """Get import findings per source. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_OSVServicer_to_server(servicer, server): - rpc_method_handlers = { - 'GetVulnById': grpc.unary_unary_rpc_method_handler( - servicer.GetVulnById, - request_deserializer=osv__service__v1__pb2.GetVulnByIdParameters.FromString, - response_serializer=vulnerability__pb2.Vulnerability.SerializeToString, - ), - 'QueryAffected': grpc.unary_unary_rpc_method_handler( - servicer.QueryAffected, - request_deserializer=osv__service__v1__pb2.QueryAffectedParameters.FromString, - response_serializer=osv__service__v1__pb2.VulnerabilityList.SerializeToString, - ), - 'QueryAffectedBatch': grpc.unary_unary_rpc_method_handler( - servicer.QueryAffectedBatch, - request_deserializer=osv__service__v1__pb2.QueryAffectedBatchParameters.FromString, - response_serializer=osv__service__v1__pb2.BatchVulnerabilityList.SerializeToString, - ), - 'DetermineVersion': grpc.unary_unary_rpc_method_handler( - servicer.DetermineVersion, - request_deserializer=osv__service__v1__pb2.DetermineVersionParameters.FromString, - response_serializer=osv__service__v1__pb2.VersionMatchList.SerializeToString, - ), - 'ImportFindings': grpc.unary_unary_rpc_method_handler( - servicer.ImportFindings, - request_deserializer=osv__service__v1__pb2.ImportFindingsParameters.FromString, - response_serializer=osv__service__v1__pb2.ImportFindingList.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'osv.v1.OSV', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) - server.add_registered_method_handlers('osv.v1.OSV', rpc_method_handlers) - - - # This class is part of an EXPERIMENTAL API. -class OSV(object): - """Open source vulnerability database. - """ - - @staticmethod - def GetVulnById(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/osv.v1.OSV/GetVulnById', - osv__service__v1__pb2.GetVulnByIdParameters.SerializeToString, - vulnerability__pb2.Vulnerability.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def QueryAffected(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/osv.v1.OSV/QueryAffected', - osv__service__v1__pb2.QueryAffectedParameters.SerializeToString, - osv__service__v1__pb2.VulnerabilityList.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def QueryAffectedBatch(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/osv.v1.OSV/QueryAffectedBatch', - osv__service__v1__pb2.QueryAffectedBatchParameters.SerializeToString, - osv__service__v1__pb2.BatchVulnerabilityList.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def DetermineVersion(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/osv.v1.OSV/DetermineVersion', - osv__service__v1__pb2.DetermineVersionParameters.SerializeToString, - osv__service__v1__pb2.VersionMatchList.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def ImportFindings(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/osv.v1.OSV/ImportFindings', - osv__service__v1__pb2.ImportFindingsParameters.SerializeToString, - osv__service__v1__pb2.ImportFindingList.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) diff --git a/gcp/api/poetry.lock b/gcp/api/poetry.lock deleted file mode 100644 index 02e0f31a8ca..00000000000 --- a/gcp/api/poetry.lock +++ /dev/null @@ -1,1886 +0,0 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. - -[[package]] -name = "astroid" -version = "4.0.4" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.10.0" -groups = ["dev"] -files = [ - {file = "astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753"}, - {file = "astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0"}, -] - -[[package]] -name = "attrs" -version = "26.1.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"}, - {file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"}, -] - -[[package]] -name = "certifi" -version = "2026.7.22" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775"}, - {file = "certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55"}, -] - -[[package]] -name = "cffi" -version = "2.1.1" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be"}, - {file = "cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b"}, - {file = "cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004"}, - {file = "cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9"}, - {file = "cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98"}, - {file = "cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9"}, - {file = "cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6"}, - {file = "cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf"}, - {file = "cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659"}, - {file = "cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9"}, - {file = "cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41"}, - {file = "cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1"}, - {file = "cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12"}, - {file = "cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1"}, - {file = "cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0"}, - {file = "cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813"}, - {file = "cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990"}, - {file = "cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af"}, - {file = "cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632"}, - {file = "cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd"}, - {file = "cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a"}, - {file = "cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa"}, - {file = "cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3"}, - {file = "cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0"}, - {file = "cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455"}, - {file = "cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0"}, - {file = "cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf"}, - {file = "cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a"}, - {file = "cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890"}, - {file = "cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50"}, - {file = "cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e"}, - {file = "cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf"}, - {file = "cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517"}, - {file = "cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735"}, - {file = "cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e"}, - {file = "cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a"}, - {file = "cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80"}, - {file = "cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e"}, - {file = "cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c"}, - {file = "cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6"}, - {file = "cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971"}, - {file = "cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c"}, - {file = "cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125"}, - {file = "cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264"}, - {file = "cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3"}, - {file = "cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2"}, - {file = "cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b"}, - {file = "cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7"}, - {file = "cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac"}, - {file = "cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d"}, - {file = "cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973"}, - {file = "cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c"}, - {file = "cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb"}, - {file = "cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54"}, - {file = "cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72"}, - {file = "cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1"}, - {file = "cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062"}, - {file = "cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03"}, - {file = "cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96"}, - {file = "cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527"}, - {file = "cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13"}, - {file = "cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c"}, - {file = "cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48"}, - {file = "cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836"}, - {file = "cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3"}, - {file = "cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2"}, - {file = "cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94"}, - {file = "cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc"}, - {file = "cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29"}, - {file = "cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676"}, - {file = "cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e"}, - {file = "cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f"}, - {file = "cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4"}, - {file = "cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e"}, - {file = "cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5"}, - {file = "cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d"}, - {file = "cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b"}, - {file = "cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4"}, - {file = "cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8"}, - {file = "cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6"}, - {file = "cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80"}, - {file = "cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779"}, - {file = "cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399"}, - {file = "cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688"}, - {file = "cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7"}, - {file = "cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac"}, - {file = "cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960"}, - {file = "cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1"}, - {file = "cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc"}, - {file = "cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab"}, - {file = "cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e"}, - {file = "cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358"}, - {file = "cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231"}, - {file = "cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6"}, - {file = "cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94"}, - {file = "cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5"}, - {file = "cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66"}, - {file = "cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3"}, - {file = "cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692"}, - {file = "cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be"}, -] - -[package.dependencies] -pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} - -[[package]] -name = "charset-normalizer" -version = "3.5.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "charset_normalizer-3.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1ee1e296209fdce05b81b663250eefa02213a2da7b41bf26f7829b8ba3545aa"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9fbdce1e47394b09bc9f26ab117dfc8d6491977a11d86f592bb42c779db2fda"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:00668ebb0609751758682eb0b5857e7c35b9f00e84dfdef062e103244ec94d45"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba2f37ee79e6338845261a3c5b1784e5d1acdff2c0785b284f1b633033d136ab"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ce854f5f478050ade5a238731c4ca985a7d3b3cb53ff600a9b5c3b689b5f0a7a"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96eefc178f8636b9c760c5829345307fd81cfae9ab1e80997dbddeb0f54ee9a3"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:366ec70f5547c640d3ce1985722490f23faf4eb5216a7eeba78277490e78dacb"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:950f23cb393f85543777b0433f082cddd25b51ab398eac7971146495679efe5f"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1dcc36dcb96abc02236e182d17e0f71430152a6c2c7447421da2d2dc144edea"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:07ffd07412fc5d5e84cd8952acf9ff7e4ed7a708e69d1bada19d8ba91711353f"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f5542f9b941279d82d41eb0aa9f98eba36fe4df5c7086c651df7944935b37182"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a545775cfe815855ea32d7c27731d79da358ef2055b4a25830231b1622dd18aa"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:494b70049a4d69aec6e8137c13af4cf8db8c9f9820a1392ac293b0dd2987a818"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-win32.whl", hash = "sha256:94fbf1c0c6cc0d3d5e50f9a9313a8cdca90dd696d34b381cd1704f8c9e939f20"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:be47f99644b208bff7766314013f9acf57b056b04191d570d68ad14022cf5b1d"}, - {file = "charset_normalizer-3.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:a6d095662e73e74f0a49988e0593373e243e3a52e27bfeea0a859e88acf4a0f5"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eda059b6bc8bc0812d626fd91a7ce01bf583df0a61296eff390fd94141a34e30"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa2bb0b37202dca27175591f761108b5d34096ade1191ffe4808bdf6b1571488"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b2b1b3fa5670c127b246df1d0c059defd41f689a868a3b9d79df9b1cac42d22"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e5e4d73d588ca5ed09df1b7dcd1b203d1df3c542e3f50d126c947d432b10731"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b54e7e13267d49ffbfe68e25b3cbd774dab38fa37238f71265e91b36146eb21c"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7b742bf31c88566b4bb6335a7f393bb322e580b6bb98df7bd0c25e6e3519ce8"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ba32c4d2abf1d2fe7cf27d280f4cca5664233b0f885549c7761719eb977f486"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0722590aabf9dc6a6c0343d523c05458fa2b5047dbe6302fd526bb570600753f"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa1099b956fb795e686d073568f6dc002a0bb89765ea6d5b055dd7d9bf1b116c"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd6c173f04743d483881bffa1478d5a4624475b8cd1d2194956a75548e191c18"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:f298e218441525d3794428b4c8b8fb8662c6d3ea79925d4807ee6b9a96a3bca5"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6e2912d4babbc65196ac13c2f53468dc57fb8b9c25ef913e8c59ddf7c6dc0e1b"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3d27167433c0d5f18dc850f07d0b3816221984fecdc405d6c157a6f0b8f8e9e6"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-win32.whl", hash = "sha256:ac00177c4831ffa650f8609e4bdddd5fe09c03b1c0c47acece7e6ea20421598b"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:f9b1e28d0e8dbfa858abdba91d6b547beaf2df1a59bec6da6faae7b96a4991a9"}, - {file = "charset_normalizer-3.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:ae31a1a1db2ee6cc2942fccaf695c934bc7f3db9f2133a3fef1f367cf1a4ab10"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-win32.whl", hash = "sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2"}, - {file = "charset_normalizer-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-win32.whl", hash = "sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6"}, - {file = "charset_normalizer-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:774d157f112367ff4abd29019f38f023c24e00e56edc7829c20e358a5a913ad8"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:26422d45fd13551cf564c58932f7d72b4f58b93b0fcf18c35ba6be12b46bb102"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:09a7bba9f739468c8e78c36a75c33768e53cb1959fc638f510454c14683f00d5"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4c9548dc78002099910abaebc0a72ac58b7d30931869e0351c09b507dff4ece3"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b7430cf5728e68f6c462254009a6ef4086e1bea43cf2f57aa9c55fb4f50ff96"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab743e9bc90c1f73552ec33e10e3331315acd2c397b36065b591b0181de533cc"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6f7deae3feb4edfa2efaf7c574fe88cbf055038a6abdb40188e4fff66d5699f"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:823f82903d189af463d7df250ef1f7f696f3cee08cc8d91deb565e8d425f6506"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:01e93745f7f219b703b60ba7afead36cfc4242782be5af484673fc500df12da5"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:329fc3ccb63ad22d867d84c2adea759a64079a37ba4a343433b02c7a2816871e"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:bb57753e36e4855b8ca375069482250a6246372331a3e4f3407eaebb007443f5"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fce8cbd4997efeb450bd298b54f755dcdff18d496f7a5ddbb4867c6d7c88fdc3"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6c9cdde8becb25a7fde49924511aa2644d6f8081cc8df8e9452724303348d8e3"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9ac4444d8d4fd4c4bd08bf451ed3167aa9e7ec6cdb41b648794f1d1103652e36"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f03ac127268b43ef4fe9e6ab6794a6794b49485a0cc0c1db79876d2f33f75bc7"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-win32.whl", hash = "sha256:1f5883d77fd409a261abb5dc8ccbe335720d798b1de4abb3b1d47ccbbc76b53b"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:c658c50ac0c98cd755a2dd50b7977d3bca7df401dcc47fbdfa87db53ef7d4e8b"}, - {file = "charset_normalizer-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:4bea7f8ebe90bbd7f0e4a2de42ca6924ba23e3e76418c408ff82f1d46fabd687"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:fbc597639158fd7c14d55e808718848319540f51b0e6746e3eefa59723a4a348"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71c909f353863b2b89c83de2ebed71ea6d0df8a6ef65a128193c5e650766bef"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7ac76cf9afd34929d76eb7fcb63be476a4853d8a96f0dcf2d0db68a0cbdf9885"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a3a370082ce34d0612f421e15fe011c53bb1feff21a26d06ad4fb244dab5a375"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:256dd4d85d9e4dc595e2bc983c980e73f62ddeb3165c58b4c3dfe78c5c8548c1"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:58d4aa13a59c969dbfdf9e6a9560e242cbfd9e8a8f50c2747714df1a423adf65"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c6dfb5ca6723eeed15aa8e564a014d69fcb8812f94eef11fe3631e0508199f5"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c010f5581d9c612804cc59fcf7b524b707fbcb72828551237ab545bb5c7034af"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:52ec005752a56ae79547a05c0139ca2501a0c866390b6115008456b9f0e7cde1"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2bced4061f000f7187254a02ad3433ae17eaf991747ceea2f478422590a5bba9"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9eea3ab2597a5e65fe65296e2d6a84570845a6b55532d90333d740d48bbc850a"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:496846868fea80e479324862fa877f02411f2fd0f83b79ccee2607aa68b2a032"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:85d5855daafc240cc045c026d7a15fd198a09b0fc8ff6f5ecbb5297b509cb11e"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-win32.whl", hash = "sha256:58d3e12c88e0950bca850ae1f7c256055c097639c2edb9eb123af9807d8b15e4"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:acaf604462bf330b0d07e7a07c1d6e4adac79e5fb13e9c5140590542cafacc00"}, - {file = "charset_normalizer-3.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:fdb8a068947befafba9952162645dc2fecaeb400e64584829ed5e9b2fbe21a7f"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9085f87b0e38a2b92b8923059b4e8789fe40d9279712d15dcc670048d77079af"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2679de311c7946dde5d3b6f44941844133ff5c7cb86099c0061ab1e8901c20a8"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:baf3775a2635e5a11fbd5e4e64ee69c7e86875d224a5c72aca4c141064589a90"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ac8c94b6539074e0f40899301273ac8402b9b3e01c7b7ba269ff30340aaaf20"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe532b3c966d1fb794e0698e4589d0444017ae77fc0b31edea13c0e35bcc449"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c84bec0ab5ae0c64bfe73a7d2adcb5ce73b467523fc27fd6a28ab2aa6cbe35a"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:854066be00447fa8de2ccbbe893e2ffc4b123ef16d897af794c1e18bd4a714b0"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:21b82d8082f6f5e7f456ef0bd16323d08de1266efbfeb476e64b2a91d1471a4e"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:838648accb3a7fd9803fd45c87bce8509648eb0c11bc34e216141300977244f2"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:195ce897c6153c0700078142cf8efe3e6454ca4cf4357499e4078dfd83396626"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:978eab16f55b4ab2c2a745be9a0a840bf8f09a7f227d9c76eb30214d078865a5"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:cc0329df4caaceb950d2f580b5ac716a377f7059624a0bafaeaf8a218c6ed774"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:687c9ca3035544b113bea2055e180af96fb63c0c476e22a9180f51925186e7b7"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-win32.whl", hash = "sha256:706bfd38730a5ac7a365793269a00f4e988178cec121391f4248d84ad8c972e9"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:92caef967d287a407085d61176fce4012b1dd62daed4eb6d5ceb26d3d2538712"}, - {file = "charset_normalizer-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:5fc45d653ea8c9a20479167e11d4a0f8cb2fa3470737ab6f9c827532313187b7"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:59171c6e45bf07d0d5cab3b0bf81d945035530f6873398b3b531c31184d46663"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9dbdd9205662134957cf0c324f639bdc5031c0ca056e2369e238db75187c0f11"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4b018dc5a0eee4676e38fe84a47a427816c590b93b55d9025274ec4d6ffc2dc"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced3fdd71aaa83ce593746c2edb42b7a59cb4c19c8b5c407781c72e493aae55a"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:19a3dd5aa73cef1c99687c4fc57db016a9c17104ae1185da88ba566a5d3bebe4"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc5d36d96478aa9c60654bd932525bf32964c62a7281eafdf16d85003a8d6004"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:04368edf83514385ffc3e1cfd4546e595f4f1272dd23ba437a93a9cc3741d47b"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:9b5db6052055d34d41230fb78d7c439c23dc536a9896f6cb039e8dd92cfc1263"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:252d099029bcbea642f2a06c4ed5046bdf8b5a8150b64afa5e027e88b106e5ee"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6199d5606e2bbf2b096cf64d03f8b6790c91081d5ac866b8e7bb6422738cc60c"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:77efcff2b23071c349402ac1066667a3d011f62398d81408c9b88ad991747c9e"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:a5cbd90ecf0fc62e64726917ad083b73001f0563657a87ec3c0b504e277dc90d"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:4d26f14f041e83dd8edfd61f4cd4fa7285d31798b5bf1f28e70c367ba6c41d61"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-win32.whl", hash = "sha256:ac13b004224fb341e1e25a1ed5e19d32f57cdb2a403e01f003b46f051a550f6f"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:35aea775dc2bd5f54cd84a1cd2696cc3207c479cb9cf0bd346f0d343e4300ddb"}, - {file = "charset_normalizer-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:fb78f6e7fcd8ad785d28cd577168bc1aaee827b25bb8755638f694794ea98f0a"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-win32.whl", hash = "sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959"}, - {file = "charset_normalizer-3.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85de3134b5379856e323ba37c19c9256d39425f7b76a63af52b09fb4664c2e8f"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e5e1224c0a6a90e05843e07adfec669edebec17801c67072f51e59561d63c0b"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5e2d0e146dcb57034f8b97dc58d2d512cb90aba253960ce449f695fec6a82c6f"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e06efa066f7dbadbc84ebc126a97c452a6451dfcf589d89d788484949e1cf795"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:994e883d17c559cdfd38c84003c8b27d25424a1077272a17e7cd27bfe0bf57b2"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:789b8982559ae28dad2356519f841655756cdcd96616410590ae0b17454ee64f"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a951ad59cad9145664a730d3036b40b844e74d2d3683da40111463cd3a83845d"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:55261ac0d2941c42f196dd576f543d87a8ee03cd6f5e30dfb4d807b2e3b9121a"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5ca0555312ae2fe82715cada7fac375530c2f3349e1eaa1bcb33d0283ac79a18"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2e9cf9253119d8e5d111f05d71626786fd3d6193817316eab1ca088cdb8593cf"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:433c5a81eade63b47e522303bad236f59dba55ea6951746f5558355eeed8c75d"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:13e3afe97712e8887cd516e960c63f0b93122971e5b5e4b2622fe7701771e838"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb12fb2ba69ffa05f8695f61c69e591dc4b4a12ac3757ac8af8adb259bf56d17"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-win32.whl", hash = "sha256:56490c595a28b1bb27dfc583e816152a9767721ef58b2c03b13f954d2f707420"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:012a22b88a77ca2e59b98ac5889b0deb604147666032f45e6d6e217634d2550d"}, - {file = "charset_normalizer-3.5.1-cp39-cp39-win_arm64.whl", hash = "sha256:29880d17a8eb0b5cfdfd8944b468322928059aa35f1f5fa8ff22b149ec0b42f8"}, - {file = "charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6"}, - {file = "charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "cryptography" -version = "50.0.0" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = "!=3.9.0,!=3.9.1,>=3.9" -groups = ["main"] -files = [ - {file = "cryptography-50.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169"}, - {file = "cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f"}, - {file = "cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105"}, - {file = "cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef"}, - {file = "cryptography-50.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30"}, - {file = "cryptography-50.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d"}, - {file = "cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c"}, - {file = "cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c"}, - {file = "cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95"}, - {file = "cryptography-50.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269"}, - {file = "cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708"}, - {file = "cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47"}, - {file = "cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9"}, - {file = "cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7"}, - {file = "cryptography-50.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba"}, - {file = "cryptography-50.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c"}, - {file = "cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a"}, - {file = "cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e"}, - {file = "cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d"}, - {file = "cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437"}, - {file = "cryptography-50.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9"}, - {file = "cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9"}, -] - -[package.dependencies] -cffi = {version = ">=2.0.0", markers = "platform_python_implementation != \"PyPy\""} - -[package.extras] -ssh = ["bcrypt (>=3.1.5)"] - -[[package]] -name = "dill" -version = "0.4.1" -description = "serialize all of Python" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d"}, - {file = "dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - -[[package]] -name = "google-api-core" -version = "2.25.2" -description = "Google API client core library" -optional = false -python-versions = ">=3.7" -groups = ["main"] -markers = "python_version >= \"3.14\"" -files = [ - {file = "google_api_core-2.25.2-py3-none-any.whl", hash = "sha256:e9a8f62d363dc8424a8497f4c2a47d6bcda6c16514c935629c257ab5d10210e7"}, - {file = "google_api_core-2.25.2.tar.gz", hash = "sha256:1c63aa6af0d0d5e37966f157a77f9396d820fba59f9e43e9415bc3dc5baff300"}, -] - -[package.dependencies] -google-auth = ">=2.14.1,<3.0.0" -googleapis-common-protos = ">=1.56.2,<2.0.0" -grpcio = {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} -grpcio-status = {version = ">=1.49.1,<2.0.0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} -proto-plus = {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" -requests = ">=2.18.0,<3.0.0" - -[package.extras] -async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.0)"] -grpc = ["grpcio (>=1.33.2,<2.0.0)", "grpcio (>=1.49.1,<2.0.0) ; python_version >= \"3.11\"", "grpcio-status (>=1.33.2,<2.0.0)", "grpcio-status (>=1.49.1,<2.0.0) ; python_version >= \"3.11\""] -grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"] -grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"] - -[[package]] -name = "google-api-core" -version = "2.34.0" -description = "Google API client core library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.13\"" -files = [ - {file = "google_api_core-2.34.0-py3-none-any.whl", hash = "sha256:cdf9c67e7ca2402d86ccbfde5f2503fc83e3cc3f58cc78456ae96cad24a6d2de"}, - {file = "google_api_core-2.34.0.tar.gz", hash = "sha256:98a779fe72de956eb1c9c2f47ff4c4432a668ece1a002ec38bed07ec2698ae59"}, -] - -[package.dependencies] -google-auth = ">=2.14.1,<3.0.0" -googleapis-common-protos = ">=1.69.2,<2.0.0" -grpcio = {version = ">=1.59.0,<2.0.0", optional = true, markers = "python_version < \"3.14\" and extra == \"grpc\""} -grpcio-status = {version = ">=1.59.0,<2.0.0", optional = true, markers = "extra == \"grpc\""} -proto-plus = ">=1.26.1,<2.0.0" -protobuf = ">=6.33.5,<8.0.0" -requests = ">=2.33.0,<3.0.0" - -[package.extras] -async-rest = ["aiohttp (>=3.13.4)", "google-auth[aiohttp] (>=2.14.1,<3.0.0)"] -grpc = ["grpcio (>=1.59.0,<2.0.0)", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.59.0,<2.0.0)", "grpcio-status (>=1.75.1,<2.0.0) ; python_version >= \"3.14\""] - -[[package]] -name = "google-auth" -version = "2.56.3" -description = "Google Authentication Library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "google_auth-2.56.3-py3-none-any.whl", hash = "sha256:8ec438808f813ad034535000261eed1067475d229d05bbf4216e78c3f2362e53"}, - {file = "google_auth-2.56.3.tar.gz", hash = "sha256:40e229fc901f0a305b553050e5fce562d509bee0435be053abfa91582b51b90c"}, -] - -[package.dependencies] -cryptography = [ - {version = ">=41.0.5", markers = "python_version >= \"3.14\""}, - {version = ">=38.0.3", markers = "python_version < \"3.14\""}, -] -pyasn1-modules = ">=0.2.1" - -[package.extras] -aiohttp = ["aiohttp (>=3.8.0,<4.0.0) ; python_version < \"3.14\"", "aiohttp (>=3.9.0,<4.0.0) ; python_version >= \"3.14\"", "requests (>=2.30.0,<3.0.0)"] -cryptography = ["cryptography (>=38.0.3) ; python_version < \"3.14\"", "cryptography (>=41.0.5) ; python_version >= \"3.14\""] -enterprise-cert = ["cryptography (>=38.0.3) ; python_version < \"3.14\"", "cryptography (>=41.0.5) ; python_version >= \"3.14\""] -grpc = ["grpcio (>=1.59.0,<2.0.0) ; python_version < \"3.14\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\""] -pyjwt = ["pyjwt (>=2.0)"] -pyopenssl = ["cryptography (>=38.0.3) ; python_version < \"3.14\"", "cryptography (>=41.0.5) ; python_version >= \"3.14\""] -reauth = ["pyu2f (>=0.1.5)"] -requests = ["requests (>=2.30.0,<3.0.0)"] -rsa = ["rsa (>=4.0.0,<5)"] -testing = ["aiohttp (>=3.8.0,<4.0.0) ; python_version < \"3.14\"", "aiohttp (>=3.9.0,<4.0.0) ; python_version >= \"3.14\"", "aioresponses", "flask", "freezegun", "grpcio (>=1.59.0,<2.0.0) ; python_version < \"3.14\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "packaging (>=20.0)", "pyjwt (>=2.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.30.0,<3.0.0)", "responses", "urllib3 (>=1.26.15,<3.0.0)"] -urllib3 = ["packaging (>=20.0)", "urllib3 (>=1.26.15,<3.0.0)"] - -[[package]] -name = "google-cloud-appengine-logging" -version = "1.6.2" -description = "Google Cloud Appengine Logging API client library" -optional = false -python-versions = ">=3.7" -groups = ["main"] -markers = "python_version >= \"3.14\"" -files = [ - {file = "google_cloud_appengine_logging-1.6.2-py3-none-any.whl", hash = "sha256:2b28ed715e92b67e334c6fcfe1deb523f001919560257b25fc8fcda95fd63938"}, - {file = "google_cloud_appengine_logging-1.6.2.tar.gz", hash = "sha256:4890928464c98da9eecc7bf4e0542eba2551512c0265462c10f3a3d2a6424b90"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras = ["grpc"]} -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" -proto-plus = {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""} -protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" - -[[package]] -name = "google-cloud-appengine-logging" -version = "1.10.0" -description = "Google Cloud Appengine Logging API client library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.13\"" -files = [ - {file = "google_cloud_appengine_logging-1.10.0-py3-none-any.whl", hash = "sha256:193675caaf062c41688a3e2c744b73614db82408bc7fb060353b6878d7134492"}, - {file = "google_cloud_appengine_logging-1.10.0.tar.gz", hash = "sha256:b5563e76010a36e6adf1cc489620c29ee4fb3b986b006d237e9a061eb0f0abb7"}, -] - -[package.dependencies] -google-api-core = {version = ">=2.17.1,<3.0.0", extras = ["grpc"]} -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" -grpcio = ">=1.59.0,<2.0.0" -proto-plus = {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""} -protobuf = ">=4.25.8,<8.0.0" - -[[package]] -name = "google-cloud-audit-log" -version = "0.6.1" -description = "Google Cloud Audit Protos" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "google_cloud_audit_log-0.6.1-py3-none-any.whl", hash = "sha256:981b2ac69e69fe8ed0eb71998d8520a746d5db9bdfad0f088ab4b4628702824e"}, - {file = "google_cloud_audit_log-0.6.1.tar.gz", hash = "sha256:4aba72b45dc15344da1b2d8981320050fe39cfe80432f38e7318aa2e477c5bf2"}, -] - -[package.dependencies] -googleapis-common-protos = ">=1.69.2,<2.0.0" -protobuf = ">=6.33.5,<8.0.0" - -[[package]] -name = "google-cloud-core" -version = "2.6.0" -description = "Google Cloud API client core library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version >= \"3.14\"" -files = [ - {file = "google_cloud_core-2.6.0-py3-none-any.whl", hash = "sha256:6d63ac8e5eca6d9e4319d0a1e2265fadcd7f1049904378caecfa01cf52dd869e"}, - {file = "google_cloud_core-2.6.0.tar.gz", hash = "sha256:e76149739f90fac1fc6757c09f47eaccb3145b54adbd7759b0f7c4b235f46c83"}, -] - -[package.dependencies] -google-api-core = ">=2.11.0,<3.0.0" -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" - -[package.extras] -grpc = ["grpcio (>=1.47.0,<2.0.0) ; python_version < \"3.14\"", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.47.0,<2.0.0)"] - -[[package]] -name = "google-cloud-core" -version = "2.6.1" -description = "Google Cloud API client core library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.13\"" -files = [ - {file = "google_cloud_core-2.6.1-py3-none-any.whl", hash = "sha256:2682a8a4474a32f56292fb4bca7fa7e4fb0b4af958f6abfe4bca8d195747fd45"}, - {file = "google_cloud_core-2.6.1.tar.gz", hash = "sha256:1e044b131f2ae097b92312fa195164b0aeb6dc6a88e00231e1210516314c420c"}, -] - -[package.dependencies] -google-api-core = ">=2.28.0,<3.0.0" -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" - -[package.extras] -grpc = ["grpcio (>=1.59.0,<2.0.0)", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.59.0,<2.0.0)", "grpcio-status (>=1.75.1,<2.0.0) ; python_version >= \"3.14\""] - -[[package]] -name = "google-cloud-datastore" -version = "2.21.0" -description = "Google Cloud Datastore API client library" -optional = false -python-versions = ">=3.7" -groups = ["main"] -markers = "python_version >= \"3.14\"" -files = [ - {file = "google_cloud_datastore-2.21.0-py2.py3-none-any.whl", hash = "sha256:f303f27cd1983383f20bd227019cd8a7897419e0ec6b878367c58c66245f9d9b"}, - {file = "google_cloud_datastore-2.21.0.tar.gz", hash = "sha256:eee454dd4a55f5b327f9f344928ff1a09a6f77c23d5e3d908ad31a13cc2f4073"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras = ["grpc"]} -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" -google-cloud-core = ">=1.4.0,<3.0.0" -proto-plus = {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""} -protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" - -[package.extras] -libcst = ["libcst (>=0.2.5)"] - -[[package]] -name = "google-cloud-datastore" -version = "2.26.0" -description = "Google Cloud Datastore API client library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.13\"" -files = [ - {file = "google_cloud_datastore-2.26.0-py3-none-any.whl", hash = "sha256:1bde5694308468da5c932153a6b0331d5b54e1c21de366b6a75ddb0000fd5f4b"}, - {file = "google_cloud_datastore-2.26.0.tar.gz", hash = "sha256:1ac4882f033fcb90bd75d1171b46400ecd5def23768598843d9c041e0c0b4a6a"}, -] - -[package.dependencies] -google-api-core = {version = ">=2.24.2,<3.0.0", extras = ["grpc"]} -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" -google-cloud-core = ">=2.0.0,<3.0.0" -grpcio = ">=1.59.0,<2.0.0" -proto-plus = ">=1.26.1,<2.0.0" -protobuf = ">=6.33.5,<8.0.0" - -[[package]] -name = "google-cloud-logging" -version = "3.13.0" -description = "Stackdriver Logging API client library" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "google_cloud_logging-3.13.0-py3-none-any.whl", hash = "sha256:f215e1c76ee29239c6cacf02443dffa985663c74bf47c9818854694805c6019f"}, - {file = "google_cloud_logging-3.13.0.tar.gz", hash = "sha256:3aae0573b1a1a4f59ecdf4571f4e7881b5823bd129fe469561c1c49a7fa8a4c1"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras = ["grpc"]} -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" -google-cloud-appengine-logging = ">=0.1.3,<2.0.0" -google-cloud-audit-log = ">=0.3.1,<1.0.0" -google-cloud-core = ">=2.0.0,<3.0.0" -grpc-google-iam-v1 = ">=0.12.4,<1.0.0" -opentelemetry-api = ">=1.9.0" -proto-plus = {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""} -protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" - -[[package]] -name = "google-cloud-ndb" -version = "2.4.0" -description = "NDB library for Google Cloud Datastore" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "google_cloud_ndb-2.4.0-py3-none-any.whl", hash = "sha256:adbb34d14f6657b48d8b3d460fcf2dcb274acf2d574d5efc33d13e903504fe85"}, - {file = "google_cloud_ndb-2.4.0.tar.gz", hash = "sha256:3a2ab97b4402f1e18cca0ef31ea40695a07483421dd1a59d4aa36c59916d10d0"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras = ["grpc"]} -google-cloud-datastore = ">=2.16.0,<2.20.2 || >2.20.2,<3.0.0" -protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" -pymemcache = ">=2.1.0,<5.0.0" -pytz = ">=2018.3" -redis = ">=3.0.0,<7.0.0" - -[[package]] -name = "google-cloud-pubsub" -version = "2.38.0" -description = "Google Cloud Pub/Sub API client library" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "python_version >= \"3.14\"" -files = [ - {file = "google_cloud_pubsub-2.38.0-py3-none-any.whl", hash = "sha256:fed2c40cfb77d58f6dced563a8146a8c34319c7dfbbb4d045b6c9c101e043db9"}, - {file = "google_cloud_pubsub-2.38.0.tar.gz", hash = "sha256:9212309f8d6cfaefb577bca52492b13464b56e584505408685d63e69346c56cf"}, -] - -[package.dependencies] -google-api-core = {version = ">=2.11.0,<3.0.0", extras = ["grpc"]} -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" -grpc-google-iam-v1 = ">=0.14.0,<1.0.0" -grpcio-status = ">=1.33.2" -opentelemetry-api = ">=1.27.0" -opentelemetry-sdk = ">=1.27.0" -proto-plus = {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""} -protobuf = ">=4.25.8,<8.0.0" - -[package.extras] -libcst = ["libcst (>=0.3.10)"] - -[[package]] -name = "google-cloud-pubsub" -version = "2.39.1" -description = "Google Cloud Pub/Sub API client library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.13\"" -files = [ - {file = "google_cloud_pubsub-2.39.1-py3-none-any.whl", hash = "sha256:b40347cff2df3c4b33747bfdf8a8d74942754c5a6752592acd51a20fec316b56"}, - {file = "google_cloud_pubsub-2.39.1.tar.gz", hash = "sha256:dacead1fa0aca7b2015f1acc88d279cd4b12852087908f426fbecf71985940e8"}, -] - -[package.dependencies] -google-api-core = {version = ">=2.25.0,<3.0.0", extras = ["grpc"]} -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" -grpc-google-iam-v1 = ">=0.14.2,<1.0.0" -grpcio = ">=1.59.0,<2.0.0" -grpcio-status = ">=1.59.0,<2.0.0" -opentelemetry-api = ">=1.27.0" -opentelemetry-sdk = ">=1.27.0" -proto-plus = ">=1.26.1,<2.0.0" -protobuf = ">=6.33.5,<8.0.0" - -[package.extras] -libcst = ["libcst (>=0.3.10)"] - -[[package]] -name = "google-cloud-storage" -version = "3.4.1" -description = "Google Cloud Storage API client library" -optional = false -python-versions = ">=3.7" -groups = ["main"] -markers = "python_version >= \"3.14\"" -files = [ - {file = "google_cloud_storage-3.4.1-py3-none-any.whl", hash = "sha256:972764cc0392aa097be8f49a5354e22eb47c3f62370067fb1571ffff4a1c1189"}, - {file = "google_cloud_storage-3.4.1.tar.gz", hash = "sha256:6f041a297e23a4b485fad8c305a7a6e6831855c208bcbe74d00332a909f82268"}, -] - -[package.dependencies] -google-api-core = ">=2.15.0,<3.0.0" -google-auth = ">=2.26.1,<3.0.0" -google-cloud-core = ">=2.4.2,<3.0.0" -google-crc32c = ">=1.1.3,<2.0.0" -google-resumable-media = ">=2.7.2,<3.0.0" -requests = ">=2.22.0,<3.0.0" - -[package.extras] -protobuf = ["protobuf (>=3.20.2,<7.0.0)"] -tracing = ["opentelemetry-api (>=1.1.0,<2.0.0)"] - -[[package]] -name = "google-cloud-storage" -version = "3.13.1" -description = "Google Cloud Storage API client library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.13\"" -files = [ - {file = "google_cloud_storage-3.13.1-py3-none-any.whl", hash = "sha256:98208de6c21e85cecd3eb44551894efff33d98365500e178867d4305854a770a"}, - {file = "google_cloud_storage-3.13.1.tar.gz", hash = "sha256:a80bf8cac2794808aa61c50c5f769ecbbe2d10331bacd0d69d30e59b14b346b2"}, -] - -[package.dependencies] -google-api-core = ">=2.27.0,<3.0.0" -google-auth = ">=2.26.1,<3.0.0" -google-cloud-core = ">=2.4.2,<3.0.0" -google-crc32c = ">=1.6.0,<2.0.0" -google-resumable-media = ">=2.7.2,<3.0.0" -requests = ">=2.22.0,<3.0.0" - -[package.extras] -grpc = ["google-api-core[grpc] (>=2.27.0,<3.0.0)", "grpc-google-iam-v1 (>=0.14.2,<1.0.0)", "grpcio (>=1.59.0,<2.0.0)", "grpcio (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "grpcio-status (>=1.59.0,<2.0.0)", "grpcio-status (>=1.75.1,<2.0.0) ; python_version >= \"3.14\"", "proto-plus (>=1.26.1,<2.0.0)", "protobuf (>=6.33.5,<8.0.0)"] -protobuf = ["protobuf (>=6.33.5,<8.0.0)"] -testing = ["PyYAML", "black", "brotli", "coverage", "flake8", "google-cloud-iam", "google-cloud-kms", "google-cloud-pubsub", "google-cloud-testutils", "google-cloud-testutils", "mock", "numpy", "opentelemetry-sdk", "psutil", "py-cpuinfo", "pyopenssl", "pytest", "pytest-asyncio", "pytest-benchmark", "pytest-cov", "pytest-rerunfailures", "pytest-xdist"] -tracing = ["opentelemetry-api (>=1.1.0,<2.0.0)"] - -[[package]] -name = "google-crc32c" -version = "1.8.0" -description = "A python wrapper of the C library 'Google CRC32C'" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff"}, - {file = "google_crc32c-1.8.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288"}, - {file = "google_crc32c-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d"}, - {file = "google_crc32c-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092"}, - {file = "google_crc32c-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733"}, - {file = "google_crc32c-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8"}, - {file = "google_crc32c-1.8.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:86cfc00fe45a0ac7359e5214a1704e51a99e757d0272554874f419f79838c5f7"}, - {file = "google_crc32c-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:19b40d637a54cb71e0829179f6cb41835f0fbd9e8eb60552152a8b52c36cbe15"}, - {file = "google_crc32c-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:17446feb05abddc187e5441a45971b8394ea4c1b6efd88ab0af393fd9e0a156a"}, - {file = "google_crc32c-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:71734788a88f551fbd6a97be9668a0020698e07b2bf5b3aa26a36c10cdfb27b2"}, - {file = "google_crc32c-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4b8286b659c1335172e39563ab0a768b8015e88e08329fa5321f774275fc3113"}, - {file = "google_crc32c-1.8.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2a3dc3318507de089c5384cc74d54318401410f82aa65b2d9cdde9d297aca7cb"}, - {file = "google_crc32c-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411"}, - {file = "google_crc32c-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454"}, - {file = "google_crc32c-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc0c8912038065eafa603b238abf252e204accab2a704c63b9e14837a854962"}, - {file = "google_crc32c-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:3ebb04528e83b2634857f43f9bb8ef5b2bbe7f10f140daeb01b58f972d04736b"}, - {file = "google_crc32c-1.8.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:450dc98429d3e33ed2926fc99ee81001928d63460f8538f21a5d6060912a8e27"}, - {file = "google_crc32c-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3b9776774b24ba76831609ffbabce8cdf6fa2bd5e9df37b594221c7e333a81fa"}, - {file = "google_crc32c-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89c17d53d75562edfff86679244830599ee0a48efc216200691de8b02ab6b2b8"}, - {file = "google_crc32c-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:57a50a9035b75643996fbf224d6661e386c7162d1dfdab9bc4ca790947d1007f"}, - {file = "google_crc32c-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:e6584b12cb06796d285d09e33f63309a09368b9d806a551d8036a4207ea43697"}, - {file = "google_crc32c-1.8.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:f4b51844ef67d6cf2e9425983274da75f18b1597bb2c998e1c0a0e8d46f8f651"}, - {file = "google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b0d1a7afc6e8e4635564ba8aa5c0548e3173e41b6384d7711a9123165f582de2"}, - {file = "google_crc32c-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3f68782f3cbd1bce027e48768293072813469af6a61a86f6bb4977a4380f21"}, - {file = "google_crc32c-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:d511b3153e7011a27ab6ee6bb3a5404a55b994dc1a7322c0b87b29606d9790e2"}, - {file = "google_crc32c-1.8.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ba6aba18daf4d36ad4412feede6221414692f44d17e5428bdd81ad3fc1eee5dc"}, - {file = "google_crc32c-1.8.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:87b0072c4ecc9505cfa16ee734b00cd7721d20a0f595be4d40d3d21b41f65ae2"}, - {file = "google_crc32c-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d488e98b18809f5e322978d4506373599c0c13e6c5ad13e53bb44758e18d215"}, - {file = "google_crc32c-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01f126a5cfddc378290de52095e2c7052be2ba7656a9f0caf4bcd1bfb1833f8a"}, - {file = "google_crc32c-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:61f58b28e0b21fcb249a8247ad0db2e64114e201e2e9b4200af020f3b6242c9f"}, - {file = "google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:87fa445064e7db928226b2e6f0d5304ab4cd0339e664a4e9a25029f384d9bb93"}, - {file = "google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f639065ea2042d5c034bf258a9f085eaa7af0cd250667c0635a3118e8f92c69c"}, - {file = "google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79"}, -] - -[[package]] -name = "google-resumable-media" -version = "2.10.1" -description = "Utilities for Google Media Downloads and Resumable Uploads" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "google_resumable_media-2.10.1-py3-none-any.whl", hash = "sha256:4e2cbc704207ddc09f23b1f18e8ef4a4ccbfe0f1768b370e5c969704adbd0a1c"}, - {file = "google_resumable_media-2.10.1.tar.gz", hash = "sha256:224975032ddb73f7ed9e2f0f4cc08ed1b06874c52d48cc8533e3eb72980b21a0"}, -] - -[package.dependencies] -google-crc32c = ">=1.0.0,<2.0.0" - -[package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "google-auth (>=2.14.1,<3.0.0)"] -requests = ["requests (>=2.18.0,<3.0.0)"] - -[[package]] -name = "googleapis-common-protos" -version = "1.75.1" -description = "Common protobufs used in Google APIs" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "googleapis_common_protos-1.75.1-py3-none-any.whl", hash = "sha256:28a1934bcd33b9c9da66ac301a0a4227e3367f095a17d0375cb98f0a09d93b79"}, - {file = "googleapis_common_protos-1.75.1.tar.gz", hash = "sha256:d3042c6c5a2d4e67113104d6b6818b59b6bd92a197f2a91508e801fe815cf071"}, -] - -[package.dependencies] -grpcio = {version = ">=1.59.0,<2.0.0", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=6.33.5,<8.0.0" - -[package.extras] -grpc = ["grpcio (>=1.59.0,<2.0.0)"] - -[[package]] -name = "grpc-google-iam-v1" -version = "0.14.5" -description = "IAM API client library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "grpc_google_iam_v1-0.14.5-py3-none-any.whl", hash = "sha256:0f5e680b20aa0a9441e68c769da04d94d70fca4e43751a82d8abb8aa6a7181ca"}, - {file = "grpc_google_iam_v1-0.14.5.tar.gz", hash = "sha256:07fd3a9fafb586588e771831fbfc8f6597050181d0c3b45e039d18b8fdc1aab5"}, -] - -[package.dependencies] -googleapis-common-protos = {version = ">=1.69.2,<2.0.0", extras = ["grpc"]} -grpcio = ">=1.59.0,<2.0.0" -protobuf = ">=6.33.5,<8.0.0" - -[[package]] -name = "grpcio" -version = "1.75.0" -description = "HTTP/2-based RPC framework" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "grpcio-1.75.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:1ec9cbaec18d9597c718b1ed452e61748ac0b36ba350d558f9ded1a94cc15ec7"}, - {file = "grpcio-1.75.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7ee5ee42bfae8238b66a275f9ebcf6f295724375f2fa6f3b52188008b6380faf"}, - {file = "grpcio-1.75.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9146e40378f551eed66c887332afc807fcce593c43c698e21266a4227d4e20d2"}, - {file = "grpcio-1.75.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0c40f368541945bb664857ecd7400acb901053a1abbcf9f7896361b2cfa66798"}, - {file = "grpcio-1.75.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:50a6e43a9adc6938e2a16c9d9f8a2da9dd557ddd9284b73b07bd03d0e098d1e9"}, - {file = "grpcio-1.75.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dce15597ca11913b78e1203c042d5723e3ea7f59e7095a1abd0621be0e05b895"}, - {file = "grpcio-1.75.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:851194eec47755101962da423f575ea223c9dd7f487828fe5693920e8745227e"}, - {file = "grpcio-1.75.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ca123db0813eef80625a4242a0c37563cb30a3edddebe5ee65373854cf187215"}, - {file = "grpcio-1.75.0-cp310-cp310-win32.whl", hash = "sha256:222b0851e20c04900c63f60153503e918b08a5a0fad8198401c0b1be13c6815b"}, - {file = "grpcio-1.75.0-cp310-cp310-win_amd64.whl", hash = "sha256:bb58e38a50baed9b21492c4b3f3263462e4e37270b7ea152fc10124b4bd1c318"}, - {file = "grpcio-1.75.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:7f89d6d0cd43170a80ebb4605cad54c7d462d21dc054f47688912e8bf08164af"}, - {file = "grpcio-1.75.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:cb6c5b075c2d092f81138646a755f0dad94e4622300ebef089f94e6308155d82"}, - {file = "grpcio-1.75.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:494dcbade5606128cb9f530ce00331a90ecf5e7c5b243d373aebdb18e503c346"}, - {file = "grpcio-1.75.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:050760fd29c8508844a720f06c5827bb00de8f5e02f58587eb21a4444ad706e5"}, - {file = "grpcio-1.75.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:266fa6209b68a537b2728bb2552f970e7e78c77fe43c6e9cbbe1f476e9e5c35f"}, - {file = "grpcio-1.75.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:06d22e1d8645e37bc110f4c589cb22c283fd3de76523065f821d6e81de33f5d4"}, - {file = "grpcio-1.75.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9880c323595d851292785966cadb6c708100b34b163cab114e3933f5773cba2d"}, - {file = "grpcio-1.75.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:55a2d5ae79cd0f68783fb6ec95509be23746e3c239290b2ee69c69a38daa961a"}, - {file = "grpcio-1.75.0-cp311-cp311-win32.whl", hash = "sha256:352dbdf25495eef584c8de809db280582093bc3961d95a9d78f0dfb7274023a2"}, - {file = "grpcio-1.75.0-cp311-cp311-win_amd64.whl", hash = "sha256:678b649171f229fb16bda1a2473e820330aa3002500c4f9fd3a74b786578e90f"}, - {file = "grpcio-1.75.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fa35ccd9501ffdd82b861809cbfc4b5b13f4b4c5dc3434d2d9170b9ed38a9054"}, - {file = "grpcio-1.75.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:0fcb77f2d718c1e58cc04ef6d3b51e0fa3b26cf926446e86c7eba105727b6cd4"}, - {file = "grpcio-1.75.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36764a4ad9dc1eb891042fab51e8cdf7cc014ad82cee807c10796fb708455041"}, - {file = "grpcio-1.75.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:725e67c010f63ef17fc052b261004942763c0b18dcd84841e6578ddacf1f9d10"}, - {file = "grpcio-1.75.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91fbfc43f605c5ee015c9056d580a70dd35df78a7bad97e05426795ceacdb59f"}, - {file = "grpcio-1.75.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a9337ac4ce61c388e02019d27fa837496c4b7837cbbcec71b05934337e51531"}, - {file = "grpcio-1.75.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ee16e232e3d0974750ab5f4da0ab92b59d6473872690b5e40dcec9a22927f22e"}, - {file = "grpcio-1.75.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55dfb9122973cc69520b23d39867726722cafb32e541435707dc10249a1bdbc6"}, - {file = "grpcio-1.75.0-cp312-cp312-win32.whl", hash = "sha256:fb64dd62face3d687a7b56cd881e2ea39417af80f75e8b36f0f81dfd93071651"}, - {file = "grpcio-1.75.0-cp312-cp312-win_amd64.whl", hash = "sha256:6b365f37a9c9543a9e91c6b4103d68d38d5bcb9965b11d5092b3c157bd6a5ee7"}, - {file = "grpcio-1.75.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:1bb78d052948d8272c820bb928753f16a614bb2c42fbf56ad56636991b427518"}, - {file = "grpcio-1.75.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:9dc4a02796394dd04de0b9673cb79a78901b90bb16bf99ed8cb528c61ed9372e"}, - {file = "grpcio-1.75.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:437eeb16091d31498585d73b133b825dc80a8db43311e332c08facf820d36894"}, - {file = "grpcio-1.75.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c2c39984e846bd5da45c5f7bcea8fafbe47c98e1ff2b6f40e57921b0c23a52d0"}, - {file = "grpcio-1.75.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38d665f44b980acdbb2f0e1abf67605ba1899f4d2443908df9ec8a6f26d2ed88"}, - {file = "grpcio-1.75.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e8e752ab5cc0a9c5b949808c000ca7586223be4f877b729f034b912364c3964"}, - {file = "grpcio-1.75.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3a6788b30aa8e6f207c417874effe3f79c2aa154e91e78e477c4825e8b431ce0"}, - {file = "grpcio-1.75.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc33e67cab6141c54e75d85acd5dec616c5095a957ff997b4330a6395aa9b51"}, - {file = "grpcio-1.75.0-cp313-cp313-win32.whl", hash = "sha256:c8cfc780b7a15e06253aae5f228e1e84c0d3c4daa90faf5bc26b751174da4bf9"}, - {file = "grpcio-1.75.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c91d5b16eff3cbbe76b7a1eaaf3d91e7a954501e9d4f915554f87c470475c3d"}, - {file = "grpcio-1.75.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:0b85f4ebe6b56d2a512201bb0e5f192c273850d349b0a74ac889ab5d38959d16"}, - {file = "grpcio-1.75.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:68c95b1c1e3bf96ceadf98226e9dfe2bc92155ce352fa0ee32a1603040e61856"}, - {file = "grpcio-1.75.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:153c5a7655022c3626ad70be3d4c2974cb0967f3670ee49ece8b45b7a139665f"}, - {file = "grpcio-1.75.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:53067c590ac3638ad0c04272f2a5e7e32a99fec8824c31b73bc3ef93160511fa"}, - {file = "grpcio-1.75.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:78dcc025a144319b66df6d088bd0eda69e1719eb6ac6127884a36188f336df19"}, - {file = "grpcio-1.75.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ec2937fd92b5b4598cbe65f7e57d66039f82b9e2b7f7a5f9149374057dde77d"}, - {file = "grpcio-1.75.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:597340a41ad4b619aaa5c9b94f7e6ba4067885386342ab0af039eda945c255cd"}, - {file = "grpcio-1.75.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0aa795198b28807d28570c0a5f07bb04d5facca7d3f27affa6ae247bbd7f312a"}, - {file = "grpcio-1.75.0-cp39-cp39-win32.whl", hash = "sha256:585147859ff4603798e92605db28f4a97c821c69908e7754c44771c27b239bbd"}, - {file = "grpcio-1.75.0-cp39-cp39-win_amd64.whl", hash = "sha256:eafbe3563f9cb378370a3fa87ef4870539cf158124721f3abee9f11cd8162460"}, - {file = "grpcio-1.75.0.tar.gz", hash = "sha256:b989e8b09489478c2d19fecc744a298930f40d8b27c3638afbfe84d22f36ce4e"}, -] - -[package.dependencies] -typing-extensions = ">=4.12,<5.0" - -[package.extras] -protobuf = ["grpcio-tools (>=1.75.0)"] - -[[package]] -name = "grpcio-health-checking" -version = "1.75.0" -description = "Standard Health Checking Service for gRPC" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "grpcio_health_checking-1.75.0-py3-none-any.whl", hash = "sha256:0390b6ad4ba07b0bb9ff5b01bd9b15147641a2bb6a54d53a58f5df59d4510452"}, - {file = "grpcio_health_checking-1.75.0.tar.gz", hash = "sha256:4de5eb270962840f60e189ebddbfcfe232b5ecfcf618dd534116a6f6684b40bf"}, -] - -[package.dependencies] -grpcio = ">=1.75.0" -protobuf = ">=6.31.1,<7.0.0" - -[[package]] -name = "grpcio-reflection" -version = "1.75.0" -description = "Standard Protobuf Reflection Service for gRPC" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "grpcio_reflection-1.75.0-py3-none-any.whl", hash = "sha256:cef02b79eea9a1a5e379e13e503a5eaa32a4bc1ad2f1c24d3a6e28a95db5569f"}, - {file = "grpcio_reflection-1.75.0.tar.gz", hash = "sha256:60bb9865f5411ad589420eadcdd7e0c338c0aabedf5d4cd07363d5bf7da9c972"}, -] - -[package.dependencies] -grpcio = ">=1.75.0" -protobuf = ">=6.31.1,<7.0.0" - -[[package]] -name = "grpcio-status" -version = "1.75.0" -description = "Status proto mapping for gRPC" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "grpcio_status-1.75.0-py3-none-any.whl", hash = "sha256:de62557ef97b7e19c3ce6da19793a12c5f6c1fbbb918d233d9671aba9d9e1d78"}, - {file = "grpcio_status-1.75.0.tar.gz", hash = "sha256:69d5b91be1b8b926f086c1c483519a968c14640773a0ccdd6c04282515dbedf7"}, -] - -[package.dependencies] -googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.75.0" -protobuf = ">=6.31.1,<7.0.0" - -[[package]] -name = "grpcio-tools" -version = "1.75.0" -description = "Protobuf code generator for gRPC" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "grpcio_tools-1.75.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:5ca29b0ae735044c6a48072cf7bf53e34ce9ab03eec66acaf2173071d4f66d8a"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d1a224887f70981683dfcaacc253c08f3680b919c0b2353fbb57f89b27e1c9b9"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c49649d2b46a5a09419631adec105b05bcb016e5727c8f1b08ac8e16d9b0e3e0"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c944610bc009185f3da399030a2a8a9d550ae3246f93ad20ff63593fa883ddfb"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:186c11fe9c8ef90b0862013b61876693644c952fda8fffef6ab0de0a83f90479"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:688668666265a8f3e5eb86f73694e8adac2d2cc5f40c90249ce80bf6c6cec9ea"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9083fe53cbe17b972d9ede47b1e6c82ec532a91770d41c790c4f9b39291041c3"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3072b10f4ad82739650aa9d667b536de8d4973083236215b7bf2389ba75bb507"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-win32.whl", hash = "sha256:c42fc86ab55018ba5afe2aa95d6d34e2e763da06eff23c08bed487a556341071"}, - {file = "grpcio_tools-1.75.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e0c8d5d4bdce7f32e2fef3e2304cdca1fbb16a6469c7d3bce38884ee4c449d1"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:6c3b8dbe8b2ad7df4ba661b5ee29ae8fe79d2715aade519847deaef26f5c1a06"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:cdbccc5a4809ef9414b7c434dd1aabc94b66a01c01c13ecc1edba9f8f4277b44"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:16a9597d1bd4143a71bfae341a32952a64c094a63d3d0bdd24b21fdc8b843846"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:899c46520446ad1935f5899729746b390e13085e9757d043401298b18fa37d99"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53c116d0d5df70845330eefb98ef4242ff09be264a22bc5e18f171a3047c9e66"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:495ce168f996d4c42328e17b788d51d808fc585a80612fe70943c00ac16d0fca"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:26f1f3cedebe465f97b5aad312fb775a4bd53a0e88d08c4000e588c195519eca"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82692be482cdcf7ac9b79563dbea99333835aaa3f5e7f0641689766b64b91543"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-win32.whl", hash = "sha256:fd038847974aeb883ee0f3b5b535d85618ad32789c15c9bf24af6c12a44f67f1"}, - {file = "grpcio_tools-1.75.0-cp311-cp311-win_amd64.whl", hash = "sha256:5c5465cd7b83c34f3c987a235fe3b04012411502d4bc66de5a34b238617ded4c"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:6ded12c79fb56ceae0ce60e653453159bfc2ccb044922b7e7d721de6c8e04506"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ebdac7cc820459874f3b19eddddae19c0c7e7cdf228aee8e7567cec1fddb2ae3"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:509ec0ce7c4269c2bea6015efcdcde00a5d55d97c88ad17587b4247cdc3d2fe8"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:a68a8dcbcbd1df33e7c08c2ceeb69ed8fd53e235784ac680dfe3fc1e89aac2ac"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ac8a663e955bf3188f76d93d7fdc656f346ff54ea7e512eb034374c6fd61b50"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c30cb36ae1a4ed5fb1960f4bc0000548fecb9ff21a51d78a1f54e3424f971c0"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:35d4368794506db2b0acde60e7e2bae21255cc0d05db9ffc078510ab6a84ff4f"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edefbb90bb7ddc4eadac3463d5f7084e1d43b1d713254f668dd55c25db5b5ef2"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-win32.whl", hash = "sha256:c2bad23bd0d43acd9d7032b6ffb04f5eb176d853cd32967eb2c4a39044c81cfe"}, - {file = "grpcio_tools-1.75.0-cp312-cp312-win_amd64.whl", hash = "sha256:0f4f31035a5178acd924a052b8954d5ac71319092b57e3711438ca6518b71017"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:69742254df93323275b7ee5ac017e3b9fdba8ecc6dca00bd6b2cd1c70c80a9c2"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a07aa71ad96103b18bb84dc069dd139897356116d2aaa68d3df84d4d59701ae8"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dcfb12654fb1d6ce84f4a55d3dfbc267a04d53dc9b52ee0974b2110d02f68dac"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:990d183fee5a2ef9d4f3a220b6506f5da740271da175efcb7e4e34ebc3191a12"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39c6ff052960a3301cd920549384a2ad7cb3165c778feed601cae2a2131b63f8"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:60bd449814fe3cebeda11c0cda3a3adffd81941559aa254e6d153751baa0cffc"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:91e430e9368afc38e94645f744840ab06995cfb7312233623c5d7370f8c0dd7c"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3351acef4b8897e99bdceae5cfcc300e1e5c1d88c0fc2ffc2b5ca1bd5ce4ced8"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-win32.whl", hash = "sha256:1241f8c65f2429f00d9e15e819aca2138c5aa571f0ac644ab658a0281dc177d9"}, - {file = "grpcio_tools-1.75.0-cp313-cp313-win_amd64.whl", hash = "sha256:193ce6aef33417849289cbb518402fe60c00d0fa66d68ea9a30c98cb8818280c"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:b9f64ab078f1e8ea09ceb72c3f7a55b9cbec515fd20e804aea78491adf785503"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7154a35243a49704782b39e8780d9a0adb393a9cedba2ab65c352e94ff42fe8c"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9a620de24caa85b102d2416c3f679260d1d4103edcc2806d7dda43aad1913e01"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08cc1b8a1364a5b8f975e6a7273684d13630caab76c209a201464ad05f826eb9"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a0c899175dd23e96f61b3ab8153642e0ae0182b9c9a582cd0cc4702a056d845"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8707b63acb1e08c4031e959936af45487bc185a3fa1ae37fdac465e8ab311774"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4d28cb03efb871a0ce13dc0fe1416c237ed6d70c42f19a64cef24aba88dd7c5f"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:93b297f77a3f9fe99ea30597e98fd62d3d40bc2520f3e6c6c12b202710a2581d"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-win32.whl", hash = "sha256:05087b1879b3f32a2182f1365e34233236c22e1a1e8cc448b5d29ea58d661846"}, - {file = "grpcio_tools-1.75.0-cp39-cp39-win_amd64.whl", hash = "sha256:aaec9c9b1cb0ff3823961e74b6cf0a1e6b0e7a82fa2fb0b2bc7b312978bd34f7"}, - {file = "grpcio_tools-1.75.0.tar.gz", hash = "sha256:eb5e4025034d92da3c81fd5e3468c33d5ae7571b07a72c385b5ec1746658573f"}, -] - -[package.dependencies] -grpcio = ">=1.75.0" -protobuf = ">=6.31.1,<7.0.0" -setuptools = "*" - -[[package]] -name = "idna" -version = "3.19" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4"}, - {file = "idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15"}, -] - -[package.extras] -all = ["coverage (>=7.10.0)", "hypothesis (>=6.141.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.16.0)", "ty (>=0.0.37)"] - -[[package]] -name = "isort" -version = "8.0.1" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.10.0" -groups = ["dev"] -files = [ - {file = "isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75"}, - {file = "isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d"}, -] - -[package.extras] -colors = ["colorama"] - -[[package]] -name = "jsonschema" -version = "4.26.0" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce"}, - {file = "jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.25.0" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, - {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, -] - -[package.dependencies] -referencing = ">=0.31.0" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy-protobuf" -version = "5.1.0" -description = "Generate mypy stub files from protobuf specs" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mypy_protobuf-5.1.0-py3-none-any.whl", hash = "sha256:d7031f563f806b8bcd448a3e86768bf0994c0af5b0017b344a4fbbba4191f43c"}, - {file = "mypy_protobuf-5.1.0.tar.gz", hash = "sha256:8493758852a9cdc075a11dbe96c6e37ea2feba5fd5f33ef7442b9a275322a94c"}, -] - -[package.dependencies] -protobuf = ">=4.25.3" -types-protobuf = ">=4.24" - -[[package]] -name = "opentelemetry-api" -version = "1.44.0" -description = "OpenTelemetry Python API" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "opentelemetry_api-1.44.0-py3-none-any.whl", hash = "sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef"}, - {file = "opentelemetry_api-1.44.0.tar.gz", hash = "sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a"}, -] - -[package.dependencies] -typing-extensions = ">=4.5.0" - -[[package]] -name = "opentelemetry-sdk" -version = "1.44.0" -description = "OpenTelemetry Python SDK" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "opentelemetry_sdk-1.44.0-py3-none-any.whl", hash = "sha256:df081c4c6bcfdb1211e3e86140376792643128a25f8d72d1d27675936e7e96ad"}, - {file = "opentelemetry_sdk-1.44.0.tar.gz", hash = "sha256:cebe7f65dc12f26ead75c6064de12fd2a9052e5060c0272d402cfa203aae123b"}, -] - -[package.dependencies] -opentelemetry-api = "1.44.0" -opentelemetry-semantic-conventions = "0.65b0" -typing-extensions = ">=4.5.0" - -[package.extras] -file-configuration = ["opentelemetry-configuration (==0.65b0)"] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.65b0" -description = "OpenTelemetry Semantic Conventions" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "opentelemetry_semantic_conventions-0.65b0-py3-none-any.whl", hash = "sha256:1cacde7b0ad306f84c5ef08c3dbe1bbaf20165bba6f8bff43b670e555a086bcb"}, - {file = "opentelemetry_semantic_conventions-0.65b0.tar.gz", hash = "sha256:f9b2b81e9d5b64f11bc952075e7e9c7fb0aab075c7fd1c46d597f1b919852d60"}, -] - -[package.dependencies] -opentelemetry-api = "1.44.0" -typing-extensions = ">=4.5.0" - -[[package]] -name = "osv" -version = "0.1.2" -description = "Open Source Vulnerabilities library" -optional = false -python-versions = ">=3.13,<4.0" -groups = ["main"] -files = [] -develop = true - -[package.dependencies] -attrs = ">=23.2" -google-cloud-logging = ">=3.10" -google-cloud-ndb = ">=2.3" -google-cloud-pubsub = ">=2.31.1" -google-cloud-storage = ">=2.17" -grpcio = ">=1.0" -jsonschema = ">=4.0" -packageurl-python = ">=0.17.0" -packaging-legacy = ">=23.0.post0" -pygit2 = ">=1.14.0" -pyyaml = ">=6.0" -requests = ">=2.33" -semver = ">=3.0" - -[package.source] -type = "directory" -url = "../.." - -[[package]] -name = "packageurl-python" -version = "0.17.6" -description = "A purl aka. Package URL parser and builder" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packageurl_python-0.17.6-py3-none-any.whl", hash = "sha256:31a85c2717bc41dd818f3c62908685ff9eebcb68588213745b14a6ee9e7df7c9"}, - {file = "packageurl_python-0.17.6.tar.gz", hash = "sha256:1252ce3a102372ca6f86eb968e16f9014c4ba511c5c37d95a7f023e2ca6e5c25"}, -] - -[package.extras] -build = ["setuptools", "wheel"] -lint = ["black", "isort", "mypy"] -sqlalchemy = ["sqlalchemy (>=2.0.0)"] -test = ["pytest"] - -[[package]] -name = "packaging" -version = "26.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, - {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, -] - -[[package]] -name = "packaging-legacy" -version = "23.0.post0" -description = "Core utilities for legacy Python packages" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "packaging_legacy-23.0.post0-py3-none-any.whl", hash = "sha256:6cd21cd283c09409349bccc10bb55bfd837b4aab86a7b0f87bfcb8dd9831a8a3"}, - {file = "packaging_legacy-23.0.post0.tar.gz", hash = "sha256:c974a42291a77112313f0198b87ad96e07a3c357295d572560a4b9c368f7d9db"}, -] - -[package.dependencies] -packaging = ">=23.0" - -[[package]] -name = "platformdirs" -version = "4.11.3" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "platformdirs-4.11.3-py3-none-any.whl", hash = "sha256:5ed065d443751de711da036041a7a214122efc4a4de393b3f4137ba5576540e7"}, - {file = "platformdirs-4.11.3.tar.gz", hash = "sha256:66a73d38a849810252df809a3d8bcbda8e26f6c189920e7535ad608a48dbb5ab"}, -] - -[[package]] -name = "proto-plus" -version = "1.28.3" -description = "Beautiful, Pythonic protocol buffers" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "proto_plus-1.28.3-py3-none-any.whl", hash = "sha256:dc76880b8ee951cca002098574376cf71e055f9f16d9ba6570fb8a06f726d281"}, - {file = "proto_plus-1.28.3.tar.gz", hash = "sha256:5f91b30dafa6bb38d432c5557a6ee1d35ffd40b4b1e0e3ca27260448560b91d9"}, -] - -[package.dependencies] -protobuf = ">=6.33.5,<8.0.0" - -[package.extras] -testing = ["google-api-core (>=2.25.0)"] - -[[package]] -name = "protobuf" -version = "6.33.6" -description = "" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3"}, - {file = "protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326"}, - {file = "protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a"}, - {file = "protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2"}, - {file = "protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3"}, - {file = "protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593"}, - {file = "protobuf-6.33.6-cp39-cp39-win32.whl", hash = "sha256:bd56799fb262994b2c2faa1799693c95cc2e22c62f56fb43af311cae45d26f0e"}, - {file = "protobuf-6.33.6-cp39-cp39-win_amd64.whl", hash = "sha256:f443a394af5ed23672bc6c486be138628fbe5c651ccbc536873d7da23d1868cf"}, - {file = "protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901"}, - {file = "protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135"}, -] - -[[package]] -name = "pyasn1" -version = "0.6.4" -description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b"}, - {file = "pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81"}, -] - -[[package]] -name = "pyasn1-modules" -version = "0.4.2" -description = "A collection of ASN.1-based protocols modules" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, - {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, -] - -[package.dependencies] -pyasn1 = ">=0.6.1,<0.7.0" - -[[package]] -name = "pycparser" -version = "3.0" -description = "C parser in Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "implementation_name != \"PyPy\"" -files = [ - {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, - {file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"}, -] - -[[package]] -name = "pygit2" -version = "1.20.0" -description = "Python bindings for libgit2." -optional = false -python-versions = ">=3.11" -groups = ["main"] -files = [ - {file = "pygit2-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:97a903fdd3e6e99d22bac87f0aaf439ccb9ad86bb4c9e15655188e97c0e53513"}, - {file = "pygit2-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0bba0fd1568d9ba2948cdfe0680019cb0b8c1d58e54825856b6e31ebf13a1398"}, - {file = "pygit2-1.20.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12a837854f1e7fc2c141eb2ac193a316e26812ca04ad4ed977ed9be92e598d27"}, - {file = "pygit2-1.20.0-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d458c50de33f5d0f086122ecf87651aba4291a89d99d79ef46976ed9088cbd0b"}, - {file = "pygit2-1.20.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:45403d9f0c38df1ab2219d22e5e3fca6a7d40c27784a2a771a629a1c05a6a055"}, - {file = "pygit2-1.20.0-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e9b7f915c8a555f2300ec231652e91303b67654465f26669fb86a6c7b2d2dcc8"}, - {file = "pygit2-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2ec2ade07eb56515f8fa4864efafb472ffac1715431338c985039273852aee8e"}, - {file = "pygit2-1.20.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:db972efa0a0f3bd2f3f215cb5cbb3cca68b11fd638400167e0ec4c4d5726fb15"}, - {file = "pygit2-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5d6cfc3684ac7ca944e7172f0518adcbc93c1d7711705c4b36ef70218fc721ce"}, - {file = "pygit2-1.20.0-cp311-cp311-win32.whl", hash = "sha256:8c9073e38f216e937cfc69d8d93e97a59c59f06dfa67c557134b10f8927503d2"}, - {file = "pygit2-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:c4800ad2643e0cb540f802973866b16480d45da2c8237a56c0b9405460ea97e6"}, - {file = "pygit2-1.20.0-cp311-cp311-win_arm64.whl", hash = "sha256:83f1ccb0455e801b70dc13b4fec6dcc5ed0412ab7a38b986e0f559c9ed0bf7e2"}, - {file = "pygit2-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dec857dcd64cfb3f52d2e79faa53ff9fc34a6db2ed5d3f865bf5a7239177de56"}, - {file = "pygit2-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9374c6f8d8ee0ba01a58238416ed2b24d4c154a8823ebc6b638a7ee4a480415"}, - {file = "pygit2-1.20.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62bd32dcfc1982e1cb2218ca38c14c511caf970ec64ae2d39e6aa0d9d62dc4f0"}, - {file = "pygit2-1.20.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9a540410f9f893c5e68e221d1df21319fe5add04e45d51b2329ce0fa14516b7"}, - {file = "pygit2-1.20.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b2be2e29283268a2842342c7d4e206e1d8326dfc71598743fa8ef61ef6d744d5"}, - {file = "pygit2-1.20.0-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8119ef9b00587f243ee5462dfe5dc71aad600cf1b25b268bffa67065d3412439"}, - {file = "pygit2-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7150878d113dee6291ca2d17876729b739e762d1585d90459aa61b34bd662de7"}, - {file = "pygit2-1.20.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a79cf9048797997cc3610726b7b2827d6459961208553b187522786ab5ffc0f2"}, - {file = "pygit2-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:97619aa269f31f2b94e83078b5c6df4e7a0cc9bdfe5b672378c10ed09d50c360"}, - {file = "pygit2-1.20.0-cp312-cp312-win32.whl", hash = "sha256:d49d7bb4987fabece611f4f873bfe490e574f0bb5bb4fafe557d4d504efe3026"}, - {file = "pygit2-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c16126d27218e7b5d059e5072b15819261ec3ad515ff45ed611fa1687993c25"}, - {file = "pygit2-1.20.0-cp312-cp312-win_arm64.whl", hash = "sha256:3f7b97f577d7d15d59fbef8e3f6f6943bdc5e315cbf6d4c0478984858e55b0ea"}, - {file = "pygit2-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9217790892f6c9faf38737a078ac36203bd082b226e0b0ba68d8930f4ba7737b"}, - {file = "pygit2-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c21715780541121c9784a7e4372c32b440b3ac67452b5364af738f5f4b82f8e1"}, - {file = "pygit2-1.20.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23a1879065591b65b26f58048450629c2cdbdaa2a65328eee27d908614b963db"}, - {file = "pygit2-1.20.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4e51e9a9a4aa15bd570d57c5febc0142b86a19c4f2b579e9a33d6c170189ff9b"}, - {file = "pygit2-1.20.0-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c7fba542df37ef208dba315579df6ea62c841ed60101154e15e811480f3f5ae"}, - {file = "pygit2-1.20.0-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4600c5cf14895bd7a4ffac14932969c02b6fd7ce2a047b004f3397bdc040425c"}, - {file = "pygit2-1.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229bbd20bde6a9fcceee732ca4bc30360d375b0db439e19c6b9a0e1d3bb4311e"}, - {file = "pygit2-1.20.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:223be35205f6dd4ed9fd35ae9feca59fb1a98913de090d8716c5830495e31901"}, - {file = "pygit2-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:46b6bac662c55cfbdc137f131050317d56e508a4506ab2e8ac01518f33a29bcd"}, - {file = "pygit2-1.20.0-cp313-cp313-win32.whl", hash = "sha256:a455a1d9714b49ba36fc45985d58debe3d6d07fbaa187cd217f79713ae1d1af7"}, - {file = "pygit2-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:451bfe3ec1719419157f49423d1a13a90788df78d2e97fe53efd2c872dbdf8a1"}, - {file = "pygit2-1.20.0-cp313-cp313-win_arm64.whl", hash = "sha256:e9f715e5ddef14eb4ba344b1deca8a82a2015acb01f6aa0a79eaea122dfc9384"}, - {file = "pygit2-1.20.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:79dece6a15fee4e0c1fc9231b3156327933adbdd6d21c2070e3c06c26add3d73"}, - {file = "pygit2-1.20.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:15907f5b6e28dafd54062e41e9375cb4dc2f9cc37ffad58b0195dfeab58b3325"}, - {file = "pygit2-1.20.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68b4cc2a442b59202c038214aa93ea9db8bb222429552e545afe0a98a5ccb004"}, - {file = "pygit2-1.20.0-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9c63594002feaa4b406dd3b133b4d40a1c53dd285f9bbf3156af89647f2ddf2"}, - {file = "pygit2-1.20.0-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b99f40e2b3ea98e8ff905958db279d21e735d2fb5888818b359480f0883c2d"}, - {file = "pygit2-1.20.0-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cf93fd9dea38087f6642837bfeb6db722b661ea9dd90b604d06810063ff030e6"}, - {file = "pygit2-1.20.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:115580869b6c78bbf07b24f389c8a146c6f080e55121ada2506b3ca29710dead"}, - {file = "pygit2-1.20.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:37e0db3a58603531d6ceef3b36b0bab645a2a81b91c1b8e0e1111f34d67fa61d"}, - {file = "pygit2-1.20.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93ed5d0c305c265b97337844b2cecc8075ff05f657475ffcefbe985de8b04134"}, - {file = "pygit2-1.20.0-cp314-cp314-win32.whl", hash = "sha256:c7ec615560faf3a4f868263df680954bf3deb74cefd24be63a2caad4ffadf6f9"}, - {file = "pygit2-1.20.0-cp314-cp314-win_amd64.whl", hash = "sha256:4ae51387c4016966703f990fad2a1adf60f594239d8a0f5b308ec82921e3b582"}, - {file = "pygit2-1.20.0-cp314-cp314-win_arm64.whl", hash = "sha256:055057b36ce141ccc6ce600475c3f8ce0edadad45c78b480cdcb61da29f7ecf7"}, - {file = "pygit2-1.20.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:00415a2aad3a4e1eeeb1da5c0d805e992971e849a1a7d9f604c8e0381dff70fa"}, - {file = "pygit2-1.20.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4207f901c377410f3d85df259d32903af1523258150968a1add805f40daad8bc"}, - {file = "pygit2-1.20.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4cf13d0b12fc1df25953a23c3112f4cb8abed1802b0479b317086f79f5d1511"}, - {file = "pygit2-1.20.0-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d3b2db875ff92b288bb23cdb4bd398b045869e98c8dde55ba49d9abf264f19fc"}, - {file = "pygit2-1.20.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fb2becd2cee9b610da613902aeca5426ac8c4a09e16344e1f75802f2ffe2bf3"}, - {file = "pygit2-1.20.0-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cbbe502c735ee630be31be1c6e20f9ef35f980c492301c77969ce3d883242ffc"}, - {file = "pygit2-1.20.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:254e0b61201c78b1cabe87d06d60891676311da07b5393a04e0ac813c130d583"}, - {file = "pygit2-1.20.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:7cb0607755a251ca5ab942f1f45796dddb03b8499770f8c97d8de5f3616233ee"}, - {file = "pygit2-1.20.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c23e5405d6837760d3de58635492221067ee2d89c000c7b53e39e679b378ee1"}, - {file = "pygit2-1.20.0-cp314-cp314t-win32.whl", hash = "sha256:20dd3576c05cc94dd6ad6650d0a937593de6115bcbfeba37414dd57fa33c9b8a"}, - {file = "pygit2-1.20.0-cp314-cp314t-win_amd64.whl", hash = "sha256:82006722c4aacd4f5e4c93cd8914d1dd1521ec77a71c37f50ac58bc4f6b563f4"}, - {file = "pygit2-1.20.0-cp314-cp314t-win_arm64.whl", hash = "sha256:3831bf91550c487540f3e2d5cea211689b8e65b4589e812d3c335cb5500a5eae"}, - {file = "pygit2-1.20.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e6432cf4eb368366affb1bce4eaac750f03ee440defe5ee185557ba7e656909a"}, - {file = "pygit2-1.20.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:aff3a6312006e668ce35983ef3c8d3c27115a37ce9ceb6ce316422f25d44b01d"}, - {file = "pygit2-1.20.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8108f420748549807603e5463f46df123844a7b5c0123d990d1107f308a3171f"}, - {file = "pygit2-1.20.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de6729361800d312f9062c53a218f141af8909bad44c4d0064995728a284e8ae"}, - {file = "pygit2-1.20.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e063d809e5ae8624a3c29a9743417670f2f57ab27fcf300f4e35575f9f30be79"}, - {file = "pygit2-1.20.0.tar.gz", hash = "sha256:7253735629c22fff412a72c48c204b19c206fda9fcb01e51113d9689194cb1cf"}, -] - -[package.dependencies] -cffi = ">=2.0" - -[[package]] -name = "pylint" -version = "4.0.7" -description = "python code static checker" -optional = false -python-versions = ">=3.10.0" -groups = ["dev"] -files = [ - {file = "pylint-4.0.7-py3-none-any.whl", hash = "sha256:be4a3111557a614411ed1fc89347ce4a8e1013a59e1f33d11485227a02e3304d"}, - {file = "pylint-4.0.7.tar.gz", hash = "sha256:9b2d1d15791c84b77a4fe2aafe8f0d9570717e2dea06d53b19c105cf60275a52"}, -] - -[package.dependencies] -astroid = ">=4.0.2,<=4.1.dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} -isort = ">=5,<5.13 || >5.13,<9" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2" -tomlkit = ">=0.10.1" - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -name = "pymemcache" -version = "4.0.0" -description = "A comprehensive, fast, pure Python memcached client" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "pymemcache-4.0.0-py2.py3-none-any.whl", hash = "sha256:f507bc20e0dc8d562f8df9d872107a278df049fa496805c1431b926f3ddd0eab"}, - {file = "pymemcache-4.0.0.tar.gz", hash = "sha256:27bf9bd1bbc1e20f83633208620d56de50f14185055e49504f4f5e94e94aff94"}, -] - -[[package]] -name = "pytz" -version = "2026.3.post1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "pytz-2026.3.post1-py2.py3-none-any.whl", hash = "sha256:dd95840dd199baea12d9cc096a1d452caa6596a1c1e4b5f3dbd1541855d5e815"}, - {file = "pytz-2026.3.post1.tar.gz", hash = "sha256:2211d3fcf9a797d3405cac96ac7f61d80e6a644f72a3309607282fe8a2010c5d"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, - {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, - {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, - {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, - {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, - {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, - {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, - {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, - {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, - {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, - {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, - {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, - {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, - {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, - {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, - {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, - {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, - {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, - {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, - {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, - {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, - {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, - {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, - {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, - {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, - {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, - {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, - {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, - {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, - {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, - {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, - {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, - {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, - {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, - {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, - {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, - {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, - {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, - {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, - {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, - {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, - {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, - {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, - {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, - {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, - {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, - {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, - {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, - {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, - {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, - {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, - {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, - {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, - {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, - {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, - {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, - {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, - {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, - {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, - {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, - {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, - {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, - {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, - {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, - {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, - {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, - {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, - {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, - {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, - {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, -] - -[[package]] -name = "redis" -version = "6.4.0" -description = "Python client for Redis database and key-value store" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f"}, - {file = "redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010"}, -] - -[package.extras] -hiredis = ["hiredis (>=3.2.0)"] -jwt = ["pyjwt (>=2.9.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (>=20.0.1)", "requests (>=2.31.0)"] - -[[package]] -name = "referencing" -version = "0.37.0" -description = "JSON Referencing + Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, - {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" - -[[package]] -name = "requests" -version = "2.33.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b"}, - {file = "requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652"}, -] - -[package.dependencies] -certifi = ">=2023.5.7" -charset_normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.26,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -test = ["PySocks (>=1.5.6,!=1.5.7)", "pytest (>=3)", "pytest-cov", "pytest-httpbin (==2.1.0)", "pytest-mock", "pytest-xdist"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] - -[[package]] -name = "rpds-py" -version = "2026.6.3" -description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.11" -groups = ["main"] -files = [ - {file = "rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7"}, - {file = "rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911"}, - {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4"}, - {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261"}, - {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278"}, - {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9"}, - {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7"}, - {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3"}, - {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da"}, - {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4"}, - {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6"}, - {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93"}, - {file = "rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a"}, - {file = "rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127"}, - {file = "rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804"}, - {file = "rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0"}, - {file = "rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf"}, - {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24"}, - {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e"}, - {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975"}, - {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680"}, - {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6"}, - {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a"}, - {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4"}, - {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa"}, - {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc"}, - {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822"}, - {file = "rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed"}, - {file = "rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f"}, - {file = "rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96"}, - {file = "rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223"}, - {file = "rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f"}, - {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f"}, - {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7"}, - {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6"}, - {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af"}, - {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf"}, - {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885"}, - {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4"}, - {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7"}, - {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d"}, - {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97"}, - {file = "rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0"}, - {file = "rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80"}, - {file = "rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb"}, - {file = "rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e"}, - {file = "rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd"}, - {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d"}, - {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda"}, - {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8"}, - {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53"}, - {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504"}, - {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc"}, - {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77"}, - {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698"}, - {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd"}, - {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d"}, - {file = "rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8"}, - {file = "rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5"}, - {file = "rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e"}, - {file = "rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2"}, - {file = "rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13"}, - {file = "rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05"}, - {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba"}, - {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617"}, - {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9"}, - {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb"}, - {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885"}, - {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a"}, - {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868"}, - {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187"}, - {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107"}, - {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba"}, - {file = "rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369"}, - {file = "rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146"}, - {file = "rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577"}, - {file = "rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76"}, - {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826"}, - {file = "rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4"}, -] - -[[package]] -name = "semver" -version = "3.0.4" -description = "Python helper for Semantic Versioning (https://semver.org)" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"}, - {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, -] - -[[package]] -name = "setuptools" -version = "84.0.0" -description = "Most extensible Python build backend with support for C/C++ extension modules" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670"}, - {file = "setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.14)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.13.0) ; sys_platform != \"cygwin\""] -core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=3.4)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.18.*)", "pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] - -[[package]] -name = "tomlkit" -version = "0.15.1" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304"}, - {file = "tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97"}, -] - -[[package]] -name = "types-protobuf" -version = "7.34.1.20260816" -description = "Typing stubs for protobuf" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -files = [ - {file = "types_protobuf-7.34.1.20260816-py3-none-any.whl", hash = "sha256:2e3a225b3c21f0022daa34cec155f03858bb9aff9792947b8e71f0e9f1791fdb"}, - {file = "types_protobuf-7.34.1.20260816.tar.gz", hash = "sha256:6f43846e7a3cc2621abfa79e2bb03461e6769a74cdae7462efaddf84580fd5c1"}, -] - -[[package]] -name = "typing-extensions" -version = "4.16.0" -description = "Backported and Experimental Type Hints for Python 3.9+" -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"}, - {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"}, -] - -[[package]] -name = "urllib3" -version = "2.7.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, - {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, -] - -[package.extras] -brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] - -[[package]] -name = "yapf" -version = "0.43.0" -description = "A formatter for Python code" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca"}, - {file = "yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e"}, -] - -[package.dependencies] -platformdirs = ">=3.5.1" - -[metadata] -lock-version = "2.1" -python-versions = ">=3.13,<4.0" -content-hash = "c449d1077dbb8531b77ded4bd5ef87a52adee4a56201b6e330cf2272c655de64" diff --git a/gcp/api/pyproject.toml b/gcp/api/pyproject.toml deleted file mode 100644 index fca2bc8abd5..00000000000 --- a/gcp/api/pyproject.toml +++ /dev/null @@ -1,30 +0,0 @@ -[project] -name = "api-backend" -requires-python = ">=3.13,<4.0" -dependencies = [ - "google-cloud-ndb==2.4.0", - "google-cloud-logging==3.13.0", - "packaging==26.0", - "requests==2.33.0", - "grpcio==1.75.0", - "grpcio-reflection==1.75.0", - "grpcio-health-checking==1.75.0", - "osv", -] - -[tool.poetry] -package-mode = false - - -[tool.poetry.dependencies] -osv = { path = "../../", develop = true } - -[tool.poetry.group.dev.dependencies] -grpcio-tools = "*" -mypy-protobuf = "^5.0.0" -yapf = "*" -pylint = "*" - -[build-system] -requires = ["poetry-core>=2.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/gcp/api/run_apitester.py b/gcp/api/run_apitester.py deleted file mode 100644 index 281063a320b..00000000000 --- a/gcp/api/run_apitester.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Run E2E golang cassette API tests.""" - -import os -import sys -import subprocess -import time -import test_server - -_PORT = 8080 - - -def main(): - if len(sys.argv) < 2: - print(f'Usage: {sys.argv[0]} path/to/credential.json') - sys.exit(1) - - credential_path = sys.argv[1] - - # Ensure Docker image is pulled - subprocess.run( - ['docker', 'pull', 'gcr.io/endpoints-release/endpoints-runtime:2'], - check=True) - - print("Starting test server...") - server = test_server.start(credential_path, port=_PORT) - - # Wait for server to start up - time.sleep(10) - - try: - # Determine API URL - if os.getenv('CLOUDBUILD'): - host = test_server.get_cloudbuild_esp_host() - else: - host = 'localhost' - - api_base_url = f"{host}:{_PORT}" - print(f"Running Go tests against {api_base_url}") - - env = os.environ.copy() - env['OSV_API_BASE_URL'] = api_base_url - - # Go tests path - # Assuming this script is in gcp/api/ - go_test_dir = os.path.abspath( - os.path.join(os.path.dirname(__file__), '../../tools/apitester')) - - cmd = ['go', 'test', './...'] - print(f"Executing: {' '.join(cmd)} in {go_test_dir}") - - subprocess.run(cmd, cwd=go_test_dir, env=env, check=True) - - finally: - print("Stopping test server...") - server.stop() - - -if __name__ == '__main__': - main() diff --git a/gcp/api/run_tests.sh b/gcp/api/run_tests.sh deleted file mode 100755 index bada310ec4e..00000000000 --- a/gcp/api/run_tests.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -x -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ $# -lt 1 ]; then - echo "Usage: $0 /path/to/credential.json" - exit 1 -fi - -export GOOGLE_CLOUD_PROJECT=oss-vdb OSV_VULNERABILITIES_BUCKET=osv-vulnerabilities -service docker start - -# Set -e later as service docker start should be able to successfully fail -set -e - -# Install dependencies only if not running in Cloud Build -if [ -z "$CLOUDBUILD" ]; then - poetry sync -fi -poetry run python server_test.py -poetry run python integration_tests.py "$1" diff --git a/gcp/api/run_tests_e2e.sh b/gcp/api/run_tests_e2e.sh deleted file mode 100755 index aebf45a5e02..00000000000 --- a/gcp/api/run_tests_e2e.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -x -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ $# -lt 1 ]; then - echo "Usage: $0 /path/to/credential.json" - exit 1 -fi - -export GOOGLE_CLOUD_PROJECT=oss-vdb-test OSV_VULNERABILITIES_BUCKET=osv-test-vulnerabilities - -# Try to start docker if not running (mostly for CI) -service docker start || true - -set -e - -if [ -z "$CLOUDBUILD" ]; then - poetry sync -fi -poetry run python run_apitester.py "$1" diff --git a/gcp/api/server.py b/gcp/api/server.py deleted file mode 100644 index e917efd1aae..00000000000 --- a/gcp/api/server.py +++ /dev/null @@ -1,1219 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""OSV API server implementation.""" - -import argparse -import codecs -from dataclasses import dataclass -from datetime import datetime, timedelta -import math -import hashlib -import functools -import logging -import os -import threading -import time -import concurrent.futures - -from collections import defaultdict - -from google.cloud import exceptions -from google.cloud import ndb -from google.cloud.ndb import tasklets -from google.api_core.exceptions import InvalidArgument -import google.cloud.ndb.exceptions as ndb_exceptions -from google.protobuf import timestamp_pb2 - -import grpc -from grpc_health.v1 import health_pb2 -from grpc_health.v1 import health_pb2_grpc -from grpc_reflection.v1alpha import reflection -from packaging.utils import canonicalize_version - -import osv -from osv import ecosystems -from osv import purl_helpers -from osv.logs import setup_gcp_logging -import osv_service_v1_pb2 -import osv_service_v1_pb2_grpc - -from cursor import QueryCursor, QueryCursorMetadata - -# TODO(michaelkedar): A Global ThreadPoolExecutor is not ideal. -_BUCKET_THREAD_POOL = concurrent.futures.ThreadPoolExecutor(max_workers=32) - -_SHUTDOWN_GRACE_DURATION = 5 - -_MAX_SINGLE_QUERY_TIME = timedelta(seconds=20) -_MAX_BATCH_QUERY_TIME = timedelta(seconds=35) -_MAX_BATCH_QUERY = 1000 -# Maximum number of responses to return before applying post exceeded limit -_MAX_VULN_RESP_THRESH = 3000 -# Max responses after MAX_VULN_RESP_THRESH has been exceeded -_MAX_VULN_LISTED_POST_EXCEEDED = 5 -# Max responses before MAX_VULN_RESP_THRESH has been exceeded -_MAX_VULN_LISTED_PRE_EXCEEDED = 1000 - -_MAX_VULN_LISTED_PRE_EXCEEDED_UBUNTU_EXCEPTION = 50 - -# Used in DetermineVersion -# If there are more results for a bucket than this number, -# ignore the bucket completely -_MAX_MATCHES_TO_CARE = 100 -# Max results to return for DetermineVersion -_MAX_DETERMINE_VER_RESULTS_TO_RETURN = 10 -_DETERMINE_VER_MIN_SCORE_CUTOFF = 0.05 -# Size of buckets to divide hashes into in DetermineVersion -# This should match the number in the indexer -_BUCKET_SIZE = 512 - -# This needs to be kept in sync with -# https://github.com/google/osv.dev/blob/master/docker/indexer/stages/processing/processing.go#L77 -_VENDORED_LIB_NAMES = frozenset(( - '3rdparty', - 'dep', - 'deps', - 'thirdparty', - 'third-party', - 'third_party', - 'libs', - 'external', - 'externals', - 'vendor', - 'vendored', -)) - -# Prefix for the -_TAG_PREFIX = "refs/tags/" - -_TEST_INSTANCE = 'oss-vdb-test' - -# ---- - -_ndb_client = ndb.Client() - -# ---- - - -def ndb_context(func): - """Wrapper to create an NDB context.""" - - @functools.wraps(func) - def wrapper(*args, **kwargs): - with _ndb_client.context(): - return func(*args, **kwargs) - - return wrapper - - -class LogTraceFilter: - """Class for adding the trace information from the grpc requests into logs.""" - - def __init__(self): - self.thread_local = threading.local() - - def log_trace(self, func): - """Wrapper for grpc method to capture trace from header metadata""" - - @functools.wraps(func) - def wrapper(s, r, context: grpc.ServicerContext): - self.thread_local.trace = dict( - context.invocation_metadata()).get('x-cloud-trace-context') - return func(s, r, context) - - return wrapper - - def filter(self, record: logging.LogRecord) -> bool: - """logging.Filter method to add trace into log data.""" - trace = getattr(self.thread_local, 'trace', None) - if not trace: - return True - - # Trace context header example: - # "X-Cloud-Trace-Context: TRACE_ID/SPAN_ID;o=TRACE_TRUE" - parts = trace.split('/') - trace_id = parts[0] - project = get_gcp_project() - record.trace = f'projects/{project}/traces/{trace_id}' - if len(parts) > 1: - record.span_id = parts[1].split(';')[0] - - return True - - -trace_filter = LogTraceFilter() - - -class OSVServicer(osv_service_v1_pb2_grpc.OSVServicer, - health_pb2_grpc.HealthServicer): - """V1 OSV servicer.""" - - @ndb_context - @trace_filter.log_trace - @ndb.synctasklet - def GetVulnById(self, request, context: grpc.ServicerContext): - """Return a `Vulnerability` object for a given OSV ID.""" - # Datastore has a limit of how large indexed properties can be (<=1500B). - # Vulnerability IDs aren't going to be that long. - if len(request.id) > 100: - context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'ID too long') - return None - - # Get vuln from GCS - try: - return osv.gcs.get_by_id(request.id) - except exceptions.NotFound: - # Check for aliases - alias_group = yield osv.AliasGroup.query( - osv.AliasGroup.bug_ids == request.id).get_async() - if alias_group: - alias_string = ' '.join([ - f'{alias}' for alias in alias_group.bug_ids if alias != request.id - ]) - context.abort( - grpc.StatusCode.NOT_FOUND, - f'Bug not found, but the following aliases were: {alias_string}') - return None - context.abort(grpc.StatusCode.NOT_FOUND, 'Bug not found.') - return None - - @ndb_context - @trace_filter.log_trace - @ndb.synctasklet - def QueryAffected(self, request, context: grpc.ServicerContext): - """Query vulnerabilities for a particular project at a given commit or - - version. - """ - # Log some information about the query with structured logging - qtype, ecosystem, versioned = query_info(request.query) - if ecosystem is not None: - logging.info( - 'QueryAffected for %s "%s"', - qtype, - ecosystem, - extra={ - 'json_fields': { - 'details': { - 'ecosystem': ecosystem, - 'versioned': versioned == 'versioned' - } - } - }) - else: - logging.info('QueryAffected for %s', qtype) - - # Log queries for test instance. - # This is for debugging purposes. Production queries will not be recorded. - if get_gcp_project() == _TEST_INSTANCE: - logging.info('Query: %s', request.query) - try: - page_token = QueryCursor.from_page_token(request.query.page_token) - except ValueError as e: - logging.warning(e) - context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Invalid page token.') - # This is just for the type checker which doesn't know - # abort will kill the code flow - raise - - query_context = QueryContext( - service_context=context, - request_cutoff_time=datetime.now() + _MAX_SINGLE_QUERY_TIME, - input_cursor=page_token, - output_cursor=QueryCursor(), - total_responses=ResponsesCount(0)) - - try: - results, next_page_token = yield do_query(request.query, query_context) - except InvalidArgument: - # Currently cannot think of any other way - # this can be raised other than invalid cursor - context.abort(grpc.StatusCode.INVALID_ARGUMENT, - 'Invalid query, likely caused by invalid page token.') - except ndb_exceptions.BadValueError as e: - context.abort(grpc.StatusCode.INVALID_ARGUMENT, - f'Bad parameter value: {e}') - - if results is not None: - return osv_service_v1_pb2.VulnerabilityList( - vulns=results, next_page_token=next_page_token) - - return None - - @ndb_context - @trace_filter.log_trace - @ndb.synctasklet - def QueryAffectedBatch(self, request, context: grpc.ServicerContext): - """Query vulnerabilities (batch).""" - batch_results = [] - futures = [] - - # Log some information about the query with structured logging e.g. - # "message": "QueryAffectedBatch with 15 queries", - # "details": { - # "commit": 1, - # "ecosystem": { - # "PyPI": { - # "versioned": 4, - # "versionless": 5 - # }, - # "": { // no ecosystem specified - # "versioned": 1, - # } - # }, - # "purl": { - # "golang": { // purl type, not OSV ecosystem - # "versionless": 1 - # } - # } - # "invalid": 2 - # } - # Fields are not included if the value is empty/0 - query_details = { - 'commit': 0, - 'ecosystem': defaultdict(lambda: defaultdict(int)), - 'purl': defaultdict(lambda: defaultdict(int)), - 'invalid': 0, - } - for query in request.query.queries: - qtype, ecosystem, versioned = query_info(query) - if ecosystem is not None: - query_details[qtype][ecosystem][versioned] += 1 - else: - query_details[qtype] += 1 - - # Filter out empty fields - query_details = {k: v for k, v in query_details.items() if v} - - logging.info( - 'QueryAffectedBatch with %d queries', - len(request.query.queries), - extra={'json_fields': { - 'details': query_details - }}) - # Log queries for test instance. - # This is for debugging purposes. Production queries will not be recorded. - if get_gcp_project() == _TEST_INSTANCE: - logging.info('Batch query: %s', request.query) - - if len(request.query.queries) > _MAX_BATCH_QUERY: - context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Too many queries.') - return None - - total_responses = ResponsesCount(0) - req_cutoff_time = datetime.now() + _MAX_BATCH_QUERY_TIME - for i, query in enumerate(request.query.queries): - try: - page_token = QueryCursor.from_page_token(query.page_token) - except ValueError as e: - logging.warning(e) - context.abort(grpc.StatusCode.INVALID_ARGUMENT, - f'Invalid page token at index: {i}.') - # This is just for the type checker which doesn't know - # abort will kill the code flow - raise - - query_context = QueryContext( - service_context=context, - request_cutoff_time=req_cutoff_time, - input_cursor=page_token, - output_cursor=QueryCursor(), - total_responses=total_responses) - - futures.append(do_query(query, query_context, include_details=False)) - - for future in futures: - try: - result, next_page_token = yield future - except InvalidArgument: - # Currently cannot think of any other way - # this can be raised other than invalid cursor - context.abort(grpc.StatusCode.INVALID_ARGUMENT, - 'Invalid query, likely caused by invalid page token.') - except ndb_exceptions.BadValueError as e: - context.abort(grpc.StatusCode.INVALID_ARGUMENT, - f'Bad parameter value: {e}') - - batch_results.append( - osv_service_v1_pb2.VulnerabilityList( - vulns=result, next_page_token=next_page_token)) - - return osv_service_v1_pb2.BatchVulnerabilityList(results=batch_results) - - @ndb_context - @trace_filter.log_trace - @ndb.synctasklet - def DetermineVersion(self, request, context: grpc.ServicerContext): - """Determine the version of the provided hashes.""" - res = yield determine_version(request.query, context) - return res - - @ndb_context - @trace_filter.log_trace - def ImportFindings(self, request, context: grpc.ServicerContext): - """Return a list of `ImportFinding` for a given source.""" - source = request.source - # TODO(gongh@): add source check, - # check if the source name exists in the source repository. - if not source: - context.abort(grpc.StatusCode.INVALID_ARGUMENT, - 'Missing Source: Please specify the source') - if get_gcp_project() == _TEST_INSTANCE: - logging.info('Checking import finding for %s\n', source) - - query = osv.ImportFinding.query(osv.ImportFinding.source == source) - import_findings: list[osv.ImportFinding] = query.fetch() - invalid_records = [] - for finding in import_findings: - invalid_records.append(finding.to_proto()) - - return osv_service_v1_pb2.ImportFindingList(invalid_records=invalid_records) - - @ndb_context - def Check(self, request, context: grpc.ServicerContext): - """Health check per the gRPC health check protocol.""" - del request # Unused. - del context # Unused. - - # Read up to a single Vulnerability entity from the DB. - # This should not cause an exception or time out. - osv.Vulnerability.query().fetch(1) - return health_pb2.HealthCheckResponse( - status=health_pb2.HealthCheckResponse.ServingStatus.SERVING) - - def Watch(self, request, context: grpc.ServicerContext): - """Health check per the gRPC health check protocol.""" - del request # Unused. - context.abort(grpc.StatusCode.UNIMPLEMENTED, "Unimplemented") - - -def query_info(query) -> tuple[str, str | None, str | None]: - """Returns information about a query, for logging purposes. - First return value is one of 'commit', 'purl', 'ecosystem', 'invalid'. - If 'ecosystem' or 'purl', second two return values are the ecosystem name, - then 'versioned' or 'versionless' depending if the 'version' field is set. - Otherwise, last two return values are None. - """ - if query.WhichOneof('param') == 'commit': - return 'commit', None, None - if not query.HasField('package'): - return 'invalid', None, None - if not query.package.purl and not query.package.name: - return 'invalid', None, None - qtype = 'ecosystem' - ecosystem = query.package.ecosystem - version = query.version - if query.package.purl: - try: - purl = purl_helpers.parse_purl(query.package.purl) # can raise ValueError - if purl is None: - raise ValueError('purl ecosystem is unknown') - if query.package.ecosystem or query.package.name: - raise ValueError('purl and name/ecosystem cannot both be specified') - if purl.version and query.version: - raise ValueError('purl version and version cannot both be specified') - qtype = 'purl' - ecosystem = purl.ecosystem - version = purl.version or version - except ValueError: - return 'invalid', None, None - - return qtype, ecosystem, 'versioned' if version else 'versionless' - - -# Wrapped in a separate class -@dataclass -class ResponsesCount: - """Wraps responses count in a separate class - to allow it to be passed by reference - - Also adds a interface to allow easy updating to a mutex - if necessary - """ - count: int - - def add(self, amount): - # This is to prevent query `limit` parameter being smaller than - # the number that is checked later in the iter() loop for the last page - if amount < 0: - raise ValueError("change amount must be positive") - self.count += amount - - def exceeded(self) -> bool: - return self.count > _MAX_VULN_RESP_THRESH - - def page_limit(self) -> int: - """ - Returns the limit based on whether we have - exceeded the _MAX_VULN_RESP_THRESH with the total number - of responses in the entire query batch - """ - if self.exceeded(): - return _MAX_VULN_LISTED_POST_EXCEEDED - - return _MAX_VULN_LISTED_PRE_EXCEEDED - - -@dataclass -class QueryContext: - """ - Information about the query the server is currently - responding to. - - Attributes: - service_context: Context of the underlying grpc call. - input_cursor: Cursor from the user API query input. - output_cursor: Cursor to potentially return to the user. - request_cutoff_time: Time past which all further datastore - queries stop and a page cut is made. - total_responses: **Reference** to the total count of responses - across all queries in the batch. - query_counter: Number of queries which has already been executed - (does not count batch queries). - """ - service_context: grpc.ServicerContext - input_cursor: QueryCursor - output_cursor: QueryCursor - request_cutoff_time: datetime - # Use a dataclass to copy by reference - total_responses: ResponsesCount - query_counter: int = 0 - single_page_limit_override: int | None = None - - def should_break_page(self, response_count: int): - """ - Returns whether the API should finish its current page here - and return a cursor. - - Currently uses two criteria: - - total response size greater than page limit - - request exceeding the cutoff time - """ - page_limit = self.total_responses.page_limit() - if (self.single_page_limit_override and - not self.total_responses.exceeded()): - page_limit = self.single_page_limit_override - - return (response_count >= page_limit or - datetime.now() > self.request_cutoff_time) - - def should_skip_query(self): - """ - Returns whether a query should be executed or skipped depending - on the cursor position. - - A query should be skipped when: - - Input cursor is for a future query - - Output cursor is not ended(), which means we already hit page limit - in a previous query. - """ - return (self.query_counter < self.input_cursor.query_number or - not self.output_cursor.ended) - - def cursor_at_current(self) -> ndb.Cursor | None: - """ - Return the cursor if the stored cursor is for the current query. - """ - if self.input_cursor.query_number == self.query_counter: - return self.input_cursor.ndb_cursor - - return None - - def save_cursor_at_page_break(self, - it: ndb.QueryIterator, - meta: QueryCursorMetadata | None = None): - """ - Saves the cursor at the current page break position - """ - self.output_cursor.update_from_iterator(it) - self.output_cursor.query_number = self.query_counter - if meta: - self.output_cursor.metadata = meta - else: - self.output_cursor.metadata = QueryCursorMetadata() - - -def should_skip_bucket(path: str) -> bool: - """Returns whether or not the given file path should be skipped for the - determineversions bucket computation.""" - if not path: - return False - - # Check for a nested vendored directory, as this could mess with results. The - # API expects the file path passed to be relative to the potential library - # path, so any vendored library names found here would imply it's a nested - # vendored library. - components = path.split('/') - return any(c in _VENDORED_LIB_NAMES for c in components) - - -def process_buckets( - file_results: list[osv.FileResult]) -> list[osv.RepoIndexBucket]: - """ - Create buckets in the same process as - indexer to generate the same bucket hashes - """ - buckets: list[list[bytes]] = [[] for _ in range(_BUCKET_SIZE)] - - for fr in file_results: - if should_skip_bucket(fr.path): - continue - - buckets[int.from_bytes(fr.hash[:2], byteorder='big') % _BUCKET_SIZE].append( - fr.hash) - - results: list[osv.RepoIndexBucket] = [] - for bucket in buckets: - bucket.sort() - - hasher = hashlib.md5() - for v in bucket: - hasher.update(v) - - results.append( - osv.RepoIndexBucket( - node_hash=hasher.digest(), - files_contained=len(bucket), - )) - - return results - - -def build_determine_version_result( - file_matches_by_proj: dict[ndb.Key, int], - bucket_matches_by_proj: dict[ndb.Key, int], - num_skipped_buckets: int, - # 1 means has items, 0 means empty - empty_bucket_bitmap: int, - query_file_count: int) -> osv_service_v1_pb2.VersionMatchList: - """Build sorted determine version result from the input""" - bucket_match_items = list(bucket_matches_by_proj.items()) - # Sort by number of files matched - bucket_match_items.sort(key=lambda x: x[1], reverse=True) - - # Only interested in our maximum number of results - bucket_match_items = bucket_match_items[:min( - _MAX_DETERMINE_VER_RESULTS_TO_RETURN, len(bucket_match_items))] - idx_futures = ndb.get_multi_async([b[0] for b in bucket_match_items - ]) # type: ignore - output = [] - - # Apply bitwise NOT to the user bitmap - inverted_empty_bucket_bitmap = (1 << _BUCKET_SIZE) - 1 - empty_bucket_bitmap - empty_bucket_count = inverted_empty_bucket_bitmap.bit_count() - - for i, f in enumerate(idx_futures): - idx: osv.RepoIndex = f.result() - - if idx is None: - logging.warning( - 'Bucket exists for project: %s, which does ' + - 'not have a matching IndexRepo entry', bucket_match_items[i][0]) - continue - - if idx.empty_bucket_bitmap is None: - logging.warning('No empty bucket bitmap for: %s@%s', idx.name, idx.tag) - continue - - # Byte order little is how the bitmap is stored in the indexer originally - bitmap = int.from_bytes(idx.empty_bucket_bitmap, byteorder='little') - - # We are looking to find cases where the bitmap generated by the user query - # gives a 0, but the bitmap of the repo is a 1. - # We do not want to count cases where the repo bitmap contains 0 but - # the user bitmap contains 1, since these are already accounted for by not - # having these in the query results in the first place. - # A bitwise NOT on the user query bitmap followed by a bitwise AND satisfies - # this requirement. - missed_empty_buckets = (inverted_empty_bucket_bitmap & bitmap).bit_count() - - estimated_diff_files = estimate_diff( - _BUCKET_SIZE # Starting with the total number of buckets - - bucket_matches_by_proj[idx.key] # Buckets that match are not changed - - empty_bucket_count # Buckets that are empty are not changed - + missed_empty_buckets # Unless they don't match the bitmap - - num_skipped_buckets, # Buckets skipped are assumed unchanged - abs(idx.file_count - query_file_count) # The difference in file count - ) - - max_files = max(idx.file_count, query_file_count) - - version = osv.normalize_tag(idx.tag.removeprefix(_TAG_PREFIX)) - version = version.replace('-', '.') - if not version: # This tag actually isn't a version (rare) - continue - - version_match = osv_service_v1_pb2.VersionMatch( - score=(max_files - estimated_diff_files) / max_files, - minimum_file_matches=file_matches_by_proj[idx.key], - estimated_diff_files=estimated_diff_files, - repo_info=osv_service_v1_pb2.VersionRepositoryInformation( - type=osv_service_v1_pb2.VersionRepositoryInformation.GIT, - address=idx.repo_addr, - commit=idx.commit.hex(), - tag=idx.tag.removeprefix(_TAG_PREFIX), - version=version, - )) - - if version_match.score < _DETERMINE_VER_MIN_SCORE_CUTOFF: - continue - - output.append(version_match) - - output.sort(key=lambda x: x.score, reverse=True) - - return osv_service_v1_pb2.VersionMatchList(matches=output) - - -def estimate_diff(num_bucket_change: int, file_count_diff: int) -> int: - """ - Estimates the number of files that have changed based on - the number of buckets that changed. - """ - estimate = _BUCKET_SIZE * math.log( - (_BUCKET_SIZE + 1) / (_BUCKET_SIZE - num_bucket_change + 1)) - - return file_count_diff + round(max(estimate - file_count_diff, 0) / 2) - - -@ndb.tasklet -def determine_version(version_query: osv_service_v1_pb2.VersionQuery, - _: grpc.ServicerContext) -> ndb.Future: - """Identify fitting commits based on a subset of hashes""" - logging.info("DetermineVersion for %d hashes", len(version_query.file_hashes)) - - req_list = [] - for x in version_query.file_hashes: - if x.hash is not None and len(x.hash) <= 100: - # We are expecting MD5 hashes which should not be super long. - req_list.append(osv.FileResult(hash=x.hash)) - - # Build all the buckets and query the bucket hash - buckets = process_buckets(req_list) - - file_match_count: dict[ndb.Key, int] = defaultdict(int) - bucket_match_count: dict[ndb.Key, int] = defaultdict(int) - num_skipped_buckets = 0 - skipped_files = 0 - - # 1 means not empty, 0 means empty - empty_bucket_bitmap = 0 - - # Tuple is (Future, index, number_of_files) - query_futures: list[tuple[ndb.Future, int, int]] = [] - - for idx, bucket in enumerate(buckets): - if bucket.files_contained == 0: - continue - - empty_bucket_bitmap |= 1 << idx - query = osv.RepoIndexBucket.query( - osv.RepoIndexBucket.node_hash == bucket.node_hash) - # Limit the number of requests to prevent super long queries - query_futures.append((query.fetch_async(limit=_MAX_MATCHES_TO_CARE), idx, - bucket.files_contained)) - - # Take the results and group the library versions, - # aggregating on the number of files matched - - for future, idx, num_of_files in query_futures: - result: list[osv.RepoIndexBucket] = list(future.result()) - if result: # If there is a match, add it to list of potential versions - # If it equals the limit, there probably is more versions beyond the limit - # so just ignore it completely since it's not a useful indicator - if len(result) == _MAX_MATCHES_TO_CARE: - num_skipped_buckets += 1 - skipped_files += num_of_files - continue - - for index_bucket in result: - parent_key = index_bucket.key.parent() # type: ignore - file_match_count[parent_key] += index_bucket.files_contained - bucket_match_count[parent_key] += 1 - - # Up the matches by the ones that match too commonly - # This is used to return 100% matches - for key in file_match_count.keys(): - file_match_count[key] += skipped_files - - return build_determine_version_result(file_match_count, bucket_match_count, - num_skipped_buckets, - empty_bucket_bitmap, - len(version_query.file_hashes)) - - -@ndb.tasklet -def do_query(query: osv_service_v1_pb2.Query, - context: QueryContext, - include_details=True) -> tuple[list, str | None]: - """Do a query.""" - package_name = '' - ecosystem = '' - purl_str = '' - version = '' - - if query.HasField('package'): - package_name = query.package.name - ecosystem = query.package.ecosystem - purl_str = query.package.purl - - if query.HasField('version'): - version = query.version - - # convert purl to package names - if purl_str: - try: - purl = purl_helpers.parse_purl(purl_str) - except ValueError as e: - context.service_context.abort( - grpc.StatusCode.INVALID_ARGUMENT, - f'{e}', - ) - - if package_name: # Purls already include the package name - context.service_context.abort( - grpc.StatusCode.INVALID_ARGUMENT, - 'name specified in a PURL query', - ) - - if ecosystem: - # Purls already include the ecosystem inside - context.service_context.abort( - grpc.StatusCode.INVALID_ARGUMENT, - 'ecosystem specified in a PURL query', - ) - - if purl is None: - # TODO(gongh@): Previously, we didn't perform any PURL validation. - # All unsupported PURL queries would simply return a 200 - # status code with an empty response. - # To avoid breaking existing behavior, - # we return an empty response here with no error. - # This needs to be revisited with a more considerate design. - return [], None - - if purl.version and version: - # version included both in purl and query - context.service_context.abort( - grpc.StatusCode.INVALID_ARGUMENT, - 'version specified in params and PURL query', - ) - - ecosystem = purl.ecosystem - package_name = purl.package - if purl.version: - version = purl.version - - if ecosystem and not ecosystems.is_known(ecosystem): - context.service_context.abort(grpc.StatusCode.INVALID_ARGUMENT, - 'Invalid ecosystem.') - - # Normalize package names as necessary. - if package_name: - package_name = ecosystems.maybe_normalize_package_names( - package_name, ecosystem) - - # Hack to work around ubuntu having extremely large individual entries - if ecosystem.startswith('Ubuntu'): - # Specifically the linux entries - if 'linux' in package_name: - context.single_page_limit_override = \ - _MAX_VULN_LISTED_PRE_EXCEEDED_UBUNTU_EXCEPTION - - bugs: list[ndb.Future] - if query.WhichOneof('param') == 'commit': - try: - commit_bytes = codecs.decode(query.commit, 'hex') - except ValueError: - context.service_context.abort(grpc.StatusCode.INVALID_ARGUMENT, - 'Invalid hash.') - return None - - bugs = yield query_by_commit(context, commit_bytes, include_details) - elif package_name and (ecosystem or version): - # New Database table & GCS querying - bugs = yield query_package(context, package_name, ecosystem, version, - include_details) - else: - context.service_context.abort(grpc.StatusCode.INVALID_ARGUMENT, - 'Invalid query.') - # This will never be reached, and is just here for the type checker, - # to know that control flow breaks here. - raise ValueError - - if context.query_counter < context.input_cursor.query_number: - logging.error( - 'Cursor is invalid - received "%d" while total query count is "%d".', - context.input_cursor.query_number, context.query_counter) - # If the input cursor is for a query number that's greater than - # the number of queries performed, the cursor must be invalid - # (and there will be no results, as everything is skipped) - raise ValueError('Cursor is invalid/does not belong to this query') - - next_page_token_str = context.output_cursor.url_safe_encode() - if next_page_token_str: - logging.warning('Page size limit hit, response size: %s', len(bugs)) - - # Wait on all the bug futures - bugs = yield bugs - - return [b for b in bugs if b is not None], next_page_token_str - - -@ndb.tasklet -def query_by_commit(context: QueryContext, - commit: bytes, - include_details: bool = True) -> list[ndb.Future]: - """ - Perform a query by commit. - - This is a ndb.tasklet, so will return a future that will need to be yielded. - - Args: - context: QueryContext for the current query. - commit: The commit hash to query. - include_details: Whether to return full or minimal vulnerability details. - - Returns: - A list of Vulnerability protos. - """ - query = osv.AffectedCommits.query( - osv.AffectedCommits.commits == commit, - # pylint: disable=singleton-comparison - osv.AffectedCommits.public == True) - - context.query_counter += 1 - if context.should_skip_query(): - return [] - - bugs = [] - it: ndb.QueryIterator = query.iter( - keys_only=True, start_cursor=context.cursor_at_current()) - - while (yield it.has_next_async()): - if context.should_break_page(len(bugs)): - context.save_cursor_at_page_break(it) - break - - # Affect commits key follows this format: - # - - affected_commits: ndb.Key = it.next() - bug_id: str = affected_commits.id().rsplit("-", 1)[0] - vuln: osv.Vulnerability = yield osv.Vulnerability.get_by_id_async(bug_id) - if vuln is None: - logging.error('AffectedCommit for %s matched but Vulnerability not found', - bug_id) - osv.pubsub.publish_failure(b'', type='gcs_missing', id=bug_id) - continue - if vuln.is_withdrawn: - continue - - if include_details: - bugs.append(get_vuln_async(bug_id)) - else: - bugs.append(vulnerability_to_minimal(vuln)) - context.total_responses.add(1) - - return bugs - - -@ndb.tasklet -def query_package(context: QueryContext, - package_name: str | None, - ecosystem: str | None, - version: str | None, - include_details: bool = True) -> list[ndb.Future]: - """ - Queries for vulnerabilities by package and version using a new data model. - - This function is designed to test a new query path that may use different - data sources (like GCS) for fetching vulnerability details. - - Args: - context: The QueryContext for the current request. - package_name: The name of the package. - ecosystem: The package's ecosystem. - version: The version of the package to query for. - include_details: Whether to return full or minimal vulnerability details. - - Returns: - A list of Vulnerability protos. - """ - - context.query_counter += 1 - if context.should_skip_query(): - return [] - - # Bare minimum we need a package name. - if not package_name: - return [] - - # Ideally, we'd check both unnormalized and normalized named at once, if there - # is no provided ecosystem (even though that is not explicitly supported), but - # Datastore cannot give cursors for 'OR' queries, so just only normalize if - # this is explicitly GIT. - if ecosystem == 'GIT': - package_name = osv.normalize_repo_package(package_name) - - query = osv.AffectedVersions.query(osv.AffectedVersions.name == package_name) - if ecosystem: - query = query.filter(osv.AffectedVersions.ecosystem == ecosystem) - if version: - helper = ecosystems.get(ecosystem) - if helper is not None: - try: - coarse = helper.coarse_version(version) - query = query.filter(osv.AffectedVersions.coarse_min <= coarse) - query = query.filter(osv.AffectedVersions.coarse_max >= coarse) - except (ValueError, NotImplementedError): - # We want to avoid logging user requests. - pass - # TODO(michaelkedar): I think a ValueError would mean the version - # would not match any affected versions, so we may be able to return. - query = query.order(osv.AffectedVersions.vuln_id) - - bugs = [] - last_matched_id = '' - if query_cursor := context.input_cursor: - if query_cursor.metadata.last_id: - last_matched_id = query_cursor.metadata.last_id - - it: ndb.QueryIterator = query.iter(start_cursor=context.cursor_at_current()) - while (yield it.has_next_async()): - if context.should_break_page(len(bugs)): - meta = QueryCursorMetadata( - last_id=last_matched_id) if last_matched_id else None - context.save_cursor_at_page_break(it, meta) - break - - affected: osv.AffectedVersions = it.next() - if affected.vuln_id == last_matched_id: - continue - if not version or affected_affects(version, affected): - if include_details: - bugs.append(get_vuln_async(affected.vuln_id)) - else: - bugs.append(get_minimal_async(affected.vuln_id)) - last_matched_id = affected.vuln_id - context.total_responses.add(1) - - return bugs - - -def affected_affects(version: str, affected: osv.AffectedVersions) -> bool: - """Check if a given version is affected by the AffectedVersions entry.""" - if affected.versions: - return _match_versions(version, affected) - if affected.events: - return _match_events(version, affected) - - # No versions or events can happen with e.g. GIT ranges that could not find - # tags. Just ignore these entities. - return False - - -def _match_versions(version: str, affected: osv.AffectedVersions) -> bool: - """Check if the given version matches one of the AffectedVersions' listed - versions.""" - ecosystem_helper = osv.ecosystems.get(affected.ecosystem) - if ecosystem_helper is not None: - # Most ecosystem helpers return a very large version on invalid, but if it - # does cause an error, just match nothing. - try: - parsed_version = ecosystem_helper.sort_key(version) - except Exception: - logging.debug('Ecosystem helper for %s raised an exception', - affected.ecosystem) - return False - - for v in affected.versions: - try: - if ecosystem_helper.sort_key(v) == parsed_version: - return True - except Exception: - logging.error('Version %s in AffectedVersion %s (%s) does not parse', v, - affected.key, affected.vuln_id) - return False - - # Helper not implemented: - # Direct string matching - if version in affected.versions: - return True - # Try fuzzy matching - vers = affected.versions - if affected.ecosystem == 'GIT': - vers = osv.models.maybe_strip_repo_prefixes(vers, [affected.name]) - if osv.normalize_tag(version) in osv.normalize_tags(vers): - return True - if canonicalize_version(version) in ( - canonicalize_version(v) for v in affected.versions): - return True - return False - - -def _match_events(version: str, affected: osv.AffectedVersions) -> bool: - """Check if the given version matches in the AffectedVersions' events list.""" - # TODO(michaelkedar): We don't support grabbing the release number from PURLs - # https://github.com/google/osv.dev/issues/3126 - # This causes many false positive matches in Ubuntu and Alpine in particular - # when doing range-based matching. - # We have version enumeration for Alpine, and Ubuntu provides versions for us. - # Just skip range-based matching if they don't have release numbers for now. - if affected.ecosystem in ('Alpine', 'Ubuntu'): - return False - ecosystem_helper = osv.ecosystems.get(affected.ecosystem) - if ecosystem_helper is None: - # Ecosystem does not support comparisons. - return False - try: - parsed_version = ecosystem_helper.sort_key(version) - except Exception: - logging.debug('Ecosystem helper for %s raised an exception', - affected.ecosystem) - return False - - # Find where this version would belong in the sorted events list. - for event in reversed(affected.events): - try: - event_ver = ecosystem_helper.sort_key(event.value) - except Exception: - # Shouldn't really happen. We use sort_key to sort these before creating - # the AffectedVersions entity. - logging.error('Event %s in AffectedVersion %s (%s) does not parse', - event.value, affected.key, affected.vuln_id) - return False - if event_ver == parsed_version: - return event.type in ('introduced', 'last_affected') - if event_ver < parsed_version: - return event.type == 'introduced' - - return False - - -@ndb.tasklet -def get_minimal_async(vuln_id: str): - """Asynchronously get a minimal vulnerability record.""" - vuln = yield osv.Vulnerability.get_by_id_async(vuln_id) - minimal = yield vulnerability_to_minimal(vuln) - return minimal - - -@ndb.tasklet -def vulnerability_to_minimal(vuln: osv.Vulnerability): - """Construct a minimal response from a Vulnerability entity.""" - vuln_id = vuln.key.id() - modified = timestamp_pb2.Timestamp() - modified.FromDatetime(vuln.modified) - return osv.vulnerability_pb2.Vulnerability(id=vuln_id, modified=modified) - - -def get_vuln_async(vuln_id: str) -> ndb.Future: - """Asynchronously get a full vulnerability record.""" - - # As a work around for using external processes with ndb's async, - # do the bucket get in another thread, and poll it with abd ndb Future. - f = _BUCKET_THREAD_POOL.submit(osv.gcs.get_by_id, vuln_id) - - @ndb.tasklet - def async_poll_result(): - while not f.done(): - yield tasklets.sleep(0.1) - try: - return f.result() - except exceptions.NotFound: - logging.error('Vulnerability %s matched query but not found in GCS', - vuln_id) - osv.pubsub.publish_failure(b'', type='gcs_missing', id=vuln_id) - return None - - def cleanup(_: ndb.Future): - f.cancel() - - future = async_poll_result() - future.add_done_callback(cleanup) - return future - - -def serve(port: int, local: bool): - """Configures and runs the OSV API server.""" - server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=5)) - servicer = OSVServicer() - osv_service_v1_pb2_grpc.add_OSVServicer_to_server(servicer, server) - health_pb2_grpc.add_HealthServicer_to_server(servicer, server) - if local: - service_names = ( - osv_service_v1_pb2.DESCRIPTOR.services_by_name['OSV'].full_name, - health_pb2.DESCRIPTOR.services_by_name['Health'].full_name, - reflection.SERVICE_NAME, - ) - reflection.enable_server_reflection(service_names, server) - server.add_insecure_port('[::]:{}'.format(port)) - server.start() - - print('Listening on port {}'.format(port)) - try: - while True: - time.sleep(3600) - except KeyboardInterrupt: - print('Shutting down with {} grace period'.format(_SHUTDOWN_GRACE_DURATION)) - server.stop(_SHUTDOWN_GRACE_DURATION) - - -def is_cloud_run() -> bool: - """Check if we are running in Cloud Run.""" - # https://cloud.google.com/run/docs/container-contract#env-vars - return os.getenv('K_SERVICE') is not None - - -def get_gcp_project(): - """Get the GCP project name.""" - project = osv.utils.get_google_cloud_project() - if not project: - project = 'oss-vdb' # fall back to oss-vdb - return project - - -def main(): - """Entrypoint.""" - if is_cloud_run(): - setup_gcp_logging('api-backend') - logging.getLogger().addFilter(trace_filter) - - logging.getLogger().setLevel(logging.INFO) - - parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument( - '--port', - type=int, - default=None, - help='The port to listen on.' - 'If arg is not set, will listen on the $PORT env var.' - 'If env var is empty, defaults to 8000.') - parser.add_argument( - '--local', - action='store_true', - default=False, - help='If set reflection is enabled to allow debugging with grpcurl.') - - args = parser.parse_args() - port = args.port - if not port: - port = int(os.environ.get('PORT', '8000')) - - serve(port, args.local) - - -if __name__ == '__main__': - main() diff --git a/gcp/api/server_test.py b/gcp/api/server_test.py deleted file mode 100644 index db1789ef3c5..00000000000 --- a/gcp/api/server_test.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2023 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Server unit tests.""" - -import unittest - -from server import should_skip_bucket - - -class ServerTest(unittest.TestCase): - """Server tests.""" - - def test_should_skip_bucket(self): - """Test should_skip_bucket.""" - test_cases = [ - (None, False), - ('', False), - ('/a', False), - ('a', False), - ('a/b', False), - ('a/b/c', False), - ('/a/b/c', False), - ('/a/3rdparty/c', True), - ('/a/third_party/c/d', True), - ('/third_party/c/d', True), - ('vendor/c/d', True), - ('/a/3rdpartyy/c', False), - ] - - for path, expected in test_cases: - self.assertEqual(expected, should_skip_bucket(path)) - - -if __name__ == '__main__': - unittest.main() diff --git a/gcp/api/test_server.py b/gcp/api/test_server.py deleted file mode 100644 index 7570f1b40ab..00000000000 --- a/gcp/api/test_server.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Start a test API server.""" - -import os -import subprocess -import sys -import time - -_ESP_PORT = 8080 -_BACKEND_PORT = 8000 - - -class ServerInstance: - """Server instance.""" - - def __init__(self, backend, esp): - self.backend = backend - self.esp = esp - - def check(self): - """Check that the server is still up.""" - if self.backend and self.backend.poll(): - raise RuntimeError('Backend process died.') - - if self.esp.poll(): - raise RuntimeError('ESP process died.') - - def stop(self): - """Stop the server.""" - self.esp.kill() - if self.backend: - self.backend.kill() - - -def start_backend(port, log_path): - """Start backend server.""" - log_handle = open(log_path, 'w') - env = os.environ.copy() - - if os.getenv('OSV_USE_GO_BACKEND') == '1': - # Use the Go backend. We assume it has been compiled. - # Path is relative to gcp/api/ directory. - backend_cmd = ['../../go/api', f'--port={port}'] - print(f"Starting Go backend: {' '.join(backend_cmd)}") - else: - backend_cmd = [sys.executable, 'server.py', f'--port={port}'] - print(f"Starting Python backend: {' '.join(backend_cmd)}") - - backend_proc = subprocess.Popen( - backend_cmd, env=env, stdout=log_handle, stderr=subprocess.STDOUT) - - return backend_proc - - -def get_cloudbuild_esp_host(): - """Get cloudbuild host.""" - result = subprocess.run([ - 'docker', 'inspect', 'osv-esp', - '--format={{(index .NetworkSettings.Networks "cloudbuild").IPAddress}}' - ], - capture_output=True, - check=True) - ip = result.stdout.decode().strip() - print('Got cloudbuild ESP host', ip) - return ip - - -def get_ip(): - """Get IP.""" - result = subprocess.run([ - 'hostname', - '-i', - ], capture_output=True, check=True) - ip = result.stdout.decode().strip() - print('Got cloudbuild host', ip) - return ip - - -def start_esp(port, backend_port, credential_path, log_path): - """Start ESPv2 frontend.""" - log_handle = open(log_path, 'w') - - if os.getenv('CLOUDBUILD'): - network = '--network=cloudbuild' - host = get_ip() - docker_image = 'gcr.io/endpoints-release/endpoints-runtime:2' - # Use standard endpoints container and GCP credentials. - docker_cmd = [ - 'docker', - 'run', - '--name', - 'osv-esp', - network, - '--rm', - '-v', - f'--publish={port}', - f'{docker_image}', - '--disable_tracing', - '--service=api-test.osv.dev', - '--rollout_strategy=managed', - '--underscores_in_headers', - f'--listener_port={port}', - f'--backend=grpc://{host}:{backend_port}', - '--enable_debug', - '--transcoding_preserve_proto_field_names', - '--envoy_connection_buffer_limit_bytes=10485760', - ] - else: - # Use custom container and local ADC credential by mapping files - # into container. - credential_dir = os.path.dirname(os.path.abspath(credential_path)) - credential_name = os.path.basename(credential_path) - network = '--network=host' - host = 'localhost' - docker_image = 'osv/esp:latest' - docker_cmd = [ - 'docker', - 'run', - '--name', - 'osv-esp', - network, - '--rm', - '-v', - f'{credential_dir}:/esp:ro', - f'--publish={port}', - f'{docker_image}', - '--disable_tracing', - '--service=api-test.osv.dev', - '--rollout_strategy=managed', - '--underscores_in_headers', - f'--listener_port={port}', - f'--backend=grpc://{host}:{backend_port}', - f'--service_account_key=/esp/{credential_name}', - '--non_gcp', - '--enable_debug', - '--transcoding_preserve_proto_field_names', - '--envoy_connection_buffer_limit_bytes=104857600', - ] - - # Stop existing osv-esp processes that weren't killed properly. - result = subprocess.run(['docker', 'stop', 'osv-esp'], check=False) - if result.returncode == 0: - print("Killed process, waiting 5 seconds before starting again") - # If the subprocess actually stopped an instance, wait a bit - # before trying to run the command - time.sleep(5) - esp_proc = subprocess.Popen( - docker_cmd, stdout=log_handle, stderr=subprocess.STDOUT) - return esp_proc - - -def start(credential_path, - no_backend=False, - port=_ESP_PORT, - backend_port=_BACKEND_PORT): - """Start the test server.""" - backend = None - esp = None - try: - if not no_backend: - backend = start_backend(_BACKEND_PORT, 'backend.log') - esp = start_esp(port, backend_port, credential_path, 'esp.log') - except Exception: - if esp: - esp.kill() - - if backend: - backend.kill() - - raise - - return ServerInstance(backend, esp) - - -if __name__ == '__main__': - if len(sys.argv) < 2: - print(f'Usage: {sys.argv[0]} path/to/credential.json') - sys.exit(1) - - no_backend_flag = False - if len(sys.argv) == 3: - no_backend_flag = sys.argv[2] == '--no-backend' - - server = start(sys.argv[1], no_backend=no_backend_flag) - try: - while True: - server.check() - time.sleep(5) - finally: - server.stop() diff --git a/gcp/api/v1/googleapis b/gcp/api/v1/googleapis deleted file mode 120000 index c3464506fe2..00000000000 --- a/gcp/api/v1/googleapis +++ /dev/null @@ -1 +0,0 @@ -../googleapis \ No newline at end of file diff --git a/gcp/api/v1/osv b/gcp/api/v1/osv deleted file mode 120000 index b1f45271964..00000000000 --- a/gcp/api/v1/osv +++ /dev/null @@ -1 +0,0 @@ -../../../osv \ No newline at end of file diff --git a/gcp/api/vulnerability_pb2.py b/gcp/api/vulnerability_pb2.py deleted file mode 100644 index 8b866bb82fc..00000000000 --- a/gcp/api/vulnerability_pb2.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: vulnerability.proto -# Protobuf Python Version: 6.31.1 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'vulnerability.proto' -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13vulnerability.proto\x12\x03osv\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"}\n\x06\x43ommit\x12\'\n\trepo_type\x18\x01 \x01(\x0e\x32\x14.osv.Commit.RepoType\x12\x10\n\x08repo_url\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\t\"$\n\x08RepoType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x07\n\x03GIT\x10\x01:\x02\x18\x01\"8\n\x07Package\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x11\n\tecosystem\x18\x02 \x01(\t\x12\x0c\n\x04purl\x18\x03 \x01(\t\"_\n\x05\x45vent\x12\x12\n\nintroduced\x18\x01 \x01(\t\x12\r\n\x05\x66ixed\x18\x02 \x01(\t\x12\r\n\x05limit\x18\x03 \x01(\t\x12$\n\rlast_affected\x18\x04 \x01(\tR\rlast_affected\"\xd4\x01\n\x05Range\x12\x1d\n\x04type\x18\x01 \x01(\x0e\x32\x0f.osv.Range.Type\x12\x0c\n\x04repo\x18\x02 \x01(\t\x12\x1a\n\x06\x65vents\x18\x03 \x03(\x0b\x32\n.osv.Event\x12\x45\n\x11\x64\x61tabase_specific\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x11\x64\x61tabase_specific\";\n\x04Type\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x07\n\x03GIT\x10\x01\x12\n\n\x06SEMVER\x10\x02\x12\r\n\tECOSYSTEM\x10\x03\"\x88\x02\n\x08\x41\x66\x66\x65\x63ted\x12\x1d\n\x07package\x18\x01 \x01(\x0b\x32\x0c.osv.Package\x12\x1a\n\x06ranges\x18\x02 \x03(\x0b\x32\n.osv.Range\x12\x10\n\x08versions\x18\x03 \x03(\t\x12G\n\x12\x65\x63osystem_specific\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x12\x65\x63osystem_specific\x12\x45\n\x11\x64\x61tabase_specific\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructR\x11\x64\x61tabase_specific\x12\x1f\n\x08severity\x18\x06 \x03(\x0b\x32\r.osv.Severity\"\x87\x01\n\x08Severity\x12 \n\x04type\x18\x01 \x01(\x0e\x32\x12.osv.Severity.Type\x12\r\n\x05score\x18\x02 \x01(\t\"J\n\x04Type\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0b\n\x07\x43VSS_V3\x10\x01\x12\x0b\n\x07\x43VSS_V2\x10\x02\x12\x0b\n\x07\x43VSS_V4\x10\x03\x12\n\n\x06Ubuntu\x10\x04\"\x8a\x02\n\x06\x43redit\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontact\x18\x02 \x03(\t\x12\x1e\n\x04type\x18\x03 \x01(\x0e\x32\x10.osv.Credit.Type\"\xc0\x01\n\x04Type\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\t\n\x05OTHER\x10\x01\x12\n\n\x06\x46INDER\x10\x02\x12\x0c\n\x08REPORTER\x10\x03\x12\x0b\n\x07\x41NALYST\x10\x04\x12\x0f\n\x0b\x43OORDINATOR\x10\x05\x12\x19\n\x15REMEDIATION_DEVELOPER\x10\x06\x12\x18\n\x14REMEDIATION_REVIEWER\x10\x07\x12\x18\n\x14REMEDIATION_VERIFIER\x10\x08\x12\x08\n\x04TOOL\x10\t\x12\x0b\n\x07SPONSOR\x10\n\"\xda\x01\n\tReference\x12!\n\x04type\x18\x01 \x01(\x0e\x32\x13.osv.Reference.Type\x12\x0b\n\x03url\x18\x02 \x01(\t\"\x9c\x01\n\x04Type\x12\x08\n\x04NONE\x10\x00\x12\x0c\n\x08\x41\x44VISORY\x10\x02\x12\x0b\n\x07\x41RTICLE\x10\x06\x12\r\n\tDETECTION\x10\t\x12\x0e\n\nDISCUSSION\x10\x08\x12\x0c\n\x08\x45VIDENCE\x10\x07\x12\x07\n\x03\x46IX\x10\x04\x12\x07\n\x03GIT\x10\x0b\x12\x0e\n\nINTRODUCED\x10\n\x12\x0b\n\x07PACKAGE\x10\x05\x12\n\n\x06REPORT\x10\x03\x12\x07\n\x03WEB\x10\x01\"\xf0\x03\n\rVulnerability\x12&\n\x0eschema_version\x18\x12 \x01(\tR\x0eschema_version\x12\n\n\x02id\x18\x01 \x01(\t\x12-\n\tpublished\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08modified\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12-\n\twithdrawn\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x61liases\x18\x08 \x03(\t\x12\x0f\n\x07related\x18\r \x03(\t\x12\x10\n\x08upstream\x18\x0e \x03(\t\x12\x0f\n\x07summary\x18\x03 \x01(\t\x12\x0f\n\x07\x64\x65tails\x18\x04 \x01(\t\x12\x1f\n\x08\x61\x66\x66\x65\x63ted\x18\x11 \x03(\x0b\x32\r.osv.Affected\x12\"\n\nreferences\x18\x10 \x03(\x0b\x32\x0e.osv.Reference\x12\x45\n\x11\x64\x61tabase_specific\x18\x0f \x01(\x0b\x32\x17.google.protobuf.StructR\x11\x64\x61tabase_specific\x12\x1f\n\x08severity\x18\x13 \x03(\x0b\x32\r.osv.Severity\x12\x1c\n\x07\x63redits\x18\x14 \x03(\x0b\x32\x0b.osv.CreditB2Z0github.com/ossf/osv-schema/bindings/go/osvschemab\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'vulnerability_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'Z0github.com/ossf/osv-schema/bindings/go/osvschema' - _globals['_COMMIT']._loaded_options = None - _globals['_COMMIT']._serialized_options = b'\030\001' - _globals['_COMMIT']._serialized_start=91 - _globals['_COMMIT']._serialized_end=216 - _globals['_COMMIT_REPOTYPE']._serialized_start=176 - _globals['_COMMIT_REPOTYPE']._serialized_end=212 - _globals['_PACKAGE']._serialized_start=218 - _globals['_PACKAGE']._serialized_end=274 - _globals['_EVENT']._serialized_start=276 - _globals['_EVENT']._serialized_end=371 - _globals['_RANGE']._serialized_start=374 - _globals['_RANGE']._serialized_end=586 - _globals['_RANGE_TYPE']._serialized_start=527 - _globals['_RANGE_TYPE']._serialized_end=586 - _globals['_AFFECTED']._serialized_start=589 - _globals['_AFFECTED']._serialized_end=853 - _globals['_SEVERITY']._serialized_start=856 - _globals['_SEVERITY']._serialized_end=991 - _globals['_SEVERITY_TYPE']._serialized_start=917 - _globals['_SEVERITY_TYPE']._serialized_end=991 - _globals['_CREDIT']._serialized_start=994 - _globals['_CREDIT']._serialized_end=1260 - _globals['_CREDIT_TYPE']._serialized_start=1068 - _globals['_CREDIT_TYPE']._serialized_end=1260 - _globals['_REFERENCE']._serialized_start=1263 - _globals['_REFERENCE']._serialized_end=1481 - _globals['_REFERENCE_TYPE']._serialized_start=1325 - _globals['_REFERENCE_TYPE']._serialized_end=1481 - _globals['_VULNERABILITY']._serialized_start=1484 - _globals['_VULNERABILITY']._serialized_end=1980 -# @@protoc_insertion_point(module_scope) diff --git a/gcp/api/vulnerability_pb2.pyi b/gcp/api/vulnerability_pb2.pyi deleted file mode 100644 index 4ceda94ab55..00000000000 --- a/gcp/api/vulnerability_pb2.pyi +++ /dev/null @@ -1,557 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -Copyright 2025 Google LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -from collections import abc as _abc -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import struct_pb2 as _struct_pb2 -from google.protobuf import timestamp_pb2 as _timestamp_pb2 -from google.protobuf.internal import containers as _containers -from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper -import builtins as _builtins -import sys -import typing as _typing - -if sys.version_info >= (3, 10): - from typing import TypeAlias as _TypeAlias -else: - from typing_extensions import TypeAlias as _TypeAlias - -if sys.version_info >= (3, 13): - from warnings import deprecated as _deprecated -else: - from typing_extensions import deprecated as _deprecated - -DESCRIPTOR: _descriptor.FileDescriptor - -@_deprecated("""This message has been marked as deprecated using proto message options.""") -@_typing.final -class Commit(_message.Message): - """Commit reference (DEPRECATED). - - In some rare cases, this may refer to a small range of commits rather than an - exact commit (to accommodate for automated systems). In such cases, the - semantics are as follows: - - - If this is referring to a commit which introduces a vulnerability, then - *any* commits in the range is assumed to cause the vulnerability. - - - If this is referring to a commit which fixes a vulnerability, then *all* - commits in the range is assumed to cause the vulnerability. - """ - - DESCRIPTOR: _descriptor.Descriptor - - class _RepoType: - ValueType = _typing.NewType("ValueType", _builtins.int) - V: _TypeAlias = ValueType # noqa: Y015 - - class _RepoTypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[Commit._RepoType.ValueType], _builtins.type): - DESCRIPTOR: _descriptor.EnumDescriptor - UNSPECIFIED: Commit._RepoType.ValueType # 0 - GIT: Commit._RepoType.ValueType # 1 - - class RepoType(_RepoType, metaclass=_RepoTypeEnumTypeWrapper): - """The repository type.""" - - UNSPECIFIED: Commit.RepoType.ValueType # 0 - GIT: Commit.RepoType.ValueType # 1 - - REPO_TYPE_FIELD_NUMBER: _builtins.int - REPO_URL_FIELD_NUMBER: _builtins.int - COMMIT_FIELD_NUMBER: _builtins.int - repo_type: Global___Commit.RepoType.ValueType - """Required. The type of the repo.""" - repo_url: _builtins.str - """Required. The publicly accessible URL of the repo that can be directly - passed to clone commands. - """ - commit: _builtins.str - """Required. The commit identifier (e.g. git SHA). In some cases where the - exact commit fails to be determined by automation, this may be a small - range in the form "A:B" which means the commit range from A (exclusive) to - B (inclusive). - """ - def __init__( - self, - *, - repo_type: Global___Commit.RepoType.ValueType = ..., - repo_url: _builtins.str = ..., - commit: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["commit", b"commit", "repo_type", b"repo_type", "repo_url", b"repo_url"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Commit: _TypeAlias = Commit # noqa: Y015 - -@_typing.final -class Package(_message.Message): - """Package information and version.""" - - DESCRIPTOR: _descriptor.Descriptor - - NAME_FIELD_NUMBER: _builtins.int - ECOSYSTEM_FIELD_NUMBER: _builtins.int - PURL_FIELD_NUMBER: _builtins.int - name: _builtins.str - """Required. Name of the package. Should match the name used in the package - ecosystem (e.g. the npm package name). For C/C++ projects integrated in - OSS-Fuzz, this is the name used for the integration. - """ - ecosystem: _builtins.str - """Required. The ecosystem for this package. - For the complete list of valid ecosystem names, see - . - """ - purl: _builtins.str - """Optional. The package URL for this package.""" - def __init__( - self, - *, - name: _builtins.str = ..., - ecosystem: _builtins.str = ..., - purl: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["ecosystem", b"ecosystem", "name", b"name", "purl", b"purl"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Package: _TypeAlias = Package # noqa: Y015 - -@_typing.final -class Event(_message.Message): - """Version events.""" - - DESCRIPTOR: _descriptor.Descriptor - - INTRODUCED_FIELD_NUMBER: _builtins.int - FIXED_FIELD_NUMBER: _builtins.int - LIMIT_FIELD_NUMBER: _builtins.int - LAST_AFFECTED_FIELD_NUMBER: _builtins.int - introduced: _builtins.str - """The earliest version/commit where this vulnerability - was introduced in. - """ - fixed: _builtins.str - """The version/commit that this vulnerability was fixed in.""" - limit: _builtins.str - """The limit to apply to the range.""" - last_affected: _builtins.str - """The last affected version.""" - def __init__( - self, - *, - introduced: _builtins.str = ..., - fixed: _builtins.str = ..., - limit: _builtins.str = ..., - last_affected: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["fixed", b"fixed", "introduced", b"introduced", "last_affected", b"last_affected", "limit", b"limit"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Event: _TypeAlias = Event # noqa: Y015 - -@_typing.final -class Range(_message.Message): - """Affected ranges.""" - - DESCRIPTOR: _descriptor.Descriptor - - class _Type: - ValueType = _typing.NewType("ValueType", _builtins.int) - V: _TypeAlias = ValueType # noqa: Y015 - - class _TypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[Range._Type.ValueType], _builtins.type): - DESCRIPTOR: _descriptor.EnumDescriptor - UNSPECIFIED: Range._Type.ValueType # 0 - GIT: Range._Type.ValueType # 1 - SEMVER: Range._Type.ValueType # 2 - ECOSYSTEM: Range._Type.ValueType # 3 - - class Type(_Type, metaclass=_TypeEnumTypeWrapper): - """Type of the version information.""" - - UNSPECIFIED: Range.Type.ValueType # 0 - GIT: Range.Type.ValueType # 1 - SEMVER: Range.Type.ValueType # 2 - ECOSYSTEM: Range.Type.ValueType # 3 - - TYPE_FIELD_NUMBER: _builtins.int - REPO_FIELD_NUMBER: _builtins.int - EVENTS_FIELD_NUMBER: _builtins.int - DATABASE_SPECIFIC_FIELD_NUMBER: _builtins.int - type: Global___Range.Type.ValueType - """Required. The type of version information.""" - repo: _builtins.str - """Required if type is GIT. The publicly accessible URL of the repo that can - be directly passed to clone commands. - """ - @_builtins.property - def events(self) -> _containers.RepeatedCompositeFieldContainer[Global___Event]: - """Required. Version event information.""" - - @_builtins.property - def database_specific(self) -> _struct_pb2.Struct: - """Optional. JSON object holding additional information about the - vulnerability as defined by the database for which the record applies. - """ - - def __init__( - self, - *, - type: Global___Range.Type.ValueType = ..., - repo: _builtins.str = ..., - events: _abc.Iterable[Global___Event] | None = ..., - database_specific: _struct_pb2.Struct | None = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["database_specific", b"database_specific"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["database_specific", b"database_specific", "events", b"events", "repo", b"repo", "type", b"type"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Range: _TypeAlias = Range # noqa: Y015 - -@_typing.final -class Affected(_message.Message): - """Affected versions and commits.""" - - DESCRIPTOR: _descriptor.Descriptor - - PACKAGE_FIELD_NUMBER: _builtins.int - RANGES_FIELD_NUMBER: _builtins.int - VERSIONS_FIELD_NUMBER: _builtins.int - ECOSYSTEM_SPECIFIC_FIELD_NUMBER: _builtins.int - DATABASE_SPECIFIC_FIELD_NUMBER: _builtins.int - SEVERITY_FIELD_NUMBER: _builtins.int - @_builtins.property - def package(self) -> Global___Package: - """Optional. Package information.""" - - @_builtins.property - def ranges(self) -> _containers.RepeatedCompositeFieldContainer[Global___Range]: - """Required. Range information.""" - - @_builtins.property - def versions(self) -> _containers.RepeatedScalarFieldContainer[_builtins.str]: - """Optional. List of affected versions.""" - - @_builtins.property - def ecosystem_specific(self) -> _struct_pb2.Struct: - """Optional. JSON object holding additional information about the - vulnerability as defined by the ecosystem for which the record applies. - """ - - @_builtins.property - def database_specific(self) -> _struct_pb2.Struct: - """Optional. JSON object holding additional information about the - vulnerability as defined by the database for which the record applies. - """ - - @_builtins.property - def severity(self) -> _containers.RepeatedCompositeFieldContainer[Global___Severity]: - """Optional. Severity of the vulnerability for this package.""" - - def __init__( - self, - *, - package: Global___Package | None = ..., - ranges: _abc.Iterable[Global___Range] | None = ..., - versions: _abc.Iterable[_builtins.str] | None = ..., - ecosystem_specific: _struct_pb2.Struct | None = ..., - database_specific: _struct_pb2.Struct | None = ..., - severity: _abc.Iterable[Global___Severity] | None = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["database_specific", b"database_specific", "ecosystem_specific", b"ecosystem_specific", "package", b"package"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["database_specific", b"database_specific", "ecosystem_specific", b"ecosystem_specific", "package", b"package", "ranges", b"ranges", "severity", b"severity", "versions", b"versions"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Affected: _TypeAlias = Affected # noqa: Y015 - -@_typing.final -class Severity(_message.Message): - DESCRIPTOR: _descriptor.Descriptor - - class _Type: - ValueType = _typing.NewType("ValueType", _builtins.int) - V: _TypeAlias = ValueType # noqa: Y015 - - class _TypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[Severity._Type.ValueType], _builtins.type): - DESCRIPTOR: _descriptor.EnumDescriptor - UNSPECIFIED: Severity._Type.ValueType # 0 - CVSS_V3: Severity._Type.ValueType # 1 - CVSS_V2: Severity._Type.ValueType # 2 - CVSS_V4: Severity._Type.ValueType # 3 - Ubuntu: Severity._Type.ValueType # 4 - - class Type(_Type, metaclass=_TypeEnumTypeWrapper): - """Type of the severity.""" - - UNSPECIFIED: Severity.Type.ValueType # 0 - CVSS_V3: Severity.Type.ValueType # 1 - CVSS_V2: Severity.Type.ValueType # 2 - CVSS_V4: Severity.Type.ValueType # 3 - Ubuntu: Severity.Type.ValueType # 4 - - TYPE_FIELD_NUMBER: _builtins.int - SCORE_FIELD_NUMBER: _builtins.int - type: Global___Severity.Type.ValueType - """The type of this severity entry.""" - score: _builtins.str - """The quantitative score.""" - def __init__( - self, - *, - type: Global___Severity.Type.ValueType = ..., - score: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["score", b"score", "type", b"type"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Severity: _TypeAlias = Severity # noqa: Y015 - -@_typing.final -class Credit(_message.Message): - DESCRIPTOR: _descriptor.Descriptor - - class _Type: - ValueType = _typing.NewType("ValueType", _builtins.int) - V: _TypeAlias = ValueType # noqa: Y015 - - class _TypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[Credit._Type.ValueType], _builtins.type): - DESCRIPTOR: _descriptor.EnumDescriptor - UNSPECIFIED: Credit._Type.ValueType # 0 - OTHER: Credit._Type.ValueType # 1 - FINDER: Credit._Type.ValueType # 2 - REPORTER: Credit._Type.ValueType # 3 - ANALYST: Credit._Type.ValueType # 4 - COORDINATOR: Credit._Type.ValueType # 5 - REMEDIATION_DEVELOPER: Credit._Type.ValueType # 6 - REMEDIATION_REVIEWER: Credit._Type.ValueType # 7 - REMEDIATION_VERIFIER: Credit._Type.ValueType # 8 - TOOL: Credit._Type.ValueType # 9 - SPONSOR: Credit._Type.ValueType # 10 - - class Type(_Type, metaclass=_TypeEnumTypeWrapper): ... - UNSPECIFIED: Credit.Type.ValueType # 0 - OTHER: Credit.Type.ValueType # 1 - FINDER: Credit.Type.ValueType # 2 - REPORTER: Credit.Type.ValueType # 3 - ANALYST: Credit.Type.ValueType # 4 - COORDINATOR: Credit.Type.ValueType # 5 - REMEDIATION_DEVELOPER: Credit.Type.ValueType # 6 - REMEDIATION_REVIEWER: Credit.Type.ValueType # 7 - REMEDIATION_VERIFIER: Credit.Type.ValueType # 8 - TOOL: Credit.Type.ValueType # 9 - SPONSOR: Credit.Type.ValueType # 10 - - NAME_FIELD_NUMBER: _builtins.int - CONTACT_FIELD_NUMBER: _builtins.int - TYPE_FIELD_NUMBER: _builtins.int - name: _builtins.str - """The name to give credit to.""" - type: Global___Credit.Type.ValueType - """Optional. The type or role of the individual or entity being credited.""" - @_builtins.property - def contact(self) -> _containers.RepeatedScalarFieldContainer[_builtins.str]: - """Contact methods (URLs).""" - - def __init__( - self, - *, - name: _builtins.str = ..., - contact: _abc.Iterable[_builtins.str] | None = ..., - type: Global___Credit.Type.ValueType = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["contact", b"contact", "name", b"name", "type", b"type"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Credit: _TypeAlias = Credit # noqa: Y015 - -@_typing.final -class Reference(_message.Message): - """Reference URL.""" - - DESCRIPTOR: _descriptor.Descriptor - - class _Type: - ValueType = _typing.NewType("ValueType", _builtins.int) - V: _TypeAlias = ValueType # noqa: Y015 - - class _TypeEnumTypeWrapper(_enum_type_wrapper._EnumTypeWrapper[Reference._Type.ValueType], _builtins.type): - DESCRIPTOR: _descriptor.EnumDescriptor - NONE: Reference._Type.ValueType # 0 - """Next ID = 12.""" - ADVISORY: Reference._Type.ValueType # 2 - ARTICLE: Reference._Type.ValueType # 6 - DETECTION: Reference._Type.ValueType # 9 - DISCUSSION: Reference._Type.ValueType # 8 - EVIDENCE: Reference._Type.ValueType # 7 - FIX: Reference._Type.ValueType # 4 - GIT: Reference._Type.ValueType # 11 - INTRODUCED: Reference._Type.ValueType # 10 - PACKAGE: Reference._Type.ValueType # 5 - REPORT: Reference._Type.ValueType # 3 - WEB: Reference._Type.ValueType # 1 - - class Type(_Type, metaclass=_TypeEnumTypeWrapper): ... - NONE: Reference.Type.ValueType # 0 - """Next ID = 12.""" - ADVISORY: Reference.Type.ValueType # 2 - ARTICLE: Reference.Type.ValueType # 6 - DETECTION: Reference.Type.ValueType # 9 - DISCUSSION: Reference.Type.ValueType # 8 - EVIDENCE: Reference.Type.ValueType # 7 - FIX: Reference.Type.ValueType # 4 - GIT: Reference.Type.ValueType # 11 - INTRODUCED: Reference.Type.ValueType # 10 - PACKAGE: Reference.Type.ValueType # 5 - REPORT: Reference.Type.ValueType # 3 - WEB: Reference.Type.ValueType # 1 - - TYPE_FIELD_NUMBER: _builtins.int - URL_FIELD_NUMBER: _builtins.int - type: Global___Reference.Type.ValueType - """Required. The type of the reference.""" - url: _builtins.str - """Required. The URL.""" - def __init__( - self, - *, - type: Global___Reference.Type.ValueType = ..., - url: _builtins.str = ..., - ) -> None: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["type", b"type", "url", b"url"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Reference: _TypeAlias = Reference # noqa: Y015 - -@_typing.final -class Vulnerability(_message.Message): - """A vulnerability entry. - The protobuf representation is *NOT* stable and only used for implementing - the JSON based API. - """ - - DESCRIPTOR: _descriptor.Descriptor - - SCHEMA_VERSION_FIELD_NUMBER: _builtins.int - ID_FIELD_NUMBER: _builtins.int - PUBLISHED_FIELD_NUMBER: _builtins.int - MODIFIED_FIELD_NUMBER: _builtins.int - WITHDRAWN_FIELD_NUMBER: _builtins.int - ALIASES_FIELD_NUMBER: _builtins.int - RELATED_FIELD_NUMBER: _builtins.int - UPSTREAM_FIELD_NUMBER: _builtins.int - SUMMARY_FIELD_NUMBER: _builtins.int - DETAILS_FIELD_NUMBER: _builtins.int - AFFECTED_FIELD_NUMBER: _builtins.int - REFERENCES_FIELD_NUMBER: _builtins.int - DATABASE_SPECIFIC_FIELD_NUMBER: _builtins.int - SEVERITY_FIELD_NUMBER: _builtins.int - CREDITS_FIELD_NUMBER: _builtins.int - schema_version: _builtins.str - """The OSV schema version.""" - id: _builtins.str - """The `id` field is a unique identifier for the vulnerability entry. It is a - string of the format `-`, where `DB` names the database and - `ENTRYID` is in the format used by the database. For example: - “OSV-2020-111”, “CVE-2021-3114”, or “GHSA-vp9c-fpxx-744v”. - """ - summary: _builtins.str - """Required. One line human readable summary for the vulnerability. It is - recommended to keep this under 120 characters. - """ - details: _builtins.str - """Required. Any additional human readable details for the vulnerability.""" - @_builtins.property - def published(self) -> _timestamp_pb2.Timestamp: - """The RFC3339 timestamp indicating when this entry was published.""" - - @_builtins.property - def modified(self) -> _timestamp_pb2.Timestamp: - """The RFC3339 timestamp indicating when this entry was last modified.""" - - @_builtins.property - def withdrawn(self) -> _timestamp_pb2.Timestamp: - """Optional. The RFC3339 timestamp indicating when this entry is considered to - be withdrawn. - """ - - @_builtins.property - def aliases(self) -> _containers.RepeatedScalarFieldContainer[_builtins.str]: - """Optional. IDs for the same vulnerability in other databases.""" - - @_builtins.property - def related(self) -> _containers.RepeatedScalarFieldContainer[_builtins.str]: - """Optional. List of IDs of closely related vulnerabilities, such as the same - problem in alternate ecosystems. - """ - - @_builtins.property - def upstream(self) -> _containers.RepeatedScalarFieldContainer[_builtins.str]: - """Optional. List of IDs of upstream vulnerabilities of a vulnerability.""" - - @_builtins.property - def affected(self) -> _containers.RepeatedCompositeFieldContainer[Global___Affected]: - """Required. Affected commit ranges and versions.""" - - @_builtins.property - def references(self) -> _containers.RepeatedCompositeFieldContainer[Global___Reference]: - """Optional. URLs to more information/advisories (including the - scheme e.g. "https://"). - """ - - @_builtins.property - def database_specific(self) -> _struct_pb2.Struct: - """Optional. JSON object holding additional information about the - vulnerability as defined by the database for which the record applies. - """ - - @_builtins.property - def severity(self) -> _containers.RepeatedCompositeFieldContainer[Global___Severity]: - """Optional. Severity of the vulnerability.""" - - @_builtins.property - def credits(self) -> _containers.RepeatedCompositeFieldContainer[Global___Credit]: - """Optional. Credits for the vulnerability.""" - - def __init__( - self, - *, - schema_version: _builtins.str = ..., - id: _builtins.str = ..., - published: _timestamp_pb2.Timestamp | None = ..., - modified: _timestamp_pb2.Timestamp | None = ..., - withdrawn: _timestamp_pb2.Timestamp | None = ..., - aliases: _abc.Iterable[_builtins.str] | None = ..., - related: _abc.Iterable[_builtins.str] | None = ..., - upstream: _abc.Iterable[_builtins.str] | None = ..., - summary: _builtins.str = ..., - details: _builtins.str = ..., - affected: _abc.Iterable[Global___Affected] | None = ..., - references: _abc.Iterable[Global___Reference] | None = ..., - database_specific: _struct_pb2.Struct | None = ..., - severity: _abc.Iterable[Global___Severity] | None = ..., - credits: _abc.Iterable[Global___Credit] | None = ..., - ) -> None: ... - _HasFieldArgType: _TypeAlias = _typing.Literal["database_specific", b"database_specific", "modified", b"modified", "published", b"published", "withdrawn", b"withdrawn"] # noqa: Y015 - def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["affected", b"affected", "aliases", b"aliases", "credits", b"credits", "database_specific", b"database_specific", "details", b"details", "id", b"id", "modified", b"modified", "published", b"published", "references", b"references", "related", b"related", "schema_version", b"schema_version", "severity", b"severity", "summary", b"summary", "upstream", b"upstream", "withdrawn", b"withdrawn"] # noqa: Y015 - def ClearField(self, field_name: _ClearFieldArgType) -> None: ... - -Global___Vulnerability: _TypeAlias = Vulnerability # noqa: Y015 diff --git a/gcp/api/vulnerability_pb2_grpc.py b/gcp/api/vulnerability_pb2_grpc.py deleted file mode 100644 index b11e837a56a..00000000000 --- a/gcp/api/vulnerability_pb2_grpc.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc -import warnings - - -GRPC_GENERATED_VERSION = '1.75.0' -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f'The grpc package installed is at version {GRPC_VERSION},' - + f' but the generated code in vulnerability_pb2_grpc.py depends on' - + f' grpcio>={GRPC_GENERATED_VERSION}.' - + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' - + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - ) diff --git a/osv.dev.code-workspace b/osv.dev.code-workspace index 7f3b9e1ed3d..92c60dfdf03 100644 --- a/osv.dev.code-workspace +++ b/osv.dev.code-workspace @@ -12,9 +12,6 @@ { "path": "go/cmd/tools/reimport-tui" }, - { - "path": "gcp/api" - }, { "path": "gcp/website" }, diff --git a/proto/google/api/annotations.proto b/proto/google/api/annotations.proto new file mode 100644 index 00000000000..417edd8fa19 --- /dev/null +++ b/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/proto/google/api/http.proto b/proto/google/api/http.proto new file mode 100644 index 00000000000..bb3af8e56a0 --- /dev/null +++ b/proto/google/api/http.proto @@ -0,0 +1,370 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// - HTTP: `GET /v1/messages/123456` +// - gRPC: `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// - HTTP: `GET /v1/messages/123456?revision=2&sub.subfield=foo` +// - gRPC: `GetMessage(message_id: "123456" revision: 2 sub: +// SubMessage(subfield: "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` +// - gRPC: `UpdateMessage(message_id: "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` +// - gRPC: `UpdateMessage(message_id: "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// - HTTP: `GET /v1/messages/123456` +// - gRPC: `GetMessage(message_id: "123456")` +// +// - HTTP: `GET /v1/users/me/messages/123456` +// - gRPC: `GetMessage(user_id: "me" message_id: "123456")` +// +// Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// The following example selects a gRPC method and applies an `HttpRule` to it: +// +// http: +// rules: +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/gcp/api/v1/README.md b/proto/v1/README.md similarity index 80% rename from gcp/api/v1/README.md rename to proto/v1/README.md index c617e4dba97..c744f900596 100644 --- a/gcp/api/v1/README.md +++ b/proto/v1/README.md @@ -7,18 +7,21 @@ Updates to the protobufs and service configurations require a few steps, outlined below. ## Regenerate protobufs +Run from the root directory: +```sh +make build-protos ``` -python3 -m grpc_tools.protoc \ +Or directly using `protoc`: +```sh +protoc \ --include_imports \ --include_source_info \ - --proto_path=googleapis \ - --proto_path=. \ - --proto_path=.. \ - --descriptor_set_out=api_descriptor.pb \ - --python_out=../. \ - --grpc_python_out=../ \ - --mypy_out=../ \ - osv_service_v1.proto + --proto_path=proto \ + --proto_path=proto/v1 \ + --proto_path=osv \ + --proto_path=osv/osv-schema/proto \ + --descriptor_set_out=proto/v1/api_descriptor.pb \ + proto/v1/osv_service_v1.proto ``` ## Deploy service proxy diff --git a/gcp/api/v1/api_config_test.yaml b/proto/v1/api_config_test.yaml similarity index 100% rename from gcp/api/v1/api_config_test.yaml rename to proto/v1/api_config_test.yaml diff --git a/gcp/api/v1/api_descriptor.pb b/proto/v1/api_descriptor.pb similarity index 69% rename from gcp/api/v1/api_descriptor.pb rename to proto/v1/api_descriptor.pb index c1fd98cb732..1327f3bede0 100644 Binary files a/gcp/api/v1/api_descriptor.pb and b/proto/v1/api_descriptor.pb differ diff --git a/gcp/api/v1/osv_service_v1.proto b/proto/v1/osv_service_v1.proto similarity index 100% rename from gcp/api/v1/osv_service_v1.proto rename to proto/v1/osv_service_v1.proto diff --git a/renovate.json b/renovate.json index 2af0f5e7a61..d9337119b75 100644 --- a/renovate.json +++ b/renovate.json @@ -53,16 +53,6 @@ }, { "matchFileNames": [ - "gcp/api/**" - ], - "matchPackageNames": [ - "grpcio*" - ], - "enabled": false - }, - { - "matchFileNames": [ - "gcp/api/**", "gcp/functions/**", "gcp/workers/**" ], diff --git a/gcp/api/snapshot_tests.yaml b/tools/apitester/cloudbuild.yaml similarity index 61% rename from gcp/api/snapshot_tests.yaml rename to tools/apitester/cloudbuild.yaml index 4f4381dd621..4d7e274dbf9 100644 --- a/gcp/api/snapshot_tests.yaml +++ b/tools/apitester/cloudbuild.yaml @@ -12,37 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -# api e2e test runner +# apitester snapshot test runner # This should be triggered on changes to the following: -# gcp/api/poetry.lock -# gcp/api/run_tests_e2e.sh -# gcp/api/*.py -# gcp/api/**/*.py -# osv/*.py -# osv/**/*.py # tools/apitester/** -steps: -- name: 'gcr.io/oss-vdb/ci' - id: 'sync' - dir: gcp/api - args: ['poetry', 'sync'] - -- name: 'gcr.io/oss-vdb/ci' - id: 'build-go-backend' - dir: go - args: ['go', 'build', '-o', 'api', './cmd/api'] - waitFor: ['-'] +steps: - name: 'gcr.io/oss-vdb/ci' id: 'api-snapshot-tests' - dir: gcp/api - args: ['bash', '-ex', 'run_tests_e2e.sh', '/workspace/dummy.json'] - waitFor: ['build-go-backend', 'sync'] + dir: tools/apitester + args: ['bash', '-ex', 'run_tests.sh'] + env: + - 'OSV_API_BASE_URL=api.test.osv.dev' -timeout: 7200s +timeout: 1800s serviceAccount: 'projects/oss-vdb/serviceAccounts/api-e2e-tester@oss-vdb.iam.gserviceaccount.com' options: logging: CLOUD_LOGGING_ONLY - env: - - CLOUDBUILD=1 - - OSV_USE_GO_BACKEND=1 diff --git a/tools/apitester/run_tests.sh b/tools/apitester/run_tests.sh new file mode 100755 index 00000000000..3e077f3c8a3 --- /dev/null +++ b/tools/apitester/run_tests.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")" + +# If OSV_API_BASE_URL is explicitly set, run directly against that endpoint. +if [ -n "${OSV_API_BASE_URL:-}" ]; then + echo "Running apitester against $OSV_API_BASE_URL..." + cd "$SCRIPT_DIR" + go test ./... + exit 0 +fi + +# Check for GCP Application Default Credentials +CRED_PATH="${HOME}/.config/gcloud/application_default_credentials.json" +if [ ! -f "$CRED_PATH" ]; then + echo "GCP Application Default Credentials not found at $CRED_PATH." + echo "Please run 'gcloud auth application-default login' or set OSV_API_BASE_URL (e.g. OSV_API_BASE_URL=api.test.osv.dev)" + exit 1 +fi + +# Ensure the local ESPv2 Docker image is built +docker inspect osv/esp:latest >/dev/null 2>&1 || docker build -f "$ROOT_DIR/docker/esp/Dockerfile" -t osv/esp:latest "$ROOT_DIR/docker/esp" + +# Build api-devserver +echo "Building api-devserver..." +cd "$ROOT_DIR/go" +go build -o ./api-devserver ./cmd/api-devserver + +# Launch api-devserver against the staging/test dataset in the background +echo "Starting local API test server..." +GOOGLE_CLOUD_PROJECT="${GOOGLE_CLOUD_PROJECT:-oss-vdb-test}" \ +OSV_VULNERABILITIES_BUCKET="${OSV_VULNERABILITIES_BUCKET:-osv-test-vulnerabilities}" \ +./api-devserver >/dev/null 2>&1 & +SERVER_PID=$! + +cleanup() { + echo "Stopping API test server..." + kill "$SERVER_PID" 2>/dev/null || true + docker stop osv-esp >/dev/null 2>&1 || true + rm -f "$ROOT_DIR/go/api-devserver" +} +trap cleanup EXIT INT TERM + +# Wait for the API endpoint to be responsive (timeout after 30s) +echo "Waiting for API server to become ready..." +READY=0 +for _ in $(seq 1 30); do + if curl -s "http://localhost:8080/v1/vulns/GHSA-1" >/dev/null 2>&1; then + READY=1 + break + fi + sleep 1 +done + +if [ "$READY" -ne 1 ]; then + echo "Error: API server failed to start within 30 seconds." + exit 1 +fi + +echo "API server is ready. Running tests..." +cd "$SCRIPT_DIR" +go test ./...