SERVER-112109: Evergreen Task and scripts to run Server SBOM upload Google Drive at release - 8.0 backport (#43226)

GitOrigin-RevId: d8135f9d92d8ef43783376d6737c3bbd72e0f639
This commit is contained in:
ekovalets
2025-11-05 10:54:10 -08:00
committed by MongoDB Bot
parent 5fee9a56fe
commit f9769f7239
10 changed files with 481 additions and 3 deletions

View File

@@ -55,3 +55,81 @@ tasks:
remote_file: ${project}/${build_variant}/${revision}/artifacts/${build_id}/${task_name}/
permissions: private
visibility: signed
- name: publish-augmented-sbom
tags: ["auxiliary", "assigned_to_jira_team_platsec_server"]
depends_on:
- name: version_expansions_gen
variant: generate-tasks-for-version
exec_timeout_secs: 600 # 10 minute timeout
commands:
- command: manifest.load
- func: "git get project and add git tag"
- func: "get version expansions"
- func: "apply version expansions"
- func: "f_expansions_write"
- func: "kill processes"
- func: "cleanup environment"
- func: "set up venv"
- func: "upload pip requirements"
- command: ec2.assume_role
display_name: Assume Silkbomb IAM role
params:
role_arn: arn:aws:iam::119629040606:role/silkbomb
- func: "f_expansions_write"
- command: subprocess.exec
display_name: Write temporary AWS credentials to Silkbomb environment file
params:
binary: bash
args:
- "src/evergreen/functions/security_reporting_scripts/write_aws_creds_to_silkbomb_env_file.sh"
include_expansions_in_env:
[AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]
- command: ec2.assume_role
display_name: Assume DevProd Platforms ECR readonly IAM role
params:
role_arn: arn:aws:iam::901841024863:role/ecr-role-evergreen-ro
- func: "f_expansions_write"
- command: subprocess.exec
display_name: Run Silkbomb to augment SBOM with VEX data
params:
binary: bash
args:
- "src/evergreen/functions/security_reporting_scripts/augment_sbom.sh"
include_expansions_in_env:
[AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]
env:
REQUESTER: ${requester}
BRANCH_NAME: ${branch_name}
GITHUB_ORG: ${github_org}
GITHUB_REPO: ${github_repo}
CONTAINER_COMMAND: docker # podman or docker
CONTAINER_OPTIONS: --pull=always --platform=linux/amd64 -i --rm
CONTAINER_ENV_FILES: ${workdir}/silkbomb.env
CONTAINER_VOLUMES: -v ${workdir}:/workdir
CONTAINER_IMAGE: 901841024863.dkr.ecr.us-east-1.amazonaws.com/release-infrastructure/silkbomb:2.0
SBOM_REPO_PATH: sbom.json
SBOM_OUT_PATH: ${workdir}/sbom-with-vex-${branch_name}.json
SILKBOMB_COMMAND: augment
SILKBOMB_ARGS: --sbom-in /workdir/src/sbom.json --sbom-out /workdir/src/sbom-with-vex-${branch_name}.json --repo ${github_repo} --branch ${branch_name}
- command: subprocess.exec
display_name: Upload SBOM to Google Drive"
params:
binary: bash
args:
- "${workdir}/src/evergreen/run_python_script.sh"
- "${workdir}/src/evergreen/functions/security_reporting_scripts/upload_to_google_drive.py"
- "${workdir}/src/sbom-with-vex-${branch_name}.json"
env:
WORK_DIR: ${workdir}
GITHUB_COMMIT: ${github_commit}
TRIGGERED_BY_GIT_TAG: ${triggered_by_git_tag}
MONGODB_VERSION: ${version}
MONGODB_RELEASE_BRANCH: ${branch_name}
SBOM_OUT_PATH: ${workdir}/sbom-with-vex-${branch_name}.json
UPLOAD_FILE_NAME: "[${version}] MongoDB Server Enterprise SBOM"
SBOM_REPORT_TEST_GOOGLE_DRIVE_FOLDER_ID: ${SBOM_REPORT_TEST_GOOGLE_DRIVE_FOLDER_ID}
SBOM_REPORT_RELEASES_GOOGLE_DRIVE_FOLDER_ID: ${SBOM_REPORT_RELEASES_GOOGLE_DRIVE_FOLDER_ID}
SAST_REPORT_UPLOAD_GOOGLE_CLIENT_ID: ${SAST_REPORT_UPLOAD_GOOGLE_CLIENT_ID}
SAST_REPORT_UPLOAD_GOOGLE_CLIENT_REFRESH_TOKEN: ${SAST_REPORT_UPLOAD_GOOGLE_CLIENT_REFRESH_TOKEN}
SAST_REPORT_UPLOAD_GOOGLE_CLIENT_SECRET: ${SAST_REPORT_UPLOAD_GOOGLE_CLIENT_SECRET}

View File

@@ -48,6 +48,7 @@ buildvariants:
- devprod_coverity
tasks:
- name: publish-sast-report
- name: publish-augmented-sbom
- name: &copybara-sync-between-repos copybara-sync-between-repos
display_name: "* Copybara Sync Between Repos"

View File

@@ -8,3 +8,4 @@ buildvariants:
- devprod_coverity
tasks:
- name: publish-sast-report
- name: publish-augmented-sbom

View File

@@ -0,0 +1,17 @@
package(default_visibility = ["//visibility:public"])
sh_binary(
name = "augment_sbom",
srcs = ["augment_sbom.sh"],
)
sh_binary(
name = "write_aws_creds_to_silkbomb_env_file",
srcs = ["write_aws_creds_to_silkbomb_env_file.sh"],
)
py_library(
name = "all_python_files",
srcs = glob(["*.py"]),
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,5 @@
version: 2.0.0
filters:
- "*":
approvers:
- 10gen/platsec-server

View File

@@ -0,0 +1,55 @@
# !/bin/bash
# Augment SBOM using SilkBomb inside a container.
#
# Usage:
# augment_sbom
#
# The script uses SilkBomb.
# See: https://docs.devprod.prod.corp.mongodb.com/mms/python/src/sbom/silkbomb/
#
# Required system environment variables:
# AWS_ACCESS_KEY_ID
# AWS_SECRET_ACCESS_KEY
# AWS_SESSION_TOKEN
#
# Required script env variables:
# CONTAINER_COMMAND
# CONTAINER_OPTIONS
# CONTAINER_ENV_FILES
# CONTAINER_VOLUMES
# CONTAINER_IMAGE
# SBOM_REPO_PATH
# SBOM_OUT_PATH
# SILKBOMB_COMMAND
# SILKBOMB_ARGS
# requester
# branch_name
# github_org
# github_repo
# workdir
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)"
. "$DIR/../../prelude.sh"
set -o errexit
set -o verbose
set -o pipefail
read -ra OPTS_ARRAY <<< "$CONTAINER_OPTIONS"
read -ra VOLUMES_ARRAY <<< "$CONTAINER_VOLUMES"
read -ra ARGS_ARRAY <<< "$SILKBOMB_ARGS"
echo "--> Logging in to AWS ECR..."
aws ecr get-login-password --region us-east-1 | "${CONTAINER_COMMAND}" login --username AWS --password-stdin 901841024863.dkr.ecr.us-east-1.amazonaws.com
echo "--> Running the container..."
# The "${VAR[@]}" syntax expands arrays safely, with each element becoming a distinct argument.
"${CONTAINER_COMMAND}" run \
"${OPTS_ARRAY[@]}" \
--env-file "${CONTAINER_ENV_FILES}" \
"${VOLUMES_ARRAY[@]}" \
"${CONTAINER_IMAGE}" \
"${SILKBOMB_COMMAND}" \
"${ARGS_ARRAY[@]}"
echo "--> Script finished successfully."

View File

@@ -0,0 +1,119 @@
import os
import sys
from pathlib import Path
import typer
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaFileUpload
app = typer.Typer(add_completion=False)
@app.command()
def upload(
input_file: Path = typer.Argument(
...,
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
resolve_path=True,
help="The path to the file to upload.",
),
client_id: str = typer.Option(
...,
envvar="SAST_REPORT_UPLOAD_GOOGLE_CLIENT_ID",
help="The OAuth2 client ID for Google API.",
),
client_secret: str = typer.Option(
...,
envvar="SAST_REPORT_UPLOAD_GOOGLE_CLIENT_SECRET",
help="The OAuth2 client secret for Google API.",
),
refresh_token: str = typer.Option(
...,
envvar="SAST_REPORT_UPLOAD_GOOGLE_CLIENT_REFRESH_TOKEN",
help="The OAuth2 refresh token for Google API.",
),
branch: str = typer.Option(
..., envvar="MONGODB_RELEASE_BRANCH", help="The MongoDB release branch."
),
test_folder_id: str = typer.Option(
...,
envvar="SBOM_REPORT_TEST_GOOGLE_DRIVE_FOLDER_ID",
help="The ID of the Google Drive folder for test uploads.",
),
releases_folder_id: str = typer.Option(
...,
envvar="SBOM_REPORT_RELEASES_GOOGLE_DRIVE_FOLDER_ID",
help="The ID of the Google Drive folder for releases.",
),
triggered_by_tag: str = typer.Option(
"",
envvar="TRIGGERED_BY_GIT_TAG",
help="Indicates if the upload was triggered by a git tag.",
),
version: str = typer.Option(
"", envvar="MONGODB_VERSION", help="The version of MongoDB being processed."
),
upload_file_name: str = typer.Option(
None,
envvar="UPLOAD_FILE_NAME",
help="Optional upload file to use. If not provided, the input file name will be used.",
),
):
print("Starting file upload process to Google Drive.")
try:
creds_info = {
"client_id": client_id,
"client_secret": client_secret,
"refresh_token": refresh_token,
"token_uri": "https://oauth2.googleapis.com/token",
}
creds = Credentials.from_authorized_user_info(
info=creds_info, scopes=["https://www.googleapis.com/auth/drive"]
)
drive_service = build("drive", "v3", credentials=creds)
print("Authenticated with Google Drive API successfully.")
except Exception as e:
print(f"Failed to authenticate with Google API: {e}")
sys.exit(1)
folder_id = releases_folder_id if triggered_by_tag.lower() == "true" else test_folder_id
if upload_file_name is None:
input_file_name_str = str(input_file.resolve().name)
else:
_, file_extension = os.path.splitext(input_file.resolve().name)
input_file_name_str = f"{upload_file_name}{file_extension}"
file_metadata = {"name": input_file_name_str, "parents": [folder_id]}
media = MediaFileUpload(str(input_file), mimetype="text/html", resumable=True)
try:
print(f"Uploading '{input_file}' to Google Drive...")
print(f"File name on Drive: '{input_file_name_str}'")
file = (
drive_service.files()
.create(
body=file_metadata,
media_body=media,
fields="id, webViewLink",
supportsAllDrives=True,
)
.execute()
)
print("Upload complete.")
print(f"File ID: {file.get('id')}")
print(f"View Link: {file.get('webViewLink')}")
except HttpError as error:
print(f"An API error occurred: {error}")
sys.exit(1)
if __name__ == "__main__":
app()

View File

@@ -0,0 +1,21 @@
#!/bin/bash
#
# This is an auxiliary script that writes AWS credentials to an env file.
# Being used by augment_sbom.sh script and SBOM upload tasks to prepare the env file for SilkBomb.
#
# Usage:
# write_aws_creds_to_silkbomb_env_file.sh
#
# Required system environment variables:
# AWS_ACCESS_KEY_ID
# AWS_SECRET_ACCESS_KEY
# AWS_SESSION_TOKEN
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)"
. "$DIR/../../prelude.sh"
cat << EOF > "${workdir}/silkbomb.env"
AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}
EOF

