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 ./...