185
poetry.lock generated
View File

@@ -214,6 +214,18 @@ dev = ["CacheControl[filecache,redis]", "build", "cherrypy", "codespell[tomli]",
filecache = ["filelock (>=3.8.0)"]
redis = ["redis (>=2.10.5)"]
[[package]]
name = "cachetools"
version = "6.2.1"
description = "Extensible memoizing collections and decorators"
optional = false
python-versions = ">=3.9"
groups = ["testing"]
files = [
{file = "cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701"},
{file = "cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201"},
]
[[package]]
name = "certifi"
version = "2024.2.2"
@@ -1073,6 +1085,116 @@ gitdb = ">=4.0.1,<5"
[package.extras]
test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"]
[[package]]
name = "google-api-core"
version = "2.28.0"
description = "Google API client core library"
optional = false
python-versions = ">=3.7"
groups = ["testing"]
files = [
{file = "google_api_core-2.28.0-py3-none-any.whl", hash = "sha256:b4362b0e2e6bc06037cfb0e2b28e2fe0c3f9d760dc311f314d5fb373768c7387"},
{file = "google_api_core-2.28.0.tar.gz", hash = "sha256:4743b7d45fe8c0930e59928b1bade287242910f30b06ff9b22f139a3e33271b8"},
]
[package.dependencies]
google-auth = ">=2.14.1,<3.0.0"
googleapis-common-protos = ">=1.56.2,<2.0.0"
proto-plus = [
{version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""},
{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)", "grpcio (>=1.75.1,<2.0.0)", "grpcio-status (>=1.33.2,<2.0.0)", "grpcio-status (>=1.49.1,<2.0.0)", "grpcio-status (>=1.75.1,<2.0.0)"]
grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"]
grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"]
[[package]]
name = "google-api-python-client"
version = "2.123.0"
description = "Google API Client Library for Python"
optional = false
python-versions = ">=3.7"
groups = ["testing"]
files = [
{file = "google-api-python-client-2.123.0.tar.gz", hash = "sha256:a17226b02f71de581afe045437b441844110a9cd91580b73549d41108cf1b9f0"},
{file = "google_api_python_client-2.123.0-py2.py3-none-any.whl", hash = "sha256:1c2bcaa846acf5bac4d6f244d8373d4de9de73d64eb6e77b56767ab4cf681419"},
]
[package.dependencies]
google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0.dev0"
google-auth = ">=1.19.0,<3.0.0.dev0"
google-auth-httplib2 = ">=0.1.0"
httplib2 = ">=0.15.0,<1.dev0"
uritemplate = ">=3.0.1,<5"
[[package]]
name = "google-auth"
version = "2.41.1"
description = "Google Authentication Library"
optional = false
python-versions = ">=3.7"
groups = ["testing"]
files = [
{file = "google_auth-2.41.1-py2.py3-none-any.whl", hash = "sha256:754843be95575b9a19c604a848a41be03f7f2afd8c019f716dc1f51ee41c639d"},
{file = "google_auth-2.41.1.tar.gz", hash = "sha256:b76b7b1f9e61f0cb7e88870d14f6a94aeef248959ef6992670efee37709cbfd2"},
]
[package.dependencies]
cachetools = ">=2.0.0,<7.0"
pyasn1-modules = ">=0.2.1"
rsa = ">=3.1.4,<5"
[package.extras]
aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"]
enterprise-cert = ["cryptography", "pyopenssl"]
pyjwt = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"]
pyopenssl = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"]
reauth = ["pyu2f (>=0.1.5)"]
requests = ["requests (>=2.20.0,<3.0.0)"]
testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "cryptography (<39.0.0)", "cryptography (<39.0.0)", "cryptography (>=38.0.3)", "cryptography (>=38.0.3)", "flask", "freezegun", "grpcio", "mock", "oauth2client", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"]
urllib3 = ["packaging", "urllib3"]
[[package]]
name = "google-auth-httplib2"
version = "0.1.0"
description = "Google Authentication Library: httplib2 transport"
optional = false
python-versions = "*"
groups = ["testing"]
files = [
{file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"},
{file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"},
]
[package.dependencies]
google-auth = "*"
httplib2 = ">=0.15.0"
six = "*"
[[package]]
name = "google-auth-oauthlib"
version = "1.2.2"
description = "Google Authentication Library"
optional = false
python-versions = ">=3.6"
groups = ["testing"]
files = [
{file = "google_auth_oauthlib-1.2.2-py3-none-any.whl", hash = "sha256:fd619506f4b3908b5df17b65f39ca8d66ea56986e5472eb5978fd8f3786f00a2"},
{file = "google_auth_oauthlib-1.2.2.tar.gz", hash = "sha256:11046fb8d3348b296302dd939ace8af0a724042e8029c1b872d87fabc9f41684"},
]
[package.dependencies]
google-auth = ">=2.15.0"
requests-oauthlib = ">=0.7.0"
[package.extras]
tool = ["click (>=6.0.0)"]
[[package]]
name = "googleapis-common-protos"
version = "1.62.0"
@@ -1176,6 +1298,18 @@ files = [
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
]
[[package]]
name = "httplib2"
version = "0.18.1"
description = "A comprehensive HTTP client library."
optional = false
python-versions = "*"
groups = ["testing"]
files = [
{file = "httplib2-0.18.1-py3-none-any.whl", hash = "sha256:ca2914b015b6247791c4866782fa6042f495b94401a0f0bd3e1d6e0ba2236782"},
{file = "httplib2-0.18.1.tar.gz", hash = "sha256:8af66c1c52c7ffe1aa5dc4bcd7c769885254b0756e6e69f953c7f0ab49a70ba3"},
]
[[package]]
name = "hyperlink"
version = "21.0.0"
@@ -2319,6 +2453,24 @@ python-utils = ">=3.8.1"
docs = ["sphinx (>=1.8.5)", "sphinx-autodoc-typehints (>=1.6.0)"]
tests = ["dill (>=0.3.6)", "flake8 (>=3.7.7)", "freezegun (>=0.3.11)", "pytest (>=4.6.9)", "pytest-cov (>=2.6.1)", "pytest-mypy", "sphinx (>=1.8.5)"]
[[package]]
name = "proto-plus"
version = "1.26.1"
description = "Beautiful, Pythonic protocol buffers"
optional = false
python-versions = ">=3.7"
groups = ["testing"]
files = [
{file = "proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66"},
{file = "proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012"},
]
[package.dependencies]
protobuf = ">=3.19.0,<7.0.0"
[package.extras]
testing = ["google-api-core (>=1.31.5)"]
[[package]]
name = "protobuf"
version = "4.25.2"
@@ -2416,7 +2568,7 @@ version = "0.5.1"
description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
groups = ["external-auth"]
groups = ["external-auth", "testing"]
files = [
{file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"},
{file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"},
@@ -2428,7 +2580,7 @@ version = "0.3.0"
description = "A collection of ASN.1-based protocols modules"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
groups = ["external-auth"]
groups = ["external-auth", "testing"]
files = [
{file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"},
{file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"},
@@ -3418,6 +3570,21 @@ files = [
{file = "rpds_py-0.17.1.tar.gz", hash = "sha256:0210b2668f24c078307260bf88bdac9d6f1093635df5123789bfee4d8d7fc8e7"},
]
[[package]]
name = "rsa"
version = "4.9.1"
description = "Pure-Python RSA implementation"
optional = false
python-versions = "<4,>=3.6"
groups = ["testing"]
files = [
{file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"},
{file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"},
]
[package.dependencies]
pyasn1 = ">=0.1.3"
[[package]]
name = "s3transfer"
version = "0.10.0"
@@ -4019,6 +4186,18 @@ files = [
{file = "unittest_xml_reporting-3.0.4-py2.py3-none-any.whl", hash = "sha256:7bf515ea8cb244255a25100cd29db611a73f8d3d0aaf672ed3266307e14cc1ca"},
]
[[package]]
name = "uritemplate"
version = "4.2.0"
description = "Implementation of RFC 6570 URI Templates"
optional = false
python-versions = ">=3.9"
groups = ["testing"]
files = [
{file = "uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686"},
{file = "uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e"},
]
[[package]]
name = "urllib3"
version = "1.26.18"
@@ -4423,4 +4602,4 @@ oldcrypt = []
[metadata]
lock-version = "2.1"
python-versions = ">=3.10,<4.0"
content-hash = "3d68e8c7f9cd0f11850efa26c1a30e1d2273e8afa1857f802639543bf140e8c8"
content-hash = "788ff9b867ff983ca4544f1130137c872d40a7e56e441d041948a3a21c790b56"

View File

@@ -168,6 +168,8 @@ geckodriver-autoinstaller = "^0.1.0"
retry = "^0.9.2"
gdbmongo = "^0.15.1"
googleapis-common-protos = "^1.61.0"
google-api-python-client = "^2.19.0"
google-auth-oauthlib = "^1.2.1"
opentelemetry-api = "*"
opentelemetry-sdk = "*"
opentelemetry-exporter-otlp-proto-common = "*"