From fb823060488413d088eca2731a3a6abe21b3640d Mon Sep 17 00:00:00 2001 From: Lukasz Gryglicki Date: Thu, 26 Feb 2026 06:57:49 +0100 Subject: [PATCH 1/2] Add OpenTelemetry/DataDog API logging [prod] Signed-off-by: Lukasz Gryglicki Assisted by [OpenAI](https://platform.openai.com/) Assisted by [GitHub Copilot](https://github.com/features/copilot) --- .github/dependabot.yml | 40 +- .github/workflows/build-pr.yml | 1 + .github/workflows/codeql-analysis.yml | 70 +-- .github/workflows/deploy-dev.yml | 1 + .github/workflows/deploy-prod.yml | 1 + .github/workflows/yarn-scan-backend-go-pr.yml | 11 +- .github/workflows/yarn-scan-backend-pr.yml | 11 +- .gitignore | 1 + .yarn-audit-allowlist.json | 9 + cla-backend-go/bootstrap | 21 + cla-backend-go/cmd/server.go | 88 ++- cla-backend-go/go.mod | 43 +- cla-backend-go/go.sum | 103 +++- cla-backend-go/package.json | 8 +- cla-backend-go/serverless.yml | 50 +- cla-backend-go/telemetry/datadog_otlp.go | 381 +++++++++++++ cla-backend-go/yarn.lock | 57 +- cla-backend/bootstrap | 21 + cla-backend/cla/routes.py | 513 +++++++++++++++++- cla-backend/package.json | 12 +- cla-backend/requirements.txt | 4 + cla-backend/serverless.yml | 269 +++++---- cla-backend/yarn.lock | 91 ++-- scripts/yarn-audit-filter.mjs | 44 ++ setenv.sh | 20 + tests/functional/cypress/support/commands.js | 66 +++ utils/check_ssm_logging_params.sh | 32 ++ utils/get_ssm_value.sh | 4 +- utils/otel_dd/check_spans.sh | 2 + utils/otel_dd/check_spans_in_ddog.sh | 48 ++ utils/otel_dd/otelcol-dd.yaml | 30 + utils/otel_dd/run_collector.sh | 19 + utils/otel_dd/validate_dd_keys.sh | 2 + utils/otel_dd_go/build.sh | 2 + utils/otel_dd_go/go.mod | 30 + utils/otel_dd_go/go.sum | 61 +++ utils/otel_dd_go/otel_dd.go | 245 +++++++++ utils/otel_dd_py/otel_dd.py | 178 ++++++ utils/set_ssm_logging_params.sh | 38 ++ utils/set_ssm_value.sh | 44 ++ 40 files changed, 2402 insertions(+), 269 deletions(-) create mode 100644 .yarn-audit-allowlist.json create mode 100755 cla-backend-go/bootstrap create mode 100644 cla-backend-go/telemetry/datadog_otlp.go create mode 100755 cla-backend/bootstrap create mode 100644 scripts/yarn-audit-filter.mjs create mode 100755 utils/check_ssm_logging_params.sh create mode 100755 utils/otel_dd/check_spans.sh create mode 100755 utils/otel_dd/check_spans_in_ddog.sh create mode 100644 utils/otel_dd/otelcol-dd.yaml create mode 100755 utils/otel_dd/run_collector.sh create mode 100755 utils/otel_dd/validate_dd_keys.sh create mode 100755 utils/otel_dd_go/build.sh create mode 100644 utils/otel_dd_go/go.mod create mode 100644 utils/otel_dd_go/go.sum create mode 100644 utils/otel_dd_go/otel_dd.go create mode 100755 utils/otel_dd_py/otel_dd.py create mode 100755 utils/set_ssm_logging_params.sh create mode 100755 utils/set_ssm_value.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml index de0bdfac6..4bd0665f7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,20 +12,50 @@ updates: - package-ecosystem: "npm" # See documentation for possible values directory: "/cla-landing-page" # Location of package manifests schedule: - interval: "weekly" + interval: "monthly" + open-pull-requests-limit: 3 + ignore: + - dependency-name: "serverless" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "serverless-domain-manager" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "npm" # See documentation for possible values directory: "/cla-backend" # Location of package manifests schedule: - interval: "weekly" + interval: "monthly" + open-pull-requests-limit: 3 + ignore: + - dependency-name: "serverless" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "serverless-domain-manager" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "pip" # See documentation for possible values directory: "/cla-backend" # Location of package manifests schedule: - interval: "weekly" + interval: "monthly" + open-pull-requests-limit: 3 + ignore: + - dependency-name: "serverless" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "serverless-domain-manager" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "npm" # See documentation for possible values directory: "/cla-backend-go" # Location of package manifests schedule: - interval: "weekly" + interval: "monthly" + open-pull-requests-limit: 3 + ignore: + - dependency-name: "serverless" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "serverless-domain-manager" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gomod" # See documentation for possible values directory: "/cla-backend-go" # Location of package manifests schedule: - interval: "weekly" + interval: "monthly" + open-pull-requests-limit: 3 + ignore: + - dependency-name: "serverless" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "serverless-domain-manager" + update-types: ["version-update:semver-major", "version-update:semver-minor", "version-update:semver-patch"] diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 67b9a6b71..2d5e4aa93 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -16,6 +16,7 @@ permissions: env: AWS_REGION: us-east-1 STAGE: dev + DD_VERSION: ${{ github.sha }} jobs: build-test-lint: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8f512f781..5b87b800b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,54 +5,42 @@ name: "CodeQL" on: push: - branches: [main, ] + branches: [main] pull_request: - # The branches below must be a subset of the branches above branches: [main] schedule: - cron: '0 5 * * 4' jobs: analyse: - name: Analyse + name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - # Note: git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results. - #- run: git checkout HEAD^2 - # if: ${{ github.event_name == 'pull_request' }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - # Override language selection by uncommenting this and choosing your languages - # with: - # languages: go, javascript, csharp, python, cpp, java - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + strategy: + fail-fast: false + matrix: + language: ['go', 'python', 'javascript'] - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v4 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index af6d370b8..bc7b7ddd1 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -16,6 +16,7 @@ permissions: env: AWS_REGION: us-east-1 STAGE: dev + DD_VERSION: ${{ github.sha }} jobs: build-deploy-dev: diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 0a48fa0e8..577fcdcf6 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -18,6 +18,7 @@ permissions: env: AWS_REGION: us-east-1 STAGE: prod + DD_VERSION: ${{ github.sha }} jobs: build-deploy-prod: diff --git a/.github/workflows/yarn-scan-backend-go-pr.yml b/.github/workflows/yarn-scan-backend-go-pr.yml index 43688e3d8..32d6a8ba2 100644 --- a/.github/workflows/yarn-scan-backend-go-pr.yml +++ b/.github/workflows/yarn-scan-backend-go-pr.yml @@ -9,6 +9,10 @@ on: pull_request: branches: - dev + paths: + - "cla-backend-go/package.json" + - "cla-backend-go/yarn.lock" + - ".github/workflows/yarn-scan-backend-go-pr.yml" jobs: yarn-scan-backend-go-pr: @@ -22,7 +26,12 @@ jobs: node-version: '20' - name: Setup run: yarn install +# - name: Yarn Audit +# working-directory: cla-backend-go +# run: | +# yarn audit - name: Yarn Audit working-directory: cla-backend-go run: | - yarn audit + yarn audit --json > audit.json || true + node ../scripts/yarn-audit-filter.mjs audit.json ../.yarn-audit-allowlist.json diff --git a/.github/workflows/yarn-scan-backend-pr.yml b/.github/workflows/yarn-scan-backend-pr.yml index 51a2f2f2d..d0bcad04f 100644 --- a/.github/workflows/yarn-scan-backend-pr.yml +++ b/.github/workflows/yarn-scan-backend-pr.yml @@ -9,6 +9,10 @@ on: pull_request: branches: - dev + paths: + - "cla-backend/package.json" + - "cla-backend/yarn.lock" + - ".github/workflows/yarn-scan-backend-pr.yml" jobs: yarn-scan-backend-pr: @@ -22,7 +26,12 @@ jobs: node-version: '20' - name: Setup run: yarn install +# - name: Yarn Audit +# working-directory: cla-backend +# run: | +# yarn audit - name: Yarn Audit working-directory: cla-backend run: | - yarn audit + yarn audit --json > audit.json || true + node ../scripts/yarn-audit-filter.mjs audit.json ../.yarn-audit-allowlist.json diff --git a/.gitignore b/.gitignore index 4d3950179..bd278f41a 100755 --- a/.gitignore +++ b/.gitignore @@ -266,3 +266,4 @@ cla-backend/python-api.log cla-backend/python-api.err cla-backend-go/golang-api.err cla-backend-go/golang-api.log +utils/otel_dd_go/otel_dd diff --git a/.yarn-audit-allowlist.json b/.yarn-audit-allowlist.json new file mode 100644 index 000000000..065751807 --- /dev/null +++ b/.yarn-audit-allowlist.json @@ -0,0 +1,9 @@ +{ + "minSeverity": "high", + "allowlist": [ + 1111997 + ], + "notes": { + "1111997": "aws-sdk v2 advisory flagged as 'No patch available' in our current baseline; accepted until migration." + } +} diff --git a/cla-backend-go/bootstrap b/cla-backend-go/bootstrap new file mode 100755 index 000000000..73e636bcf --- /dev/null +++ b/cla-backend-go/bootstrap @@ -0,0 +1,21 @@ +#!/bin/sh +set -eu + +# AWS Lambda custom runtime entrypoint (provided.al2 / provided.al2023). +# AWS executes /var/task/bootstrap. The configured Lambda "Handler" is exposed +# via the $_HANDLER env var. We exec that handler binary (which is our Go +# executable) so we don't need to rename every binary to 'bootstrap'. + +if [ -z "${_HANDLER:-}" ]; then + echo "bootstrap: _HANDLER is not set" >&2 + exit 1 +fi + +HANDLER_PATH="/var/task/${_HANDLER}" +if [ ! -x "${HANDLER_PATH}" ]; then + echo "bootstrap: handler '${HANDLER_PATH}' not found or not executable" >&2 + ls -la /var/task >&2 || true + exit 1 +fi + +exec "${HANDLER_PATH}" diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go index 94400a08d..c33ba6d8c 100644 --- a/cla-backend-go/cmd/server.go +++ b/cla-backend-go/cmd/server.go @@ -81,6 +81,7 @@ import ( "github.com/linuxfoundation/easycla/cla-backend-go/api_logs" "github.com/linuxfoundation/easycla/cla-backend-go/signatures" + "github.com/linuxfoundation/easycla/cla-backend-go/telemetry" v2Signatures "github.com/linuxfoundation/easycla/cla-backend-go/v2/signatures" ini "github.com/linuxfoundation/easycla/cla-backend-go/init" @@ -146,8 +147,52 @@ type combinedRepo struct { projects_cla_groups.Repository } +const ( + envDDBAPILogging = "DDB_API_LOGGING" + envOtelDatadogAPILogging = "OTEL_DATADOG_API_LOGGING" +) + +// parseBoolish parses common "boolean-ish" env var values. +// Returns (value, ok). ok=false means "unknown/invalid". +func parseBoolish(v string) (bool, bool) { + s := strings.TrimSpace(strings.ToLower(v)) + switch s { + case "1", "true", "yes", "y", "on": + return true, true + case "0", "false", "no", "n", "off": + return false, true + default: + return false, false + } +} + +// enabledByEnvOrStage implements: +// - if env var set to true/1/yes -> enabled +// - if env var set to false/0/no -> disabled +// - if env var unset/empty -> defaultByStage[idx] +// - idx 0 = dev/default stage, index 1 = prod stage +func enabledByEnvOrStage(envVar, stage string, defaultByStage [2]bool) bool { + if raw, ok := os.LookupEnv(envVar); ok && strings.TrimSpace(raw) != "" { + if b, ok2 := parseBoolish(raw); ok2 { + return b + } + log.Warnf("LG:api-log-flag-invalid:%s value=%q (falling back to STAGE default)", envVar, raw) + } + st := strings.TrimSpace(strings.ToLower(stage)) + if st == "prod" || st == "production" { + return defaultByStage[1] + } + // dev and all non-prod stages default to enabled + return defaultByStage[0] +} + // apiPathLoggerWithDB creates a middleware that logs API requests to DynamoDB func apiPathLoggerWithDB(apiLogsRepo api_logs.Repository) func(http.Handler) http.Handler { + // No-op when API logging is disabled. This prevents nil deref panics if the middleware + // remains in the handler chain but repo creation is skipped. + if apiLogsRepo == nil { + return func(next http.Handler) http.Handler { return next } + } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Infof("LG:api-request-path:%s", r.URL.Path) @@ -177,6 +222,8 @@ func apiPathLoggerWithDB(apiLogsRepo api_logs.Repository) func(http.Handler) htt } // server function called by environment specific server functions +// +//nolint:gocyclo func server(localMode bool) http.Handler { f := logrus.Fields{ "functionName": "cmd.server", @@ -208,6 +255,28 @@ func server(localMode bool) http.Handler { stage := viper.GetString("STAGE") dynamodbRegion := ini.GetProperty("DYNAMODB_AWS_REGION") + ddbAPILoggingEnabled := enabledByEnvOrStage(envDDBAPILogging, stage, [2]bool{true, false}) + otelDatadogEnabled := enabledByEnvOrStage(envOtelDatadogAPILogging, stage, [2]bool{true, true}) + + // Initialize OTel SDK -> Datadog Lambda Extension (OTLP) once at cold start. + // If init fails, disable OTel logging but never fail startup. + if otelDatadogEnabled { + version := strings.TrimSpace(Commit) + if strings.TrimSpace(version) == "" { + version = Version + } + if strings.TrimSpace(version) == "" { + version = "unknown" + } + if er := telemetry.InitDatadogOTel(telemetry.DatadogOTelConfig{ + Stage: stage, + Service: "easycla-backend", + Version: version, + }); er != nil { + log.Infof("LG:otel-datadog-disabled err=%v", er) + otelDatadogEnabled = false + } + } log.WithFields(f).Infof("Service %s starting...", ini.ServiceName) @@ -221,6 +290,8 @@ func server(localMode bool) http.Handler { log.Infof("Golang OS : %s", runtime.GOOS) log.Infof("Golang Arch : %s", runtime.GOARCH) log.Infof("DYANAMODB_AWS_REGION : %s", dynamodbRegion) + log.Infof("DDB_API_LOGGING : %t", ddbAPILoggingEnabled) + log.Infof("OTEL_DATADOG_API_LOGGING: %t", otelDatadogEnabled) log.Infof("GH_ORG_VALIDATION : %t", githubOrgValidation) log.Infof("COMPANY_USER_VALIDATION : %t", companyUserValidation) log.Infof("STAGE : %s", stage) @@ -239,6 +310,8 @@ func server(localMode bool) http.Handler { f["companyUserValidation"] = companyUserValidation f["stage"] = stage f["serviceHost"] = host + f["ddbAPILogging"] = ddbAPILoggingEnabled + f["otelDatadog"] = otelDatadogEnabled log.WithFields(f).Info("config") } @@ -305,7 +378,14 @@ func server(localMode bool) http.Handler { approvalListRepo := approval_list.NewRepository(awsSession, stage) v1CompanyRepo := v1Company.NewRepository(awsSession, stage) eventsRepo := events.NewRepository(awsSession, stage) - apiLogsRepo := api_logs.NewRepository(stage, dynamodb.New(awsSession)) + + var apiLogsRepo api_logs.Repository + if ddbAPILoggingEnabled { + apiLogsRepo = api_logs.NewRepository(stage, dynamodb.New(awsSession)) + } else { + apiLogsRepo = nil + } + v1ProjectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage) v1CLAGroupRepo := repository.NewRepository(awsSession, stage, gitV1Repository, gerritRepo, v1ProjectClaGroupRepo) metricsRepo := metrics.NewRepository(awsSession, stage, configFile.APIGatewayURL, v1ProjectClaGroupRepo) @@ -497,6 +577,12 @@ func server(localMode bool) http.Handler { v2API.Serve(middlewareSetupfunc), v2SwaggerSpec.BasePath()), configFile.AllowedOrigins) } + + // OTel/Datadog (OTLP -> Datadog Lambda Extension) - enabled by flag + if otelDatadogEnabled { + apiHandler = telemetry.WrapHTTPHandler(apiHandler) + } + return apiHandler } diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod index 0c1646df8..6bd6be18c 100644 --- a/cla-backend-go/go.mod +++ b/cla-backend-go/go.mod @@ -29,7 +29,7 @@ require ( github.com/gofrs/uuid v4.0.0+incompatible github.com/golang/mock v1.6.0 github.com/google/go-github/v37 v37.0.0 - github.com/google/uuid v1.1.4 + github.com/google/uuid v1.6.0 github.com/gorilla/sessions v1.2.1 // indirect github.com/imroc/req v0.3.0 github.com/jessevdk/go-flags v1.4.0 @@ -37,7 +37,7 @@ require ( github.com/jmoiron/sqlx v1.2.0 github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f // indirect github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2 - github.com/kr/pretty v0.3.0 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/mapstructure v1.5.0 @@ -51,18 +51,18 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.12.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.11.1 github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a github.com/xanzy/go-gitlab v0.50.1 go.uber.org/ratelimit v0.1.0 - golang.org/x/crypto v0.7.0 // indirect + golang.org/x/crypto v0.47.0 // indirect golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect - golang.org/x/net v0.8.0 - golang.org/x/oauth2 v0.6.0 - golang.org/x/sync v0.2.0 - golang.org/x/sys v0.33.0 // indirect + golang.org/x/net v0.49.0 + golang.org/x/oauth2 v0.34.0 + golang.org/x/sync v0.19.0 + golang.org/x/sys v0.40.0 // indirect golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect ) require ( @@ -70,26 +70,36 @@ require ( github.com/bradleyfalzon/ghinstallation/v2 v2.2.0 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang-jwt/jwt/v4 v4.5.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 + go.opentelemetry.io/otel v1.40.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 + go.opentelemetry.io/otel/sdk v1.40.0 ) require ( github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/aws/smithy-go v1.20.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.3.2 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-playground/locales v0.13.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-github/v50 v50.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/gorilla/securecookie v1.1.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.6.8 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -110,7 +120,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.6.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -118,8 +128,15 @@ require ( github.com/subosito/gotenv v1.4.1 // indirect github.com/ugorji/go/codec v1.2.6 // indirect go.mongodb.org/mongo-driver v1.10.1 // indirect - golang.org/x/text v0.9.0 // indirect - google.golang.org/appengine v1.6.7 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect + golang.org/x/text v0.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/grpc v1.78.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum index 5d80d5f1d..31aeada23 100644 --- a/cla-backend-go/go.sum +++ b/cla-backend-go/go.sum @@ -83,7 +83,11 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2 github.com/bradleyfalzon/ghinstallation/v2 v2.2.0 h1:AVvVU33rE8wdTS1aNnenwpigEBA9mvzI5OhjhZfH/LU= github.com/bradleyfalzon/ghinstallation/v2 v2.2.0/go.mod h1:xo3iIfK0lDKECe0s19nbxT0KKvk7LsrGc4NxR5ckKMA= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -113,6 +117,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -129,6 +135,11 @@ github.com/go-chi/chi v0.0.0-20180202194135-e223a795a06a/go.mod h1:eB3wogJHnLi3x github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -306,9 +317,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -321,10 +331,10 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v37 v37.0.0 h1:rCspN8/6kB1BAJWZfuafvHhyfIo5fkAulaP/3bOQ/tM= github.com/google/go-github/v37 v37.0.0/go.mod h1:LM7in3NmXDrX58GbEHy7FtNLbI2JijX93RnMKvWG3m4= github.com/google/go-github/v50 v50.1.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA= @@ -353,8 +363,8 @@ github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4Mgqvf github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0= -github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -366,6 +376,8 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= @@ -427,8 +439,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -479,7 +491,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mozillazg/request v0.8.0 h1:TbXeQUdBWr1J1df5Z+lQczDFzX9JD71kTCl7Zu/9rNM= github.com/mozillazg/request v0.8.0/go.mod h1:weoQ/mVFNbWgRBtivCGF1tUT9lwneFesues+CleXMWc= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= @@ -499,6 +510,7 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -510,8 +522,9 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -560,8 +573,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= @@ -613,8 +626,30 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw= go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -634,8 +669,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -720,8 +756,9 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -732,8 +769,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -748,8 +785,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -807,8 +844,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -824,8 +861,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -894,6 +931,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -920,7 +959,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -958,6 +996,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -974,6 +1016,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -984,14 +1028,13 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= diff --git a/cla-backend-go/package.json b/cla-backend-go/package.json index 950c0d389..8c3e915bf 100644 --- a/cla-backend-go/package.json +++ b/cla-backend-go/package.json @@ -30,7 +30,7 @@ "yarn-audit-fix": "^9.3.10" }, "resolutions": { - "axios": "^0.28.0", + "axios": "^0.30.3", "ansi-regex": "^5.0.1", "aws-sdk": "^2.1329.0", "cookiejar": "^2.1.4", @@ -42,11 +42,11 @@ "json-schema": "^0.4.0", "minimist": "^1.2.6", "normalize-url": "^4.5.1", - "qs": "^6.11.0", + "qs": "^6.14.2", "set-value": "^4.0.1", "simple-git": "^3.16.0", "ws": ">=7.5.10", "xmlhttprequest-ssl": "^1.6.2", - "fast-xml-parser": ">=4.4.1" + "fast-xml-parser": "^5.3.6" } -} \ No newline at end of file +} diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml index 76bc17f37..5d4ede270 100644 --- a/cla-backend-go/serverless.yml +++ b/cla-backend-go/serverless.yml @@ -32,6 +32,16 @@ custom: dynamodb: # Region where dynamodb is installed region: us-east-1 + + # Datadog tagging conventions + placeholders (CloudOps ticket LFIT-7051 will provide real values) + datadog: + dd_env: + dev: dev + staging: staging + prod: prod + site: ${file(./env.json):dd-site-${opt:stage}, ssm:/cla-dd-site-${opt:stage}} + apiKeySecretArn: ${file(./env.json):dd-api-key-secret-arn-${opt:stage}, ssm:/cla-dd-api-key-secret-arn-${opt:stage}} + extensionLayerArn: ${file(./env.json):dd-extension-layer-arn-${opt:stage}, ssm:/cla-dd-extension-layer-arn-${opt:stage}} # Config for serverless-prune-plugin - remove all but the 10 most recent # versions to avoid the "Code storage limit exceeded" error prune: @@ -44,7 +54,7 @@ custom: provider: name: aws - runtime: go1.x + runtime: provided.al2 stage: ${opt:stage} # EasyCLA v2 is deployed in us-east-2 to support Platform API GW and ACS region: us-east-2 @@ -65,7 +75,8 @@ provider: statements: - Effect: Allow Action: - - cloudwatch:* + # - cloudwatch:* + - cloudwatch:PutMetricData Resource: "*" - Effect: Allow Action: @@ -274,6 +285,19 @@ provider: # Turn on USER_AUTH_TRACING to see additional debug of user scopes for the authenticated users - output is verbose USER_AUTH_TRACING: true + DDB_API_LOGGING: ${file(./env.json):ddb-api-logging-${opt:stage}, ssm:/cla-ddb-api-logging-${opt:stage}} + OTEL_DATADOG_API_LOGGING: ${file(./env.json):otel-datadog-api-logging-${opt:stage}, ssm:/cla-otel-datadog-api-logging-${opt:stage}} + + # Datadog Lambda Extension (OTLP/HTTP) - Go backend uses pure OTel SDK -> DD Extension. + DD_ENV: ${self:custom.datadog.dd_env.${opt:stage}, self:custom.datadog.dd_env.dev} + DD_SERVICE: easycla-backend + # DD_SITE: ${self:custom.datadog.site} + # DD_API_KEY_SECRET_ARN: ${self:custom.datadog.apiKeySecretArn} + DD_SITE: ${file(./env.json):dd-site-${opt:stage}, ssm:/cla-dd-site-${opt:stage}} + DD_API_KEY_SECRET_ARN: ${file(./env.json):dd-api-key-secret-arn-${opt:stage}, ssm:/cla-dd-api-key-secret-arn-${opt:stage}} + DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT: localhost:4318 + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: http://localhost:4318/v1/traces + stackTags: Name: ${self:service} stage: ${self:provider.stage} @@ -315,10 +339,30 @@ functions: # so, setting this allows us to always know the name of the lambda (hard-coded now, which ignores the stage identifier in the name) name: ${self:service}-api-v4-lambda # must match lfx-api-gw lambda name description: "EasyCLA v2 API" - runtime: go1.x + runtime: provided.al2 handler: 'bin/backend-aws-lambda' package: individually: true patterns: - '!**' - 'bin/backend-aws-lambda' + - 'bootstrap' + layers: + - ${self:custom.datadog.extensionLayerArn} + +resources: + Resources: + DatadogApiKeySecretReadPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyName: ${self:service}-${opt:stage, 'dev'}-datadog-api-key-secret-read + Roles: + - Ref: IamRoleLambdaExecution + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - secretsmanager:GetSecretValue + Resource: + - ${self:custom.datadog.apiKeySecretArn} diff --git a/cla-backend-go/telemetry/datadog_otlp.go b/cla-backend-go/telemetry/datadog_otlp.go new file mode 100644 index 000000000..a1b37127a --- /dev/null +++ b/cla-backend-go/telemetry/datadog_otlp.go @@ -0,0 +1,381 @@ +// Copyright The Linux Foundation and each contributor to CommunityBridge. +// SPDX-License-Identifier: MIT + +package telemetry + +import ( + "context" + "fmt" + "net/http" + "net/url" + "os" + "regexp" + "strings" + "sync" + "time" + + log "github.com/linuxfoundation/easycla/cla-backend-go/logging" + + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/trace" +) + +// DatadogOTelConfig configures OTel SDK for exporting traces to the Datadog Lambda Extension. +type DatadogOTelConfig struct { + Stage string + Service string + Version string +} + +var ( + ddInitOnce sync.Once + ddInitErr error + ddExportSuccessOnce sync.Once +) + +// ddLoggingExporter logs once when spans are successfully exported (i.e. accepted by OTLP endpoint). +// This is intentionally low-volume to avoid flooding logs in prod. +type ddLoggingExporter struct { + inner sdktrace.SpanExporter +} + +func (e ddLoggingExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { + err := e.inner.ExportSpans(ctx, spans) + if err == nil { + ddExportSuccessOnce.Do(func() { + log.Infof("LG:otel-datadog-export-success spans=%d", len(spans)) + }) + } + return err +} + +func (e ddLoggingExporter) Shutdown(ctx context.Context) error { + return e.inner.Shutdown(ctx) +} + +// InitDatadogOTel initializes the global OTel SDK (tracer provider + OTLP exporter). +// Safe to call multiple times (sync.Once). Never panics. +func InitDatadogOTel(cfg DatadogOTelConfig) error { + ddInitOnce.Do(func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Tags (prefer explicit DD_* env vars; fallback to stage/config). + ddEnv := strings.TrimSpace(os.Getenv("DD_ENV")) + if ddEnv == "" { + ddEnv = stageToDDEnv(cfg.Stage) + } + + ddService := strings.TrimSpace(os.Getenv("DD_SERVICE")) + if ddService == "" { + ddService = cfg.Service + } + + // Force "service.version" (Datadog version) to be the build commit when available. + // Env var DD_VERSION may exist, but commit should take precedence for consistency across Go+Python. + ddVersion := strings.TrimSpace(cfg.Version) + if ddVersion == "" { + ddVersion = strings.TrimSpace(os.Getenv("DD_VERSION")) + } + if ddVersion == "" { + ddVersion = "1.0" + } + + exporter, err := newOTLPHTTPExporter(ctx) + if err != nil { + ddInitErr = err + return + } + exporter = ddLoggingExporter{inner: exporter} + + // Vendor-neutral resource attributes (Datadog maps these automatically). + res, err := resource.New(ctx, + resource.WithFromEnv(), + resource.WithTelemetrySDK(), + resource.WithAttributes( + attribute.String("service.name", ddService), + attribute.String("service.version", ddVersion), + attribute.String("deployment.environment.name", ddEnv), + ), + ) + if err != nil { + ddInitErr = err + return + } + + tp := sdktrace.NewTracerProvider( + sdktrace.WithResource(res), + // Batch exporter => async export (no per-request network IO). + sdktrace.WithBatcher(exporter), + ) + + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator( + propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ), + ) + }) + + if ddInitErr != nil { + log.Infof("LG:otel-datadog-init-failed err=%v", ddInitErr) + } + return ddInitErr +} + +// WrapHTTPHandler instruments inbound HTTP requests using otelhttp and produces spans. +func WrapHTTPHandler(next http.Handler) http.Handler { + // Regexes mirror ./utils/count_apis.sh so OTel span names group the same way as the offline API log rollups: + // - collapse multiple slashes + // - trim trailing slash + // - mask common asset extensions -> ".{asset}" + // - normalize Swagger assets "/vN/swagger.{asset}" -> "/vN/swagger" (keep version; do NOT map to /v*) + // - mask UUIDs, numeric IDs, Salesforce IDs, LFX IDs, and literal "null" segments + reMultiSlash := regexp.MustCompile(`/{2,}`) + reAssetExt := regexp.MustCompile(`\.(png|svg|css|js|json|xml|htm|html)$`) + reSwaggerAsset := regexp.MustCompile(`^(/v[0-9]+)/swagger\.\{asset\}$`) + // UUIDs: classify valid vs invalid (E2E often probes invalid IDs) + reUUIDValid := regexp.MustCompile(`[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`) + reUUIDLike := regexp.MustCompile(`/[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}(/|$)`) + reUUIDHexDash36 := regexp.MustCompile(`/[0-9a-fA-F-]{36}(/|$)`) + reNumericID := regexp.MustCompile(`/[0-9]+(/|$)`) + reSFIDValid := regexp.MustCompile(`/(?:00|a0)[A-Za-z0-9]{13,16}(/|$)`) + reSFIDLike := regexp.MustCompile(`/(?:00|a0)[^/]{1,32}(/|$)`) + reLFXIDValid := regexp.MustCompile(`/lf[A-Za-z0-9]{16,22}(/|$)`) + reLFXIDLike := regexp.MustCompile(`/lf[^/]{1,32}(/|$)`) + reNull := regexp.MustCompile(`/null(/|$)`) + reInvalidUUIDSeg := regexp.MustCompile(`/(?:invalid-uuid(?:-format)?|not-a-uuid)(/|$)`) + reInvalidSFIDSeg := regexp.MustCompile(`/invalid-sfid(?:-format)?(/|$)`) + + boolishTrue := func(v string) bool { + switch strings.ToLower(strings.TrimSpace(v)) { + case "1", "true", "yes", "y", "on": + return true + default: + return false + } + } + + sanitize := func(path string) string { + p := strings.TrimSpace(path) + if p == "" { + return "/" + } + if !strings.HasPrefix(p, "/") { + p = "/" + p + } + + p = reMultiSlash.ReplaceAllString(p, "/") + if len(p) > 1 && strings.HasSuffix(p, "/") { + p = strings.TrimSuffix(p, "/") + } + + // Asset extensions (including swagger.json/xml/html) -> ".{asset}" + p = reAssetExt.ReplaceAllString(p, ".{asset}") + + // Keep the version (/v1, /v2, ...) but normalize swagger asset paths. + if m := reSwaggerAsset.FindStringSubmatch(p); m != nil { + p = m[1] + "/swagger" + } + + // Dynamic segment masking (use template placeholders, not "*") + // UUIDs: valid vs invalid + p = reUUIDValid.ReplaceAllString(p, "{uuid}") + p = reUUIDLike.ReplaceAllString(p, "/{invalid-uuid}$1") + p = reUUIDHexDash36.ReplaceAllString(p, "/{invalid-uuid}$1") + p = reNumericID.ReplaceAllString(p, "/{id}$1") + // Salesforce IDs: valid vs invalid + p = reSFIDValid.ReplaceAllString(p, "/{sfid}$1") + p = reSFIDLike.ReplaceAllString(p, "/{invalid-sfid}$1") + // LFX IDs: valid vs invalid + p = reLFXIDValid.ReplaceAllString(p, "/{lfxid}$1") + p = reLFXIDLike.ReplaceAllString(p, "/{invalid-lfxid}$1") + p = reNull.ReplaceAllString(p, "/{null}$1") + // Known "invalid" test tokens (Cypress) -> placeholders + p = reInvalidUUIDSeg.ReplaceAllString(p, "/{invalid-uuid}$1") + p = reInvalidSFIDSeg.ReplaceAllString(p, "/{invalid-sfid}$1") + + if p == "" { + return "/" + } + return p + } + + // We want: + // - grouping by templated route => span name "METHOD /vN/thing/{uuid}" and attribute http.route="/vN/thing/{uuid}" + // - raw/original path visible per span => url.path="/vN/thing/" and http.target="/vN/thing/?..." + // + // otelhttp doesn't know framework routes, so we set http.route ourselves after the span is started. + inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rawPath := "/" + rawTarget := "/" + + if r != nil && r.URL != nil { + // Prefer URL.Path if present + if strings.TrimSpace(r.URL.Path) != "" { + rawPath = r.URL.Path + } + + // Default target to path; RequestURI includes query string when present. + rawTarget = rawPath + if strings.TrimSpace(r.URL.RequestURI()) != "" { + rawTarget = r.URL.RequestURI() + } + } + + route := sanitize(rawPath) + log.Debugf("Sanitized path: %q -> %q", rawPath, route) + + span := trace.SpanFromContext(r.Context()) + span.SetAttributes( + attribute.String("http.route", route), + attribute.String("url.path", rawPath), + attribute.String("http.target", rawTarget), + ) + + // Optional E2E marker (lets us filter CI noise in Datadog). + e2eVal := "" + if r != nil { + e2eVal = r.Header.Get("X-EasyCLA-E2E") + if strings.TrimSpace(e2eVal) == "" { + e2eVal = r.Header.Get("X-E2E-TEST") + } + } + if boolishTrue(e2eVal) { + runID := "" + if r != nil { + runID = strings.TrimSpace(r.Header.Get("X-EasyCLA-E2E-RunID")) + } + if runID != "" { + span.SetAttributes( + attribute.Bool("easycla.e2e", true), + attribute.String("easycla.e2e_run_id", runID), + ) + } else { + span.SetAttributes(attribute.Bool("easycla.e2e", true)) + log.Debugf("Sanitized path: %q -> %q e2e=1", rawPath, route) + } + } + + next.ServeHTTP(w, r) + }) + + return otelhttp.NewHandler( + inner, + "easycla-http", + otelhttp.WithSpanNameFormatter(func(_ string, r *http.Request) string { + path := "/" + if r != nil && r.URL != nil && strings.TrimSpace(r.URL.Path) != "" { + path = r.URL.Path + } + return fmt.Sprintf("%s %s", r.Method, sanitize(path)) + }), + ) +} + +func newOTLPHTTPExporter(ctx context.Context) (sdktrace.SpanExporter, error) { + // Standard overrides; default to Datadog Lambda Extension OTLP/HTTP. + // + // OTLP/HTTP env var rules: + // - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is per-signal. If set, preserve its path verbatim + // (default to "/" if no path). + // - OTEL_EXPORTER_OTLP_ENDPOINT is a base endpoint. If set (and per-signal is not), + // append "/v1/traces" (handling trailing slashes). + tracesEndpoint := strings.TrimSpace(os.Getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")) + baseEndpoint := strings.TrimSpace(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")) + + var ( + endpoint string + usedTracesEndpoint bool + usedBaseEndpoint bool + ) + + if tracesEndpoint != "" { + endpoint = tracesEndpoint + usedTracesEndpoint = true + } else if baseEndpoint != "" { + endpoint = baseEndpoint + usedBaseEndpoint = true + } else { + // Datadog Lambda Extension default (OTLP/HTTP). + endpoint = "http://localhost:4318/v1/traces" + // Default is already the full traces endpoint => treat like per-signal. + usedTracesEndpoint = true + } + + var host string + parsedPath := "" + insecure := true + + // Accept full URL or host:port[/path] + if strings.HasPrefix(endpoint, "http://") || strings.HasPrefix(endpoint, "https://") { + u, err := url.Parse(endpoint) + if err != nil { + return nil, err + } + host = u.Host + parsedPath = u.Path + insecure = (u.Scheme == "http") + } else { + host = endpoint + if strings.Contains(endpoint, "/") { + parts := strings.SplitN(endpoint, "/", 2) + host = parts[0] + // Preserve remainder as path (empty remainder => "/") + parsedPath = "/" + parts[1] + } + } + + // Normalize empty/missing paths to "/" (URL semantics) + if strings.TrimSpace(parsedPath) == "" { + parsedPath = "/" + } else if !strings.HasPrefix(parsedPath, "/") { + // Defensive (shouldn't happen with url.Parse) + parsedPath = "/" + parsedPath + } + + path := parsedPath + if usedBaseEndpoint { + // Base endpoint: append OTLP/HTTP traces path, handling trailing slashes. + base := strings.TrimRight(parsedPath, "/") + path = base + "/v1/traces" + } else if usedTracesEndpoint { + // Per-signal endpoint: preserve path verbatim (already normalized above) + path = parsedPath + } + + if strings.TrimSpace(host) == "" { + return nil, fmt.Errorf("invalid OTLP endpoint: %q", endpoint) + } + + opts := []otlptracehttp.Option{ + otlptracehttp.WithEndpoint(host), + otlptracehttp.WithURLPath(path), + otlptracehttp.WithTimeout(2 * time.Second), + } + if insecure { + opts = append(opts, otlptracehttp.WithInsecure()) + } + + return otlptracehttp.New(ctx, opts...) +} + +func stageToDDEnv(stage string) string { + const prod = "prod" + const staging = "staging" + switch strings.ToLower(strings.TrimSpace(stage)) { + case prod: + return prod + case staging: + return staging + default: + return "dev" + } +} diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock index 6702703d9..e63f8510b 100644 --- a/cla-backend-go/yarn.lock +++ b/cla-backend-go/yarn.lock @@ -1873,13 +1873,13 @@ aws-sdk@^2.1329.0, aws-sdk@^2.1404.0: uuid "8.0.0" xml2js "0.6.2" -axios@^0.28.0, axios@^1.6.2: - version "0.28.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.28.1.tgz#2a7bcd34a3837b71ee1a5ca3762214b86b703e70" - integrity sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ== +axios@^0.30.3, axios@^1.6.2: + version "0.30.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.30.3.tgz#ab1be887a2d37dd9ebc219657704180faf2c4920" + integrity sha512-5/tmEb6TmE/ax3mdXBc/Mi6YdPGxQsv+0p5YlciXWt3PHIn0VamqCXhRMtScnwY3lbgSXLneOuXAKUhgmSRpwg== dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" + follow-redirects "^1.15.4" + form-data "^4.0.4" proxy-from-env "^1.1.0" balanced-match@^1.0.0: @@ -2825,12 +2825,18 @@ fast-uri@^3.0.1: resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== -fast-xml-parser@4.4.1, fast-xml-parser@>=4.4.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz#a7e665ff79b7919100a5202f23984b6150f9b31e" - integrity sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w== +fast-xml-builder@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz#a485d7e8381f1db983cf006f849d1066e2935241" + integrity sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ== + +fast-xml-parser@4.4.1, fast-xml-parser@^5.3.6: + version "5.4.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.4.0.tgz#6fdd30cc233d2027832db517096e02a12c24e1db" + integrity sha512-ChpjDjfiMs4kETpVpXZHM/GM7smQbQ8T6q4jHD+M32tIH2jyz8gtCiByXn0WNUnQ6XUbVxYSe05FP3A52cEQyQ== dependencies: - strnum "^1.0.5" + fast-xml-builder "^1.0.0" + strnum "^2.1.2" fastest-levenshtein@^1.0.16: version "1.0.16" @@ -2931,7 +2937,7 @@ folder-hash@^3.3.0: graceful-fs "~4.2.0" minimatch "~3.0.4" -follow-redirects@^1.14.7, follow-redirects@^1.15.0: +follow-redirects@^1.14.7, follow-redirects@^1.15.4: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== @@ -2952,6 +2958,17 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + formidable@^2.0.1: version "2.1.2" resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" @@ -4364,10 +4381,10 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -qs@^6.10.3, qs@^6.11.0: - version "6.14.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" - integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== +qs@^6.10.3, qs@^6.11.0, qs@^6.14.2: + version "6.15.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.0.tgz#db8fd5d1b1d2d6b5b33adaf87429805f1909e7b3" + integrity sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ== dependencies: side-channel "^1.1.0" @@ -4967,10 +4984,10 @@ strip-outer@^1.0.1: dependencies: escape-string-regexp "^1.0.2" -strnum@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" - integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== +strnum@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.2.tgz#a5e00ba66ab25f9cafa3726b567ce7a49170937a" + integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ== strtok3@^6.2.4: version "6.3.0" diff --git a/cla-backend/bootstrap b/cla-backend/bootstrap new file mode 100755 index 000000000..73e636bcf --- /dev/null +++ b/cla-backend/bootstrap @@ -0,0 +1,21 @@ +#!/bin/sh +set -eu + +# AWS Lambda custom runtime entrypoint (provided.al2 / provided.al2023). +# AWS executes /var/task/bootstrap. The configured Lambda "Handler" is exposed +# via the $_HANDLER env var. We exec that handler binary (which is our Go +# executable) so we don't need to rename every binary to 'bootstrap'. + +if [ -z "${_HANDLER:-}" ]; then + echo "bootstrap: _HANDLER is not set" >&2 + exit 1 +fi + +HANDLER_PATH="/var/task/${_HANDLER}" +if [ ! -x "${HANDLER_PATH}" ]; then + echo "bootstrap: handler '${HANDLER_PATH}' not found or not executable" >&2 + ls -la /var/task >&2 || true + exit 1 +fi + +exec "${HANDLER_PATH}" diff --git a/cla-backend/cla/routes.py b/cla-backend/cla/routes.py index f3e4a2386..624f7a50c 100755 --- a/cla-backend/cla/routes.py +++ b/cla-backend/cla/routes.py @@ -6,6 +6,10 @@ """ import hug +import os +import re +import time +from urllib.parse import urlparse import requests from falcon import HTTP_401, HTTP_400, HTTP_OK, HTTP_500, Response from hug.middleware import LogMiddleware @@ -40,6 +44,475 @@ _APILOG_CLS = None _APILOG_IMPORT_ERROR = None +_FEATURE_FLAG_CACHE = {} + + +# --- OTel/Datadog (OTLP/HTTP -> Datadog Lambda Extension) state --- +_OTEL_TRACER = None +_OTEL_TRACER_PROVIDER = None +_OTEL_INIT_ERROR = None +_OTEL_DISABLED = False +_OTEL_DISABLED_REASON = None +_OTEL_EXPORT_SUCCESS_ONCE = False + +def _disable_otel(reason): + global _OTEL_DISABLED, _OTEL_DISABLED_REASON + if _OTEL_DISABLED: + return + _OTEL_DISABLED = True + _OTEL_DISABLED_REASON = reason + try: + cla.log.info(f"LG:otel-datadog-disabled reason={reason}") + except Exception: + pass + +# --- Path sanitizer regexes (mirror ./utils/count_apis.sh, but keep /vN versions intact) --- +_RE_MULTI_SLASH = re.compile(r"/{2,}") +_RE_ASSET_EXT = re.compile(r"\.(png|svg|css|js|json|xml|htm|html)$") +_RE_SWAGGER_ASSET = re.compile(r"^(/v[0-9]+)/swagger\.\{asset\}$") +_RE_UUID_VALID = re.compile(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") +_RE_UUID_LIKE = re.compile(r"/[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}(/|$)") +_RE_UUID_HEXDASH_36 = re.compile(r"/[0-9a-fA-F-]{36}(/|$)") +_RE_NUMERIC_ID = re.compile(r"/[0-9]+(/|$)") +_RE_SFID_VALID = re.compile(r"/(?:00|a0)[A-Za-z0-9]{13,16}(/|$)") +_RE_SFID_LIKE = re.compile(r"/(?:00|a0)[^/]{1,32}(/|$)") +_RE_LFXID_VALID = re.compile(r"/lf[A-Za-z0-9]{16,22}(/|$)") +_RE_LFXID_LIKE = re.compile(r"/lf[^/]{1,32}(/|$)") +_RE_NULL = re.compile(r"/null(/|$)") +_RE_GIT_SHA = re.compile(r"^[0-9a-fA-F]{7,40}$") +_RE_INVALID_UUID_SEG = re.compile(r"/(?:invalid-uuid(?:-format)?|not-a-uuid)(/|$)") +_RE_INVALID_SFID_SEG = re.compile(r"/invalid-sfid(?:-format)?(/|$)") + +def _sanitize_api_path(path: str) -> str: + """ + Low-cardinality path template matching ./utils/count_apis.sh behavior, + except we DO NOT collapse /v1,/v2,... into /v* (version is preserved). + """ + p = (path or "").strip() + if p == "": + return "/" + if not p.startswith("/"): + p = "/" + p + + p = _RE_MULTI_SLASH.sub("/", p) + if len(p) > 1 and p.endswith("/"): + p = p[:-1] + + # Assets -> ".{asset}" + p = _RE_ASSET_EXT.sub(".{asset}", p) + + # /vN/swagger.{asset} -> /vN/swagger (keep version) + p = _RE_SWAGGER_ASSET.sub(r"\1/swagger", p) + + # Dynamic IDs -> placeholders + p = _RE_UUID_VALID.sub("{uuid}", p) + p = _RE_UUID_LIKE.sub(r"/{invalid-uuid}\1", p) + p = _RE_UUID_HEXDASH_36.sub(r"/{invalid-uuid}\1", p) + p = _RE_NUMERIC_ID.sub(r"/{id}\1", p) + p = _RE_SFID_VALID.sub(r"/{sfid}\1", p) + p = _RE_SFID_LIKE.sub(r"/{invalid-sfid}\1", p) + p = _RE_LFXID_VALID.sub(r"/{lfxid}\1", p) + p = _RE_LFXID_LIKE.sub(r"/{invalid-lfxid}\1", p) + p = _RE_NULL.sub(r"/{null}\1", p) + # Known "invalid" test tokens (Cypress) -> placeholders + p = _RE_INVALID_UUID_SEG.sub(r"/{invalid-uuid}\1", p) + p = _RE_INVALID_SFID_SEG.sub(r"/{invalid-sfid}\1", p) + + cla.log.debug(f"Sanitized path: {path!r} -> {p!r}") + return p or "/" + +def _stage_to_dd_env(stage: str) -> str: + st = (stage or "dev").strip().lower() + if st == "prod": + return "prod" + if st == "staging": + return "staging" + return "dev" + +def _normalize_git_sha(v: str) -> str: + s = (v or "").strip() + if not s: + return "" + # keep consistent with Go which uses short commit (e.g. fd573003d) + if _RE_GIT_SHA.match(s) and len(s) > 9: + return s[:9] + return s + +def _detect_git_commit(): + """ + Best-effort commit detection: + 1) common CI env vars + 2) local git (dev) + Returns short sha (9 chars) when possible (to match Go's Commit). + """ + for k in ( + "GIT_COMMIT_SHA", "GIT_COMMIT", "COMMIT_SHA", "COMMIT", + "GITHUB_SHA", "CI_COMMIT_SHA", "CODEBUILD_RESOLVED_SOURCE_VERSION", + ): + v = os.getenv(k) + if v and v.strip(): + n = _normalize_git_sha(v) + if n: + return n + try: + import subprocess + out = subprocess.check_output( + ["git", "rev-parse", "--short=9", "HEAD"], + stderr=subprocess.DEVNULL, + ) + sha = _normalize_git_sha(out.decode("utf-8").strip()) + return sha or None + except Exception: + return None + +def _build_otlp_traces_endpoint() -> str: + """ + Match the Go exporter selection logic: + - prefer OTEL_EXPORTER_OTLP_TRACES_ENDPOINT if set (preserve its path verbatim; default "/" if missing) + - else use OTEL_EXPORTER_OTLP_ENDPOINT as base and append "/v1/traces" (handling trailing slashes) + - else default to "http://localhost:4318/v1/traces" + + Accept full URL or host:port[/path]. + Returns a full URL including scheme + path (suitable for OTLPSpanExporter(endpoint=...)). + """ + traces_ep = (os.getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") or "").strip() + base_ep = (os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT") or "").strip() + + used_base = False + if traces_ep: + raw = traces_ep + elif base_ep: + raw = base_ep + used_base = True + else: + raw = "http://localhost:4318/v1/traces" + + scheme = "http" + host = "" + path = "/" + + if raw.startswith("http://") or raw.startswith("https://"): + u = urlparse(raw) + scheme = (u.scheme or "http") + host = u.netloc + path = u.path or "/" + else: + # host:port[/path] (default scheme http) + if "/" in raw: + host, rest = raw.split("/", 1) + path = "/" + rest if rest else "/" + else: + host = raw + path = "/" + + if not path.startswith("/"): + path = "/" + path + + if used_base: + base_path = path.rstrip("/") + path = base_path + "/v1/traces" + + if not host or host.strip() == "": + raise ValueError(f"invalid OTLP endpoint: {raw!r}") + + return f"{scheme}://{host}{path}" + +def _init_otel_datadog() -> None: + """ + Initialize a minimal OTel SDK pipeline for exporting spans to the Datadog Lambda Extension via OTLP/HTTP. + Never raises; on failure it caches the error and becomes a no-op. + """ + global _OTEL_TRACER, _OTEL_TRACER_PROVIDER, _OTEL_INIT_ERROR + + if _OTEL_DISABLED: + return + + if _OTEL_TRACER is not None or _OTEL_INIT_ERROR is not None: + return + try: + # Lazy import so we never fail module import / Lambda cold start if deps are missing. + from opentelemetry import trace as otel_trace + from opentelemetry.sdk.resources import Resource + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import SimpleSpanProcessor + from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter + except Exception as e: + _OTEL_INIT_ERROR = e + _disable_otel(f"init-import-1 err={e}") + return + + try: + stage = (os.getenv("STAGE", "dev") or "dev").strip() + dd_env = (os.getenv("DD_ENV") or "").strip() or _stage_to_dd_env(stage) + dd_service = (os.getenv("DD_SERVICE") or "").strip() or "easycla-backend" + # Force "service.version" (Datadog version) to be the current commit when available. + # Prefer DD_VERSION (injected by serverless/SSM) to avoid spawning a subprocess in Lambda. + dd_version = (os.getenv("DD_VERSION") or "").strip() + if not dd_version: + dd_version = _detect_git_commit() or (os.getenv("VERSION") or "").strip() or "1.0" + dd_version = _normalize_git_sha(dd_version) or "1.0" + + endpoint = _build_otlp_traces_endpoint() + + exporter = OTLPSpanExporter(endpoint=endpoint, timeout=2) + + # Wrap exporter so any export failure disables tracing for this container + from opentelemetry.sdk.trace.export import SpanExportResult + class _FailFastExporter: + def __init__(self, inner): + self._inner = inner + def export(self, spans): + if _OTEL_DISABLED: + return SpanExportResult.FAILURE + try: + res = self._inner.export(spans) + except Exception as ex: + _disable_otel(f"export err={ex}") + return SpanExportResult.FAILURE + if res != SpanExportResult.SUCCESS: + _disable_otel(f"export result={res}") + else: + global _OTEL_EXPORT_SUCCESS_ONCE + if not _OTEL_EXPORT_SUCCESS_ONCE: + _OTEL_EXPORT_SUCCESS_ONCE = True + try: + cla.log.info(f"LG:otel-datadog-export-success spans={len(spans)}") + except Exception: + pass + return res + def shutdown(self): + try: + return self._inner.shutdown() + except Exception as ex: + cla.log.info(f"LG:otel-datadog exception during shutdown err={ex}") + return + + resource = Resource.create({ + # Vendor-neutral resource attrs (Datadog maps these automatically). + "service.name": dd_service, + "service.version": dd_version, + "deployment.environment.name": dd_env, + }) + + provider = TracerProvider(resource=resource) + # In Lambda, synchronous export is safest; FailFastExporter prevents repeated latency on failure. + provider.add_span_processor(SimpleSpanProcessor(_FailFastExporter(exporter))) + + otel_trace.set_tracer_provider(provider) + + _OTEL_TRACER_PROVIDER = provider + _OTEL_TRACER = otel_trace.get_tracer("easycla-http") + except Exception as e: + _OTEL_INIT_ERROR = e + _disable_otel(f"init-import-2 err={e}") + +def _parse_http_status_code(status): + """ + Falcon typically stores response.status like "200 OK". + Return int status code or None. + """ + if status is None: + return None + try: + s = str(status).strip() + if s == "": + return None + # "200 OK" -> 200 + return int(s.split()[0]) + except Exception: + return None + + +_E2E_HEADER = "X-EasyCLA-E2E" +_E2E_RUNID_HEADER = "X-EasyCLA-E2E-RunID" +_E2E_LEGACY_HEADER = "X-E2E-TEST" + +def _extract_e2e_marker(headers): + """ + CI/E2E request marker so we can tag/filter test noise in logs and traces. + Returns (is_e2e, run_id). + """ + try: + if not headers: + return False, "" + raw = headers.get(_E2E_HEADER) or headers.get(_E2E_LEGACY_HEADER) + if _parse_boolish(raw) is True: + run_id = (headers.get(_E2E_RUNID_HEADER) or "").strip() + return True, run_id + except Exception: + pass + return False, "" + +def _otel_start_request_span(request) -> None: + """ + Start a SERVER span for the inbound request and store it in request.context. + Never raises. + """ + try: + req_ctx = getattr(request, "context", None) + if req_ctx is None: + return + # Defensive: don't double-start + if req_ctx.get("_otel_span") is not None: + return + except Exception as ex: + try: + cla.log.info(f"LG:api-log-otel-datadog-start-missing-context err={ex}") + except Exception: + pass + return + + try: + _init_otel_datadog() + if _OTEL_TRACER is None: + return + + from opentelemetry import context as otel_context + from opentelemetry.propagate import extract + from opentelemetry.trace import SpanKind, Status, StatusCode, set_span_in_context + except Exception as e: + try: + cla.log.info(f"LG:api-log-otel-datadog-init-missing err={e}") + except Exception: + pass + return + + method = (getattr(request, "method", "GET") or "GET").strip().upper() + raw_path = getattr(request, "path", "/") + route = _sanitize_api_path(raw_path) + span_name = f"{method} {route}" + + try: + # Extract W3C parent context from inbound headers (low cost). + headers = getattr(request, "headers", None) or {} + carrier = {} + for k in ("traceparent", "tracestate", "baggage"): + try: + v = headers.get(k) + except Exception: + v = None + if v: + carrier[k] = v + + parent_ctx = extract(carrier) + + span = _OTEL_TRACER.start_span(span_name, context=parent_ctx, kind=SpanKind.SERVER) + # Low-cardinality attrs + span.set_attribute("http.method", method) + span.set_attribute("http.route", route) + # High-cardinality raw path/target (lets you inspect actual UUIDs per span) + try: + raw_target = getattr(request, "relative_uri", None) or raw_path + except Exception: + raw_target = raw_path + span.set_attribute("url.path", raw_path) + span.set_attribute("http.target", raw_target) + + # Optional E2E marker (lets us filter CI noise in Datadog). + e2e, e2e_run_id = _extract_e2e_marker(headers) + if e2e: + span.set_attribute("easycla.e2e", True) + if e2e_run_id: + span.set_attribute("easycla.e2e_run_id", e2e_run_id) + + # Make span current for the remainder of the request (so any future child spans attach correctly). + ctx_with_span = set_span_in_context(span, parent_ctx) + token = otel_context.attach(ctx_with_span) + + # Persist for response middleware. + request.context["_otel_span"] = span + request.context["_otel_ctx_token"] = token + request.context["_otel_route"] = route + except Exception as e: + try: + cla.log.info(f"LG:api-log-otel-datadog-failed:{route} err={e}") + except Exception: + pass + +def _otel_end_request_span(request, response) -> None: + """ + End the SERVER span for the inbound request, set status code, and detach context. + Never raises. + """ + span = None + token = None + route = None + + try: + ctx = getattr(request, "context", None) + if ctx is None: + return + span = ctx.pop("_otel_span", None) + token = ctx.pop("_otel_ctx_token", None) + route = ctx.pop("_otel_route", None) + except Exception as ex: + # If request.context isn't mutable/dict-like for some reason, just bail. + try: + cla.log.info(f"LG:api-log-otel-datadog-end-missing-context err={ex}") + except Exception: + pass + return + + try: + if span is None: + return + + status_code = _parse_http_status_code(getattr(response, "status", None)) + if status_code is not None: + span.set_attribute("http.status_code", status_code) + # Mark 5xx as errors (4xx are usually client errors, not service faults) + if status_code >= 500: + from opentelemetry.trace import Status, StatusCode + span.set_status(Status(StatusCode.ERROR)) + + span.end() + except Exception as e: + try: + if route is None: + route = _sanitize_api_path(getattr(request, "path", "/")) + cla.log.info(f"LG:api-log-otel-datadog-failed:{route} err={e}") + except Exception: + pass + finally: + # Always detach if we attached. + if token is not None: + try: + from opentelemetry import context as otel_context + otel_context.detach(token) + except Exception: + pass + +def _parse_boolish(value): + if value is None: + return None + v = str(value).strip().lower() + if v in ("1", "true", "yes", "y", "on"): + return True + if v in ("0", "false", "no", "n", "off"): + return False + return None + +def _enabled_by_env_or_stage(env_var: str, default_by_stage: tuple[bool, bool]) -> bool: + # cache (env vars don't change during a lambda container lifetime) + if env_var in _FEATURE_FLAG_CACHE: + return _FEATURE_FLAG_CACHE[env_var] + + raw = os.getenv(env_var) + if raw is not None and raw.strip() != "": + parsed = _parse_boolish(raw) + if parsed is not None: + _FEATURE_FLAG_CACHE[env_var] = parsed + return parsed + try: + cla.log.info(f"LG:api-log-flag-invalid:{env_var} value={raw} (falling back to STAGE default)") + except Exception: + pass + + stage = (os.getenv("STAGE", "dev") or "dev").strip().lower() + is_prod = stage == "prod" + enabled = default_by_stage[1] if is_prod else default_by_stage[0] + _FEATURE_FLAG_CACHE[env_var] = enabled + return enabled def _get_apilog_cls(): """ @@ -75,14 +548,27 @@ def process_data_api_logs(request, response): for all requests. """ cla.log.info('LG:api-request-path:' + request.path) - - # Log API request to DynamoDB table - apilog_cls = _get_apilog_cls() - if apilog_cls is not None: - try: - apilog_cls.log_api_request(request.path) - except Exception as e: - cla.log.info(f"LG:api-log-dynamo-failed:{request.path} err={e}") + # Mark E2E calls in the log line so jq-based rollups can filter them out easily. + e2e, e2e_run_id = _extract_e2e_marker(getattr(request, "headers", None) or {}) + suffix = "" + if e2e: + suffix = " e2e=1" + if e2e_run_id: + suffix += f" e2e_run_id={e2e_run_id}" + cla.log.info('LG:e2e-request-path:' + request.path + suffix) + + # DynamoDB API logging (conditional) + if _enabled_by_env_or_stage("DDB_API_LOGGING", default_by_stage=(True, False)): + apilog_cls = _get_apilog_cls() + if apilog_cls is not None: + try: + apilog_cls.log_api_request(request.path) + except Exception as e: + cla.log.info(f"LG:api-log-dynamo-failed:{request.path} err={e}") + + # OTel/Datadog API logging (OTLP/HTTP -> Datadog Lambda Extension) + if _enabled_by_env_or_stage("OTEL_DATADOG_API_LOGGING", default_by_stage=(True, True)): + _otel_start_request_span(request) if "/github/activity" in request.path: body = request.bounded_stream.read() @@ -98,6 +584,10 @@ def process_data(request, response, resource): response.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") response.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization") + # Close the OTel span after handlers run (captures final status code). + if _enabled_by_env_or_stage("OTEL_DATADOG_API_LOGGING", default_by_stage=(True, True)): + _otel_end_request_span(request, response) + @hug.directive() def check_auth(request=None, **kwargs): @@ -108,11 +598,14 @@ def check_auth(request=None, **kwargs): @hug.exception(cla.auth.AuthError) -def handle_auth_error(exception, response=None, **kwargs): +def handle_auth_error(exception, request=None, response=None, **kwargs): """Handles authentication errors""" response.status = HTTP_401 - return exception.response + # Ensure OTel span closes even if response middleware isn't invoked for exceptions. + if _enabled_by_env_or_stage("OTEL_DATADOG_API_LOGGING", default_by_stage=(True, True)): + _otel_end_request_span(request, response) + return exception.response # # Health check route. diff --git a/cla-backend/package.json b/cla-backend/package.json index c7a468e20..dd0102c77 100644 --- a/cla-backend/package.json +++ b/cla-backend/package.json @@ -35,11 +35,11 @@ }, "dependencies": { "aws-sdk": "^2.1329.0", - "fast-xml-parser": ">=4.4.1", + "fast-xml-parser": "^5.3.6", "install": "^0.13.0", "node-fetch": "^2.6.7", "node.extend": "^2.0.2", - "qs": "^6.11.0", + "qs": "^6.14.2", "serverless": "^3.0.0", "serverless-domain-manager": "^7.0.4", "serverless-finch": "^4.0.3", @@ -56,7 +56,7 @@ "resolutions": { "ansi-regex": "^5.0.1", "aws-sdk": "^2.1329.0", - "axios": "^0.28.0", + "axios": "^0.30.3", "cookiejar": "^2.1.4", "file-type": "^16.5.4", "glob-parent": "^5.1.2", @@ -66,15 +66,15 @@ "json-schema": "^0.4.0", "lodash.set": "^4.3.2", "node-fetch": "^2.6.7", - "minimatch": "^6.1.6", + "minimatch": "^10.2.1", "minimist": "^1.2.6", "normalize-url": "^4.5.1", - "qs": "^6.11.0", + "qs": "^6.14.2", "set-value": "^4.0.1", "shell-quote": "^1.7.3", "simple-git": "^3.16.0", "ws": ">=7.5.10", "xmlhttprequest-ssl": "^1.6.2", - "fast-xml-parser": ">=4.4.1" + "fast-xml-parser": "^5.3.6" } } diff --git a/cla-backend/requirements.txt b/cla-backend/requirements.txt index baa8af478..0afe6b431 100644 --- a/cla-backend/requirements.txt +++ b/cla-backend/requirements.txt @@ -57,3 +57,7 @@ astroid==3.3.8 pluggy==1.5.0 gunicorn==22.0.0 PyNaCl==1.5.0 +# OpenTelemetry (OTLP/HTTP exporter) for Datadog Lambda Extension +opentelemetry-api<2,>=1 +opentelemetry-sdk<2,>=1 +opentelemetry-exporter-otlp-proto-http<2,>=1 diff --git a/cla-backend/serverless.yml b/cla-backend/serverless.yml index 68cc9bfd5..dfae5c39b 100644 --- a/cla-backend/serverless.yml +++ b/cla-backend/serverless.yml @@ -29,6 +29,14 @@ package: custom: allowed_origins: ${file(./env.json):cla-allowed-origins-${sls:stage}, ssm:/cla-allowed-origins-${sls:stage}} + datadog: + dd_env: + dev: dev + staging: staging + prod: prod + site: ${file(./env.json):dd-site-${sls:stage}, ssm:/cla-dd-site-${sls:stage}} + apiKeySecretArn: ${file(./env.json):dd-api-key-secret-arn-${sls:stage}, ssm:/cla-dd-api-key-secret-arn-${sls:stage}} + extensionLayerArn: ${file(./env.json):dd-extension-layer-arn-${sls:stage}, ssm:/cla-dd-extension-layer-arn-${sls:stage}} wsgi: app: cla.routes.__hug_wsgi__ pythonBin: python @@ -140,7 +148,8 @@ provider: statements: - Effect: Allow Action: - - cloudwatch:* + # - cloudwatch:* + - cloudwatch:PutMetricData Resource: "*" - Effect: Allow Action: @@ -226,78 +235,6 @@ provider: - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects-cla-groups" - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs" - - Effect: Allow - Action: - - dynamodb:Query - Resource: - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-api-log/index/bucket-dt-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-ccla-whitelist-requests/index/company-id-project-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-ccla-whitelist-requests/index/ccla-approval-list-request-project-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/github-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/github-username-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/gitlab-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/gitlab-username-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/github-user-external-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/lf-username-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/lf-email-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gerrit-instances/index/gerrit-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gerrit-instances/index/gerrit-project-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gerrit-instances/index/gerrit-project-sfid-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/project-signature-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/project-signature-date-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/reference-signature-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-project-reference-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-user-ccla-company-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/project-signature-external-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-company-signatory-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/reference-signature-search-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-project-id-type-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-company-initial-manager-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-project-id-sigtype-signed-approved-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-companies/index/external-company-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-companies/index/company-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-companies/index/company-signing-entity-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects/index/external-project-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects/index/project-name-search-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects/index/project-name-lower-search-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects/index/foundation-sfid-project-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/project-repository-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/repository-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/repository-organization-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/external-repository-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/sfdc-repository-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/project-sfid-repository-organization-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/project-sfid-repository-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/repository-type-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-github-orgs/index/github-org-sfid-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-github-orgs/index/project-sfid-organization-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-github-orgs/index/organization-name-lower-search-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-company-invites/index/requested-company-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-type-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/user-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-id-external-project-id-event-epoch-time-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-project-id-event-time-epoch-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-cla-group-id-event-time-epoch-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-date-and-contains-pii-event-time-epoch-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-sfid-foundation-sfid-event-time-epoch-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-sfid-project-id-event-time-epoch-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-id-event-type-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-foundation-sfid-event-time-epoch-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-company-sfid-event-data-lower-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-sfid-cla-group-id-event-time-epoch-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-metrics/index/metric-type-salesforce-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-cla-manager-requests/index/cla-manager-requests-company-project-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-cla-manager-requests/index/cla-manager-requests-external-company-project-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-cla-manager-requests/index/cla-manager-requests-project-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects-cla-groups/index/cla-group-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects-cla-groups/index/foundation-sfid-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-org-sfid-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-project-sfid-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-full-path-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-external-group-id-index" - - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-org-url-index" environment: STAGE: ${sls:stage} @@ -361,6 +298,22 @@ provider: # https://github.com/pypa/setuptools/issues/2232 SETUPTOOLS_USE_DISTUTILS: stdlib + # API usage logging toggles: + DDB_API_LOGGING: ${file(./env.json):ddb-api-logging-${sls:stage}, ssm:/cla-ddb-api-logging-${sls:stage}} + OTEL_DATADOG_API_LOGGING: ${file(./env.json):otel-datadog-api-logging-${sls:stage}, ssm:/cla-otel-datadog-api-logging-${sls:stage}} + + # Datadog/OTel placeholders (Python OTel is stubbed for now) + DD_ENV: ${self:custom.datadog.dd_env.${sls:stage}, self:custom.datadog.dd_env.dev} + DD_SERVICE: easycla-backend + # DD_VERSION: ${ssm:/cla-dd-version-${sls:stage}, env:DD_VERSION, '1.0'} + DD_VERSION: ${env:DD_VERSION, '1.0'} + # DD_SITE: ${self:custom.datadog.site} + # DD_API_KEY_SECRET_ARN: ${self:custom.datadog.apiKeySecretArn} + DD_SITE: ${file(./env.json):dd-site-${sls:stage}, ssm:/cla-dd-site-${sls:stage}} + DD_API_KEY_SECRET_ARN: ${file(./env.json):dd-api-key-secret-arn-${sls:stage}, ssm:/cla-dd-api-key-secret-arn-${sls:stage}} + DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT: localhost:4318 + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: http://localhost:4318/v1/traces + stackTags: Name: ${self:service} stage: ${sls:stage} @@ -400,16 +353,17 @@ functions: authorizer: handler: auth/bin/authorizer description: "EasyCLA API authorizer" - runtime: go1.x + runtime: provided.al2 package: individually: true patterns: - 'auth/bin/**' + - 'bootstrap' api-v3-lambda: name: ${self:service}-${sls:stage, 'dev'}-api-v3-lambda description: "EasyCLA Golang API handler for the /v3 endpoints" - runtime: go1.x + runtime: provided.al2 handler: 'bin/backend-aws-lambda' events: - http: @@ -420,71 +374,80 @@ functions: individually: true patterns: - 'bin/backend-aws-lambda' + - 'bootstrap' + layers: + - ${self:custom.datadog.extensionLayerArn} dynamo-projects-events-lambda: name: ${self:service}-${sls:stage, 'dev'}-dynamo-projects-lambda description: "EasyCLA DynamoDB stream events handler for the projects table" handler: 'bin/dynamo-events-lambda' - runtime: go1.x + runtime: provided.al2 package: individually: true patterns: - 'bin/dynamo-events-lambda' + - 'bootstrap' dynamo-signatures-events-lambda: handler: 'bin/dynamo-events-lambda' name: ${self:service}-${sls:stage, 'dev'}-dynamo-signatures-events-lambda description: "EasyCLA DynamoDB stream events handler for the signatures table" - runtime: go1.x + runtime: provided.al2 package: individually: true patterns: - 'bin/dynamo-events-lambda' + - 'bootstrap' dynamo-events-events-lambda: handler: 'bin/dynamo-events-lambda' name: ${self:service}-${sls:stage, 'dev'}-dynamo-events-events-lambda description: "EasyCLA DynamoDB stream events handler for the events table" - runtime: go1.x + runtime: provided.al2 package: individually: true patterns: - 'bin/dynamo-events-lambda' + - 'bootstrap' dynamo-repositories-events-lambda: handler: 'bin/dynamo-events-lambda' name: ${self:service}-${sls:stage, 'dev'}-dynamo-repositories-events-lambda description: "EasyCLA DynamoDB stream events handler for the repositories table" - runtime: go1.x + runtime: provided.al2 package: individually: true patterns: - 'bin/dynamo-events-lambda' + - 'bootstrap' dynamo-projects-cla-groups-events-lambda: handler: 'bin/dynamo-events-lambda' name: ${self:service}-${sls:stage, 'dev'}-dynamo-projects-cla-groups-events-lambda description: "EasyCLA DynamoDB stream events handler for the projects-cla-groups table" - runtime: go1.x + runtime: provided.al2 package: individually: true patterns: - 'bin/dynamo-events-lambda' + - 'bootstrap' dynamo-github-orgs-events-lambda: handler: 'bin/dynamo-events-lambda' name: ${self:service}-${sls:stage, 'dev'}-dynamo-github-orgs-events-lambda description: "EasyCLA DynamoDB stream events handler for cla--github-orgs the table" - runtime: go1.x + runtime: provided.al2 package: individually: true patterns: - 'bin/dynamo-events-lambda' + - 'bootstrap' save-metrics-lambda: name: ${self:service}-${sls:stage, 'dev'}-save-metrics-lambda description: "EasyCLA Save Metrics API handler" - runtime: go1.x + runtime: provided.al2 handler: 'bin/metrics-aws-lambda' timeout: 900 # maximum time allowed events: @@ -496,11 +459,12 @@ functions: individually: true patterns: - 'bin/metrics-aws-lambda' + - 'bootstrap' report-metrics-lambda: name: ${self:service}-${sls:stage, 'dev'}-report-metrics-lambda description: "EasyCLA Report Metrics API handler" - runtime: go1.x + runtime: provided.al2 handler: 'bin/metrics-report-lambda' timeout: 900 # maximum time allowed events: @@ -512,12 +476,13 @@ functions: individually: true patterns: - 'bin/metrics-report-lambda' + - 'bootstrap' zip-builder-scheduler-lambda: name: ${self:service}-${sls:stage, 'dev'}-zip-builder-scheduler-lambda description: "call zipbuilder-lambda for all cla groups periodically" handler: 'bin/zipbuilder-scheduler-lambda' - runtime: go1.x + runtime: provided.al2 timeout: 900 # maximum time allowed events: - schedule: @@ -528,24 +493,26 @@ functions: individually: true patterns: - 'bin/zipbuilder-scheduler-lambda' + - 'bootstrap' zip-builder-lambda: handler: 'bin/zipbuilder-lambda' name: ${self:service}-${sls:stage, 'dev'}-zip-builder-lambda description: "build zip of signed signature pdf for cla group" - runtime: go1.x + runtime: provided.al2 timeout: 900 # maximum time allowed memorySize: 1024 package: individually: true patterns: - 'bin/zipbuilder-lambda' + - 'bootstrap' gitlab-repository-check-lambda: handler: 'bin/gitlab-repository-check-lambda' name: ${self:service}-${sls:stage, 'dev'}-gitlab-repository-check-lambda description: "routine to periodically check the GitLab repository list for auto-enabled GitLab Groups" - runtime: go1.x + runtime: provided.al2 timeout: 900 # maximum time allowed memorySize: 1024 events: @@ -557,17 +524,19 @@ functions: individually: true patterns: - 'bin/gitlab-repository-check-lambda' + - 'bootstrap' # User Subscribe event for dynamodb cla-stage-users table. easycla-user-event-handler-lambda: handler: 'bin/user-subscribe-lambda' name: ${self:service}-${sls:stage, 'dev'}-user-event-handler-lambda - runtime: go1.x + runtime: provided.al2 description: Update easycla user data to user object in dynamodb package: individually: true patterns: - 'bin/user-subscribe-lambda' + - 'bootstrap' reservedConcurrency: 5 events: - sns: @@ -581,6 +550,8 @@ functions: method: ANY path: v1/{proxy+} cors: true + layers: + - ${self:custom.datadog.extensionLayerArn} apiv2: handler: wsgi_handler.handler @@ -590,6 +561,8 @@ functions: method: ANY path: v2/{proxy+} cors: true + layers: + - ${self:custom.datadog.extensionLayerArn} salesforceprojects: handler: cla.salesforce.get_projects @@ -599,6 +572,8 @@ functions: method: ANY path: v1/salesforce/projects cors: true + layers: + - ${self:custom.datadog.extensionLayerArn} salesforceprojectbyID: handler: cla.salesforce.get_project @@ -608,6 +583,8 @@ functions: method: ANY path: v1/salesforce/project cors: true + layers: + - ${self:custom.datadog.extensionLayerArn} # GitHub callback handler githubinstall: @@ -617,6 +594,8 @@ functions: - http: method: ANY path: v2/github/installation + layers: + - ${self:custom.datadog.extensionLayerArn} # GitHub callback handler githubactivity: @@ -626,6 +605,8 @@ functions: - http: method: POST path: v2/github/activity + layers: + - ${self:custom.datadog.extensionLayerArn} resources: @@ -641,6 +622,104 @@ resources: Fn::Not: [ Fn::Equals: [ "${env:STAGE}", "prod" ] ] Resources: + DatadogApiKeySecretReadPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyName: ${self:service}-${sls:stage}-datadog-api-key-secret-read + Roles: + - Ref: IamRoleLambdaExecution + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - secretsmanager:GetSecretValue + Resource: + - ${self:custom.datadog.apiKeySecretArn} + + DynamoDBIndexQueryPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyName: ${self:service}-${sls:stage}-dynamodb-index-query + Roles: + - Ref: IamRoleLambdaExecution + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - dynamodb:Query + Resource: + - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-*/index/*" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-api-log/index/bucket-dt-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-ccla-whitelist-requests/index/company-id-project-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-ccla-whitelist-requests/index/ccla-approval-list-request-project-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/github-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/github-username-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/gitlab-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/gitlab-username-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/github-user-external-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/lf-username-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-users/index/lf-email-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gerrit-instances/index/gerrit-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gerrit-instances/index/gerrit-project-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gerrit-instances/index/gerrit-project-sfid-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/project-signature-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/project-signature-date-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/reference-signature-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-project-reference-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-user-ccla-company-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/project-signature-external-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-company-signatory-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/reference-signature-search-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-project-id-type-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-company-initial-manager-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-signatures/index/signature-project-id-sigtype-signed-approved-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-companies/index/external-company-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-companies/index/company-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-companies/index/company-signing-entity-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects/index/external-project-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects/index/project-name-search-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects/index/project-name-lower-search-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects/index/foundation-sfid-project-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/project-repository-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/repository-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/repository-organization-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/external-repository-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/sfdc-repository-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/project-sfid-repository-organization-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/project-sfid-repository-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-repositories/index/repository-type-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-github-orgs/index/github-org-sfid-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-github-orgs/index/project-sfid-organization-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-github-orgs/index/organization-name-lower-search-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-company-invites/index/requested-company-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-type-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/user-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-id-external-project-id-event-epoch-time-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-project-id-event-time-epoch-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-cla-group-id-event-time-epoch-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-date-and-contains-pii-event-time-epoch-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-sfid-foundation-sfid-event-time-epoch-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-sfid-project-id-event-time-epoch-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-id-event-type-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-foundation-sfid-event-time-epoch-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/event-company-sfid-event-data-lower-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-events/index/company-sfid-cla-group-id-event-time-epoch-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-metrics/index/metric-type-salesforce-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-cla-manager-requests/index/cla-manager-requests-company-project-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-cla-manager-requests/index/cla-manager-requests-external-company-project-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-cla-manager-requests/index/cla-manager-requests-project-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects-cla-groups/index/cla-group-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-projects-cla-groups/index/foundation-sfid-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-org-sfid-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-project-sfid-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-organization-name-lower-search-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-project-sfid-organization-name-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-full-path-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-external-group-id-index" +# - "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/cla-${sls:stage}-gitlab-orgs/index/gitlab-org-url-index" + # ApiGatewayRestApi, and GatewayResponse are used to enable Cors on custom authorizer responses. # This let's the client read the HTTP status on error. # see link for more detail @@ -661,14 +740,14 @@ resources: RestApiId: Ref: 'ApiGatewayRestApi' - Cert: - Type: AWS::CertificateManager::Certificate - Condition: ShouldGenerateCertificate - Properties: - DomainName: ${self:custom.product.domain.name.${sls:stage}, self:custom.product.domain.name.other} - SubjectAlternativeNames: - - ${self:custom.product.domain.alt.${sls:stage}, self:custom.product.domain.alt.other} - ValidationMethod: DNS +# Cert: +# Type: AWS::CertificateManager::Certificate +# Condition: ShouldGenerateCertificate +# Properties: +# DomainName: ${self:custom.product.domain.name.${sls:stage}, self:custom.product.domain.name.other} +# SubjectAlternativeNames: +# - ${self:custom.product.domain.alt.${sls:stage}, self:custom.product.domain.alt.other} +# ValidationMethod: DNS Outputs: APIGatewayRootResourceID: diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock index af667f8b5..cfe6be076 100644 --- a/cla-backend/yarn.lock +++ b/cla-backend/yarn.lock @@ -2340,19 +2340,19 @@ aws-sdk@^2.1329.0, aws-sdk@^2.1404.0: uuid "8.0.0" xml2js "0.6.2" -axios@^0.28.0, axios@^1.6.2: - version "0.28.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.28.1.tgz#2a7bcd34a3837b71ee1a5ca3762214b86b703e70" - integrity sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ== +axios@^0.30.3, axios@^1.6.2: + version "0.30.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.30.3.tgz#ab1be887a2d37dd9ebc219657704180faf2c4920" + integrity sha512-5/tmEb6TmE/ax3mdXBc/Mi6YdPGxQsv+0p5YlciXWt3PHIn0VamqCXhRMtScnwY3lbgSXLneOuXAKUhgmSRpwg== dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" + follow-redirects "^1.15.4" + form-data "^4.0.4" proxy-from-env "^1.1.0" -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" @@ -2396,12 +2396,12 @@ bowser@^2.11.0: resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== +brace-expansion@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.3.tgz#6a9c6c268f85b53959ec527aeafe0f7300258eef" + integrity sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA== dependencies: - balanced-match "^1.0.0" + balanced-match "^4.0.2" braces@^3.0.3, braces@~3.0.2: version "3.0.3" @@ -3338,12 +3338,18 @@ fast-uri@^3.0.1: resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== -fast-xml-parser@4.4.1, fast-xml-parser@>=4.4.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz#a7e665ff79b7919100a5202f23984b6150f9b31e" - integrity sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w== +fast-xml-builder@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz#a485d7e8381f1db983cf006f849d1066e2935241" + integrity sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ== + +fast-xml-parser@4.4.1, fast-xml-parser@^5.3.6: + version "5.4.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.4.0.tgz#6fdd30cc233d2027832db517096e02a12c24e1db" + integrity sha512-ChpjDjfiMs4kETpVpXZHM/GM7smQbQ8T6q4jHD+M32tIH2jyz8gtCiByXn0WNUnQ6XUbVxYSe05FP3A52cEQyQ== dependencies: - strnum "^1.0.5" + fast-xml-builder "^1.0.0" + strnum "^2.1.2" fastest-levenshtein@^1.0.16: version "1.0.16" @@ -3436,10 +3442,10 @@ folder-hash@^3.3.0: graceful-fs "~4.2.0" minimatch "~3.0.4" -follow-redirects@^1.15.0: - version "1.15.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" - integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== +follow-redirects@^1.15.4: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== for-each@^0.3.3: version "0.3.4" @@ -3457,6 +3463,17 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + formidable@^2.0.1: version "2.1.2" resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" @@ -4510,12 +4527,12 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^5.0.1, minimatch@^5.1.0, minimatch@^6.1.6, minimatch@~3.0.4: - version "6.2.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-6.2.0.tgz#2b70fd13294178c69c04dfc05aebdb97a4e79e42" - integrity sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg== +minimatch@^10.2.1, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^5.0.1, minimatch@^5.1.0, minimatch@~3.0.4: + version "10.2.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.3.tgz#c0ef582f21071b0123a5bbf275252ebda921fbf6" + integrity sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg== dependencies: - brace-expansion "^2.0.1" + brace-expansion "^5.0.2" minimist@^1.2.6: version "1.2.8" @@ -4954,10 +4971,10 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -qs@^6.10.3, qs@^6.11.0: - version "6.14.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" - integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== +qs@^6.10.3, qs@^6.11.0, qs@^6.14.2: + version "6.15.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.0.tgz#db8fd5d1b1d2d6b5b33adaf87429805f1909e7b3" + integrity sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ== dependencies: side-channel "^1.1.0" @@ -5664,10 +5681,10 @@ strip-outer@^1.0.1: dependencies: escape-string-regexp "^1.0.2" -strnum@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" - integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== +strnum@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.2.tgz#a5e00ba66ab25f9cafa3726b567ce7a49170937a" + integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ== strtok3@^6.2.4: version "6.3.0" diff --git a/scripts/yarn-audit-filter.mjs b/scripts/yarn-audit-filter.mjs new file mode 100644 index 000000000..10623fe9b --- /dev/null +++ b/scripts/yarn-audit-filter.mjs @@ -0,0 +1,44 @@ +#!/usr/bin/env node +import fs from "node:fs"; + +const [,, auditPath = "audit.json", policyPath = ".yarn-audit-allowlist.json"] = process.argv; + +const sevRank = { info: 0, low: 1, moderate: 2, high: 3, critical: 4 }; + +const policy = JSON.parse(fs.readFileSync(policyPath, "utf8")); +const threshold = sevRank[policy.minSeverity ?? "high"] ?? sevRank.high; + +const allow = new Set((policy.allowlist ?? []).map(String)); + +const lines = fs.readFileSync(auditPath, "utf8").split(/\r?\n/).filter(Boolean); + +const hits = []; +for (const line of lines) { + let msg; + try { msg = JSON.parse(line); } catch { continue; } + if (msg.type !== "auditAdvisory") continue; + + // Yarn v1 emits { type:"auditAdvisory", data:{ advisory:{...}, ... } } + const advisory = msg?.data?.advisory ?? msg?.data; + const id = advisory?.id; + const severity = advisory?.severity; + const moduleName = advisory?.module_name; + const title = advisory?.title; + + if (id == null || severity == null) continue; + + if (allow.has(String(id))) continue; + if ((sevRank[severity] ?? -1) < threshold) continue; + + hits.push({ id, severity, moduleName, title }); +} + +if (hits.length) { + console.error(`yarn-audit policy failure: ${hits.length} advisories >= ${policy.minSeverity} (after allowlist)`); + for (const h of hits) { + console.error(`- [${h.severity}] ${h.moduleName} (advisory ${h.id}) ${h.title ?? ""}`.trim()); + } + process.exit(1); +} + +console.log(`OK: no advisories >= ${policy.minSeverity} (after allowlist)`); diff --git a/setenv.sh b/setenv.sh index 1d8af99ab..7826ada58 100644 --- a/setenv.sh +++ b/setenv.sh @@ -32,3 +32,23 @@ export GH_ORG_VALIDATION=false export DISABLE_LOCAL_PERMISSION_CHECKS=true export COMPANY_USER_VALIDATION=false export CLA_SIGNATURE_FILES_BUCKET=cla-signature-files-dev + +# Logging +export DDB_API_LOGGING=true +export OTEL_DATADOG_API_LOGGING=true +export DD_ENV=dev +export DD_SERVICE='easycla-backend' +export DD_SITE='datadoghq.com' +export DD_VERSION="${DD_VERSION:-$(git rev-parse --short=9 HEAD 2>/dev/null || echo '1.0')}" +# export DD_SITE='app.datadoghq.com' +export DD_API_KEY_SECRET_ARN1="$(cat ./DD_API_KEY_SECRET_ARN-1.secret)" +export DD_API_KEY_SECRET_ARN2="$(cat ./DD_API_KEY_SECRET_ARN-2.secret)" +export DD_APP_KEY_SECRET_ARN="$(cat ./DD_APP_KEY_SECRET_ARN.secret)" +# get via aws --profile lfproduct-dev --region us-east-1 secretsmanager get-secret-value --secret-id "$DD_API_KEY_SECRET_ARN1" --query SecretString --output text +# or via aws --profile lfproduct-dev --region us-east-2 secretsmanager get-secret-value --secret-id "$DD_API_KEY_SECRET_ARN2" --query SecretString --output text +export DD_API_KEY="$(cat ./DD_API_KEY.secret)" +# Get via aws --profile lfproduct-dev --region us-east-2 secretsmanager get-secret-value --secret-id "$DD_APP_KEY_SECRET_ARN" --query SecretString --output text +export DD_APP_KEY="$(cat ./DD_APP_KEY.secret)" +export DD_EXTENSION_LAYER_ARN_DEV="$(cat ./DD_EXTENSION_LAYER_ARN_DEV.secret)" +export DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT='localhost:4318' +export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT='http://localhost:4318/v1/traces' diff --git a/tests/functional/cypress/support/commands.js b/tests/functional/cypress/support/commands.js index afa89c8e0..aa3296666 100644 --- a/tests/functional/cypress/support/commands.js +++ b/tests/functional/cypress/support/commands.js @@ -1,6 +1,72 @@ import Ajv from 'ajv'; const ajv = new Ajv(); + +// ----------------------------------------------------------------------------- +// E2E marker headers (Cypress) +// +// Adds low-cardinality headers to every cy.request() so backend logs/metrics can +// distinguish CI E2E noise from real usage. +// +// Optional: pass a per-run ID via CYPRESS_E2E_RUN_ID (recommended in CI). +// ----------------------------------------------------------------------------- +const E2E_MARKER_HEADERS = { + // Backend expects boolish marker: + // X-EasyCLA-E2E: true/1/yes + 'X-EasyCLA-E2E': '1', + // Also set legacy marker for compatibility (backend checks it too). + 'X-E2E-TEST': '1', +}; + +const e2eRunId = + Cypress.env('E2E_RUN_ID') || + Cypress.env('GITHUB_RUN_ID') || + Cypress.env('CI_PIPELINE_ID') || + Cypress.env('BUILD_ID') || + Cypress.env('BUILD_NUMBER'); +if (e2eRunId) { + // Backend expects this exact header name for run id: + // X-EasyCLA-E2E-RunID + E2E_MARKER_HEADERS['X-EasyCLA-E2E-RunID'] = String(e2eRunId); +} + +const HTTP_METHOD_RE = /^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/i; + +// Ensure the overwrite is only applied in Cypress runtime. +if (typeof Cypress !== 'undefined' && Cypress.Commands && Cypress.Commands.overwrite) { + Cypress.Commands.overwrite('request', (originalFn, ...args) => { + let options = {}; + + // Support all cy.request() overloads while keeping behavior identical: + // cy.request(url) + // cy.request(url, body) + // cy.request(method, url) + // cy.request(method, url, body) + // cy.request(options) + if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null) { + options = { ...args[0] }; + } else if (args.length === 1) { + options = { url: args[0] }; + } else if (args.length === 2) { + const [a0, a1] = args; + if (typeof a0 === 'string' && typeof a1 === 'string' && HTTP_METHOD_RE.test(a0)) { + options = { method: a0, url: a1 }; + } else { + options = { url: a0, body: a1 }; + } + } else if (args.length >= 3) { + const [method, url, body] = args; + options = { method, url, body }; + } else { + return originalFn(...args); + } + + options.headers = { ...(options.headers || {}), ...E2E_MARKER_HEADERS }; + return originalFn(options); + }); +} +// E2E marker headers (Cypress) - end + //To validate API response using schema export function validateApiResponse(schemaPath, response) { cy.fixture(schemaPath).then((schema) => { diff --git a/utils/check_ssm_logging_params.sh b/utils/check_ssm_logging_params.sh new file mode 100755 index 000000000..3fa0b74e5 --- /dev/null +++ b/utils/check_ssm_logging_params.sh @@ -0,0 +1,32 @@ +#!/bin/bash +echo "dev us-east-1:" +echo -n 'cla-dd-site-dev: '; aws --profile lfproduct-dev --region us-east-1 ssm get-parameters --names "cla-dd-site-dev" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-dd-api-key-secret-arn-dev: '; aws --profile lfproduct-dev --region us-east-1 ssm get-parameters --names "cla-dd-api-key-secret-arn-dev" --with-decryption | jq -r '.Parameters[0].Value' +echo 'cla-dd-api-key-dev:'; aws --profile lfproduct-dev --region us-east-1 secretsmanager get-secret-value --secret-id "$(aws --profile lfproduct-dev --region us-east-1 ssm get-parameters --names "cla-dd-api-key-secret-arn-dev" --with-decryption | jq -r '.Parameters[0].Value')" --query SecretString --output text +echo -n 'cla-dd-extension-layer-arn-dev: '; aws --profile lfproduct-dev --region us-east-1 ssm get-parameters --names "cla-dd-extension-layer-arn-dev" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-ddb-api-logging-dev: '; aws --profile lfproduct-dev --region us-east-1 ssm get-parameters --names "cla-ddb-api-logging-dev" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-otel-datadog-api-logging-dev: '; aws --profile lfproduct-dev --region us-east-1 ssm get-parameters --names "cla-otel-datadog-api-logging-dev" --with-decryption | jq -r '.Parameters[0].Value' + +echo "dev us-east-2" +echo -n 'cla-dd-site-dev: '; aws --profile lfproduct-dev --region us-east-2 ssm get-parameters --names "cla-dd-site-dev" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-dd-api-key-secret-arn-dev: '; aws --profile lfproduct-dev --region us-east-2 ssm get-parameters --names "cla-dd-api-key-secret-arn-dev" --with-decryption | jq -r '.Parameters[0].Value' +echo 'cla-dd-api-key-dev:'; aws --profile lfproduct-dev --region us-east-2 secretsmanager get-secret-value --secret-id "$(aws --profile lfproduct-dev --region us-east-2 ssm get-parameters --names "cla-dd-api-key-secret-arn-dev" --with-decryption | jq -r '.Parameters[0].Value')" --query SecretString --output text +echo -n 'cla-dd-extension-layer-arn-dev: '; aws --profile lfproduct-dev --region us-east-2 ssm get-parameters --names "cla-dd-extension-layer-arn-dev" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-ddb-api-logging-dev: '; aws --profile lfproduct-dev --region us-east-2 ssm get-parameters --names "cla-ddb-api-logging-dev" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-otel-datadog-api-logging-dev: '; aws --profile lfproduct-dev --region us-east-2 ssm get-parameters --names "cla-otel-datadog-api-logging-dev" --with-decryption | jq -r '.Parameters[0].Value' + +echo "prod us-east-1" +echo -n 'cla-dd-site-prod: '; aws --profile lfproduct-prod --region us-east-1 ssm get-parameters --names "cla-dd-site-prod" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-dd-api-key-secret-arn-prod: '; aws --profile lfproduct-prod --region us-east-1 ssm get-parameters --names "cla-dd-api-key-secret-arn-prod" --with-decryption | jq -r '.Parameters[0].Value' +echo 'cla-dd-api-key-prod:'; aws --profile lfproduct-prod --region us-east-1 secretsmanager get-secret-value --secret-id "$(aws --profile lfproduct-prod --region us-east-1 ssm get-parameters --names "cla-dd-api-key-secret-arn-prod" --with-decryption | jq -r '.Parameters[0].Value')" --query SecretString --output text +echo -n 'cla-dd-extension-layer-arn-prod: '; aws --profile lfproduct-prod --region us-east-1 ssm get-parameters --names "cla-dd-extension-layer-arn-prod" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-ddb-api-logging-prod: '; aws --profile lfproduct-prod --region us-east-1 ssm get-parameters --names "cla-ddb-api-logging-prod" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-otel-datadog-api-logging-prod: '; aws --profile lfproduct-prod --region us-east-1 ssm get-parameters --names "cla-otel-datadog-api-logging-prod" --with-decryption | jq -r '.Parameters[0].Value' + +echo "prod us-east-2" +echo -n 'cla-dd-site-prod: '; aws --profile lfproduct-prod --region us-east-2 ssm get-parameters --names "cla-dd-site-prod" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-dd-api-key-secret-arn-prod: '; aws --profile lfproduct-prod --region us-east-2 ssm get-parameters --names "cla-dd-api-key-secret-arn-prod" --with-decryption | jq -r '.Parameters[0].Value' +echo 'cla-dd-api-key-prod:'; aws --profile lfproduct-prod --region us-east-2 secretsmanager get-secret-value --secret-id "$(aws --profile lfproduct-prod --region us-east-2 ssm get-parameters --names "cla-dd-api-key-secret-arn-prod" --with-decryption | jq -r '.Parameters[0].Value')" --query SecretString --output text +echo -n 'cla-dd-extension-layer-arn-prod: '; aws --profile lfproduct-prod --region us-east-2 ssm get-parameters --names "cla-dd-extension-layer-arn-prod" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-ddb-api-logging-prod: '; aws --profile lfproduct-prod --region us-east-2 ssm get-parameters --names "cla-ddb-api-logging-prod" --with-decryption | jq -r '.Parameters[0].Value' +echo -n 'cla-otel-datadog-api-logging-prod: '; aws --profile lfproduct-prod --region us-east-2 ssm get-parameters --names "cla-otel-datadog-api-logging-prod" --with-decryption | jq -r '.Parameters[0].Value' diff --git a/utils/get_ssm_value.sh b/utils/get_ssm_value.sh index 7c7d1d952..b8aede8e0 100755 --- a/utils/get_ssm_value.sh +++ b/utils/get_ssm_value.sh @@ -1,11 +1,11 @@ #!/bin/bash -set -euo pipefail -export AWS_PAGER="" if [ -z "$1" ] then echo "Usage: $0 " exit 1 fi +set -euo pipefail +export AWS_PAGER="" if [ -z "$REGION" ] then REGION="us-east-2" diff --git a/utils/otel_dd/check_spans.sh b/utils/otel_dd/check_spans.sh new file mode 100755 index 000000000..ddf82494a --- /dev/null +++ b/utils/otel_dd/check_spans.sh @@ -0,0 +1,2 @@ +#!/bin/bash +curl -s http://localhost:8888/metrics | egrep 'otelcol_(receiver_accepted_spans|exporter_sent_spans|exporter_send_failed_spans)' diff --git a/utils/otel_dd/check_spans_in_ddog.sh b/utils/otel_dd/check_spans_in_ddog.sh new file mode 100755 index 000000000..4d0e4658f --- /dev/null +++ b/utils/otel_dd/check_spans_in_ddog.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Example: +# ./utils/otel_dd/check_spans_in_ddog.sh --skip-e2e | jq -r '.data[].attributes.custom.http.route' | sort | uniq + +SKIP_E2E=0 +for arg in "$@"; do + case "$arg" in + --skip-e2e) SKIP_E2E=1 ;; + --no-skip-e2e) SKIP_E2E=0 ;; + *) ;; + esac +done + +payload='{ + "data": { + "type": "search_request", + "attributes": { + "filter": { + "from": "now-60m", + "to": "now", + "query": "service:easycla-backend env:dev" + }, + "sort": "timestamp", + "page": { "limit": 5000 } + } + } +}' + +# jq helper: treat missing as false; accept "true"/true +jq_filter=' +def is_e2e: + (.attributes.custom.easycla.e2e // false) + | tostring + | ascii_downcase == "true"; + +if env.SKIP_E2E == "1" then + .data |= (map(select(is_e2e | not))) +else + . +end +' + +curl -sS -X POST "https://api.${DD_SITE}/api/v2/spans/events/search" \ + -H "Content-Type: application/json" \ + -H "DD-API-KEY: ${DD_API_KEY}" \ + -H "DD-APPLICATION-KEY: ${DD_APP_KEY}" \ + -d "${payload}" \ +| SKIP_E2E="${SKIP_E2E}" jq "${jq_filter}" diff --git a/utils/otel_dd/otelcol-dd.yaml b/utils/otel_dd/otelcol-dd.yaml new file mode 100644 index 000000000..22a361df5 --- /dev/null +++ b/utils/otel_dd/otelcol-dd.yaml @@ -0,0 +1,30 @@ +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 +processors: + batch: +exporters: + debug: + verbosity: detailed + datadog: + api: + key: ${DD_API_KEY} + site: ${DD_SITE} +service: + telemetry: + logs: + level: info + metrics: + readers: + - pull: + exporter: + prometheus: + host: 0.0.0.0 + port: 8888 + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [datadog] diff --git a/utils/otel_dd/run_collector.sh b/utils/otel_dd/run_collector.sh new file mode 100755 index 000000000..2e432ff44 --- /dev/null +++ b/utils/otel_dd/run_collector.sh @@ -0,0 +1,19 @@ +#!/bin/bash +if [ -z "$DD_API_KEY_SECRET_ARN" ] +then + source setenv.sh +fi +if [ -z "$DD_API_KEY_SECRET_ARN" ] +then + echo "DD_API_KEY_SECRET_ARN is not set. Please set it in setenv.sh and try again." + exit 1 +fi + +docker run --rm -it \ + -p 4318:4318 \ + -p 8888:8888 \ + -e DD_API_KEY -e DD_SITE \ + -v "$PWD/utils/otel_dd/otelcol-dd.yaml:/etc/otelcol/config.yaml:ro" \ + -v /etc/passwd:/etc/passwd:ro \ + otel/opentelemetry-collector-contrib:latest \ + --config /etc/otelcol/config.yaml diff --git a/utils/otel_dd/validate_dd_keys.sh b/utils/otel_dd/validate_dd_keys.sh new file mode 100755 index 000000000..a7f1b3769 --- /dev/null +++ b/utils/otel_dd/validate_dd_keys.sh @@ -0,0 +1,2 @@ +#!/bin/bash +curl -sS -H "DD-API-KEY: ${DD_API_KEY}" -H "DD-APPLICATION-KEY: ${DD_APP_KEY}" "https://api.${DD_SITE}/api/v2/validate_keys" | jq -r diff --git a/utils/otel_dd_go/build.sh b/utils/otel_dd_go/build.sh new file mode 100755 index 000000000..c248c9f5e --- /dev/null +++ b/utils/otel_dd_go/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +go fmt otel_dd.go && go vet otel_dd.go && go build otel_dd.go diff --git a/utils/otel_dd_go/go.mod b/utils/otel_dd_go/go.mod new file mode 100644 index 000000000..206ccd48d --- /dev/null +++ b/utils/otel_dd_go/go.mod @@ -0,0 +1,30 @@ +module github.com/linuxfoundation/easycla/utils/otel_dd_go + +go 1.24.4 + +require ( + go.opentelemetry.io/otel v1.40.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 + go.opentelemetry.io/otel/sdk v1.40.0 +) + +require ( + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/grpc v1.78.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/utils/otel_dd_go/go.sum b/utils/otel_dd_go/go.sum new file mode 100644 index 000000000..66b49f2e8 --- /dev/null +++ b/utils/otel_dd_go/go.sum @@ -0,0 +1,61 @@ +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/utils/otel_dd_go/otel_dd.go b/utils/otel_dd_go/otel_dd.go new file mode 100644 index 000000000..c51785840 --- /dev/null +++ b/utils/otel_dd_go/otel_dd.go @@ -0,0 +1,245 @@ +package main + +import ( + "context" + "fmt" + "net/url" + "os" + "regexp" + "strings" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" +) + +var ( + reMultiSlash = regexp.MustCompile(`/{2,}`) + reAssetExt = regexp.MustCompile(`\.(png|svg|css|js|json|xml|htm|html)$`) + reSwaggerAsset = regexp.MustCompile(`^(/v[0-9]+)/swagger\.\{asset\}$`) + reUUIDValid = regexp.MustCompile(`[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`) + reUUIDLike = regexp.MustCompile(`/[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}(/|$)`) + reUUIDHexDash36 = regexp.MustCompile(`/[0-9a-fA-F-]{36}(/|$)`) + reNumericID = regexp.MustCompile(`/[0-9]+(/|$)`) + reSFIDValid = regexp.MustCompile(`/(?:00|a0)[A-Za-z0-9]{13,16}(/|$)`) + reSFIDLike = regexp.MustCompile(`/(?:00|a0)[^/]{1,32}(/|$)`) + reLFXIDValid = regexp.MustCompile(`/lf[A-Za-z0-9]{16,22}(/|$)`) + reLFXIDLike = regexp.MustCompile(`/lf[^/]{1,32}(/|$)`) + reNull = regexp.MustCompile(`/null(/|$)`) + reInvalidUUIDSeg = regexp.MustCompile(`/(?:invalid-uuid(?:-format)?|not-a-uuid)(/|$)`) + reInvalidSFIDSeg = regexp.MustCompile(`/invalid-sfid(?:-format)?(/|$)`) +) + +func sanitizeAPIPath(path string) string { + p := strings.TrimSpace(path) + if p == "" { + return "/" + } + if !strings.HasPrefix(p, "/") { + p = "/" + p + } + + p = reMultiSlash.ReplaceAllString(p, "/") + if len(p) > 1 && strings.HasSuffix(p, "/") { + p = strings.TrimSuffix(p, "/") + } + + p = reAssetExt.ReplaceAllString(p, ".{asset}") + if m := reSwaggerAsset.FindStringSubmatch(p); m != nil { + p = m[1] + "/swagger" + } + + p = reUUIDValid.ReplaceAllString(p, "{uuid}") + p = reUUIDLike.ReplaceAllString(p, "/{invalid-uuid}$1") + p = reUUIDHexDash36.ReplaceAllString(p, "/{invalid-uuid}$1") + p = reNumericID.ReplaceAllString(p, "/{id}$1") + p = reSFIDValid.ReplaceAllString(p, "/{sfid}$1") + p = reSFIDLike.ReplaceAllString(p, "/{invalid-sfid}$1") + p = reLFXIDValid.ReplaceAllString(p, "/{lfxid}$1") + p = reLFXIDLike.ReplaceAllString(p, "/{invalid-lfxid}$1") + p = reNull.ReplaceAllString(p, "/{null}$1") + p = reInvalidUUIDSeg.ReplaceAllString(p, "/{invalid-uuid}$1") + p = reInvalidSFIDSeg.ReplaceAllString(p, "/{invalid-sfid}$1") + + if p == "" { + return "/" + } + return p +} + +func stageToDDEnv(stage string) string { + st := strings.ToLower(strings.TrimSpace(stage)) + switch st { + case "prod", "production": + return "prod" + case "staging": + return "staging" + default: + return "dev" + } +} + +// buildOTLPTracesEndpoint matches the same rules as your Go backend: +// 1. OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (preserve path) +// 2. OTEL_EXPORTER_OTLP_ENDPOINT (append /v1/traces) +// 3. default http://localhost:4318/v1/traces +func buildOTLPTracesEndpoint() (host string, path string, insecure bool, err error) { + traces := strings.TrimSpace(os.Getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")) + base := strings.TrimSpace(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")) + + raw := "" + usedBase := false + if traces != "" { + raw = traces + } else if base != "" { + raw = base + usedBase = true + } else { + raw = "http://localhost:4318/v1/traces" + } + + insecure = true + parsedPath := "/" + + if strings.HasPrefix(raw, "http://") || strings.HasPrefix(raw, "https://") { + u, e := url.Parse(raw) + if e != nil { + return "", "", false, e + } + host = u.Host + parsedPath = u.Path + insecure = (u.Scheme == "http") + } else { + // host:port[/path] + host = raw + if strings.Contains(raw, "/") { + parts := strings.SplitN(raw, "/", 2) + host = parts[0] + if parts[1] != "" { + parsedPath = "/" + parts[1] + } else { + parsedPath = "/" + } + } + } + + if strings.TrimSpace(parsedPath) == "" { + parsedPath = "/" + } + if !strings.HasPrefix(parsedPath, "/") { + parsedPath = "/" + parsedPath + } + + if usedBase { + parsedPath = strings.TrimRight(parsedPath, "/") + "/v1/traces" + } + + if strings.TrimSpace(host) == "" { + return "", "", false, fmt.Errorf("invalid OTLP endpoint: %q", raw) + } + + return host, parsedPath, insecure, nil +} + +func main() { + if len(os.Args) < 2 { + fmt.Fprintf(os.Stderr, "usage: %s [METHOD]\n", os.Args[0]) + os.Exit(2) + } + + raw := os.Args[1] + method := "GET" + if len(os.Args) >= 3 { + method = strings.ToUpper(strings.TrimSpace(os.Args[2])) + if method == "" { + method = "GET" + } + } + + pathOnly := raw + if strings.HasPrefix(raw, "http://") || strings.HasPrefix(raw, "https://") { + if u, err := url.Parse(raw); err == nil && u.Path != "" { + pathOnly = u.Path + } else { + pathOnly = "/" + } + } + + route := sanitizeAPIPath(pathOnly) + spanName := fmt.Sprintf("%s %s", method, route) + + ddEnv := strings.TrimSpace(os.Getenv("DD_ENV")) + if ddEnv == "" { + ddEnv = stageToDDEnv(os.Getenv("STAGE")) + } + ddService := strings.TrimSpace(os.Getenv("DD_SERVICE")) + if ddService == "" { + ddService = "easycla-backend" + } + ddVersion := strings.TrimSpace(os.Getenv("DD_VERSION")) + if ddVersion == "" { + ddVersion = strings.TrimSpace(os.Getenv("VERSION")) + } + if ddVersion == "" { + ddVersion = "unknown" + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + host, urlPath, insecure, err := buildOTLPTracesEndpoint() + if err != nil { + fmt.Fprintf(os.Stderr, "endpoint error: %v\n", err) + os.Exit(3) + } + + opts := []otlptracehttp.Option{ + otlptracehttp.WithEndpoint(host), + otlptracehttp.WithURLPath(urlPath), + otlptracehttp.WithTimeout(2 * time.Second), + } + if insecure { + opts = append(opts, otlptracehttp.WithInsecure()) + } + + exp, err := otlptracehttp.New(ctx, opts...) + if err != nil { + fmt.Fprintf(os.Stderr, "exporter init error: %v\n", err) + os.Exit(4) + } + + res, _ := resource.New(ctx, + resource.WithAttributes( + attribute.String("service.name", ddService), + attribute.String("service.version", ddVersion), + attribute.String("deployment.environment.name", ddEnv), + ), + ) + + // Sync export so the span is sent before process exit. + tp := sdktrace.NewTracerProvider( + sdktrace.WithResource(res), + sdktrace.WithSyncer(exp), + ) + defer func() { + _ = tp.Shutdown(ctx) + }() + + otel.SetTracerProvider(tp) + + tr := otel.Tracer("easycla-otlp-poc") + _, span := tr.Start(ctx, spanName) + span.SetAttributes( + attribute.String("http.method", method), + attribute.String("http.route", route), + attribute.String("http.url", raw), + ) + span.End() + + _ = tp.ForceFlush(ctx) + + fmt.Printf("sent span: %s -> %s%s\n", spanName, host, urlPath) +} diff --git a/utils/otel_dd_py/otel_dd.py b/utils/otel_dd_py/otel_dd.py new file mode 100755 index 000000000..02c39f924 --- /dev/null +++ b/utils/otel_dd_py/otel_dd.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 + +# Example: +# ./cla-backend/.venv/bin/python ./utils/otel_dd_py/otel_dd.py 'https://example.com/v2/project/123' + +import os +import re +import sys +from urllib.parse import urlparse + +# --- Path sanitizer (same intent as your backend code; keeps /vN intact) --- +_RE_MULTI_SLASH = re.compile(r"/{2,}") +_RE_ASSET_EXT = re.compile(r"\.(png|svg|css|js|json|xml|htm|html)$") +_RE_SWAGGER_ASSET = re.compile(r"^(/v[0-9]+)/swagger\.\{asset\}$") +_RE_UUID_VALID = re.compile(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") +_RE_UUID_LIKE = re.compile(r"/[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}(/|$)") +_RE_UUID_HEXDASH_36 = re.compile(r"/[0-9a-fA-F-]{36}(/|$)") +_RE_NUMERIC_ID = re.compile(r"/[0-9]+(/|$)") +_RE_SFID_VALID = re.compile(r"/(?:00|a0)[A-Za-z0-9]{13,16}(/|$)") +_RE_SFID_LIKE = re.compile(r"/(?:00|a0)[^/]{1,32}(/|$)") +_RE_LFXID_VALID = re.compile(r"/lf[A-Za-z0-9]{16,22}(/|$)") +_RE_LFXID_LIKE = re.compile(r"/lf[^/]{1,32}(/|$)") +_RE_NULL = re.compile(r"/null(/|$)") +_RE_INVALID_UUID_SEG = re.compile(r"/(?:invalid-uuid(?:-format)?|not-a-uuid)(/|$)") +_RE_INVALID_SFID_SEG = re.compile(r"/invalid-sfid(?:-format)?(/|$)") + +def sanitize_api_path(path: str) -> str: + p = (path or "").strip() + if not p: + return "/" + if not p.startswith("/"): + p = "/" + p + + p = _RE_MULTI_SLASH.sub("/", p) + if len(p) > 1 and p.endswith("/"): + p = p[:-1] + + p = _RE_ASSET_EXT.sub(".{asset}", p) + p = _RE_SWAGGER_ASSET.sub(r"\1/swagger", p) + + p = _RE_UUID_VALID.sub("{uuid}", p) + p = _RE_UUID_LIKE.sub(r"/{invalid-uuid}\1", p) + p = _RE_UUID_HEXDASH_36.sub(r"/{invalid-uuid}\1", p) + p = _RE_NUMERIC_ID.sub(r"/{id}\1", p) + p = _RE_SFID_VALID.sub(r"/{sfid}\1", p) + p = _RE_SFID_LIKE.sub(r"/{invalid-sfid}\1", p) + p = _RE_LFXID_VALID.sub(r"/{lfxid}\1", p) + p = _RE_LFXID_LIKE.sub(r"/{invalid-lfxid}\1", p) + p = _RE_NULL.sub(r"/{null}\1", p) + p = _RE_INVALID_UUID_SEG.sub(r"/{invalid-uuid}\1", p) + p = _RE_INVALID_SFID_SEG.sub(r"/{invalid-sfid}\1", p) + return p or "/" + +def stage_to_dd_env(stage: str) -> str: + st = (stage or "dev").strip().lower() + if st in ("prod", "production"): + return "prod" + if st == "staging": + return "staging" + return "dev" + +def build_otlp_traces_endpoint() -> str: + """ + Preference order: + 1) OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (preserve path) + 2) OTEL_EXPORTER_OTLP_ENDPOINT (append /v1/traces) + 3) default http://localhost:4318/v1/traces + Accepts full URL or host:port[/path]. + """ + traces_ep = (os.getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") or "").strip() + base_ep = (os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT") or "").strip() + + used_base = False + if traces_ep: + raw = traces_ep + elif base_ep: + raw = base_ep + used_base = True + else: + raw = "http://localhost:4318/v1/traces" + + scheme = "http" + host = "" + path = "/" + + if raw.startswith("http://") or raw.startswith("https://"): + u = urlparse(raw) + scheme = u.scheme or "http" + host = u.netloc + path = u.path or "/" + else: + # host:port[/path] + if "/" in raw: + host, rest = raw.split("/", 1) + path = "/" + rest if rest else "/" + else: + host = raw + path = "/" + + if not path.startswith("/"): + path = "/" + path + if used_base: + path = path.rstrip("/") + "/v1/traces" + + if not host.strip(): + raise ValueError(f"invalid OTLP endpoint: {raw!r}") + + return f"{scheme}://{host}{path}" + +def main(argv: list[str]) -> int: + if len(argv) < 2: + print(f"usage: {argv[0]} [METHOD]", file=sys.stderr) + return 2 + + raw = argv[1] + method = (argv[2] if len(argv) >= 3 else "GET").strip().upper() or "GET" + + # Extract path from URL if needed + if raw.startswith("http://") or raw.startswith("https://"): + path = urlparse(raw).path or "/" + else: + path = raw + + route = sanitize_api_path(path) + span_name = f"{method} {route}" + + dd_env = (os.getenv("DD_ENV") or "").strip() or stage_to_dd_env(os.getenv("STAGE", "dev")) + dd_service = (os.getenv("DD_SERVICE") or "").strip() or "easycla-backend" + dd_version = (os.getenv("DD_VERSION") or "").strip() or (os.getenv("VERSION") or "").strip() or "1.0" + + endpoint = build_otlp_traces_endpoint() + + # Lazy imports (so the script can still show a clean error if deps are missing) + try: + from opentelemetry import trace as otel_trace + from opentelemetry.sdk.resources import Resource + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import SimpleSpanProcessor + from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter + except Exception as e: + print(f"otel deps missing: {e}", file=sys.stderr) + return 3 + + resource = Resource.create({ + "service.name": dd_service, + "service.version": dd_version, + "deployment.environment.name": dd_env, + }) + + provider = TracerProvider(resource=resource) + exporter = OTLPSpanExporter(endpoint=endpoint, timeout=2.0) + provider.add_span_processor(SimpleSpanProcessor(exporter)) + otel_trace.set_tracer_provider(provider) + tracer = otel_trace.get_tracer("easycla-otlp-poc") + + # Emit one span + try: + with tracer.start_as_current_span(span_name) as span: + span.set_attribute("http.method", method) + span.set_attribute("http.route", route) + span.set_attribute("http.url", raw) + finally: + # Ensure it gets pushed before exit + try: + provider.force_flush() + except Exception: + pass + try: + provider.shutdown() + except Exception: + pass + + print(f"sent span: {span_name} -> {endpoint}") + return 0 + +if __name__ == "__main__": + raise SystemExit(main(sys.argv)) + diff --git a/utils/set_ssm_logging_params.sh b/utils/set_ssm_logging_params.sh new file mode 100755 index 000000000..c6452b1ac --- /dev/null +++ b/utils/set_ssm_logging_params.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# REGION=us-east-1 +# STAGE=dev +# SET=1 +# STAGE=dev REGION=us-east-1 SET=1 ./set_ssm_logging_params.sh +if [ -z "$STAGE" ] +then + export STAGE=dev +fi +if [ -z "$REGION" ] +then + export REGION='us-east-1' +fi +if [ "$REGIN" = "us-east-1" ] +then + export REG_NUM=1 +fi +if [ "$REGIN" = "us-east-2" ] +then + export REG_NUM=2 +fi +if [ ! -z "$SET" ] +then + # ./utils/set_ssm_value.sh "cla-dd-site-${STAGE}" "app.datadoghq.com" String + ./utils/set_ssm_value.sh "cla-dd-site-${STAGE}" "datadoghq.com" String + ./utils/set_ssm_value.sh "cla-dd-version-${STAGE}" "$(git rev-parse --short=9 HEAD 2>/dev/null || echo '1.0')" String + ./utils/set_ssm_value.sh "cla-dd-api-key-secret-arn-${STAGE}" "$(cat ./DD_API_KEY_SECRET_ARN-${REG_NUM}.secret)" String + ./utils/set_ssm_value.sh "cla-dd-extension-layer-arn-${STAGE}" "$(cat ./DD_EXTENSION_LAYER_ARN_DEV.secret)" String + ./utils/set_ssm_value.sh "cla-ddb-api-logging-${STAGE}" true String + ./utils/set_ssm_value.sh "cla-otel-datadog-api-logging-${STAGE}" true String +fi + +./utils/get_ssm_value.sh "cla-dd-site-${STAGE}" +./utils/get_ssm_value.sh "cla-dd-version-${STAGE}" +./utils/get_ssm_value.sh "cla-dd-api-key-secret-arn-${STAGE}" +./utils/get_ssm_value.sh "cla-dd-extension-layer-arn-${STAGE}" +./utils/get_ssm_value.sh "cla-ddb-api-logging-${STAGE}" +./utils/get_ssm_value.sh "cla-otel-datadog-api-logging-${STAGE}" diff --git a/utils/set_ssm_value.sh b/utils/set_ssm_value.sh new file mode 100755 index 000000000..60dd5c728 --- /dev/null +++ b/utils/set_ssm_value.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +if [ $# -lt 2 ] +then + echo "Usage: $0 [type]" + echo "Example: STAGE=dev REGION=us-east-1 $0 cla-dd-site-dev dev String" + exit 1 +fi + +set -euo pipefail +export AWS_PAGER="" + +PARAM_NAME="$1" +PARAM_VALUE="$2" +PARAM_TYPE="${3:-String}" # Default to String if not provided + +if [ -z "${REGION:-}" ] +then + REGION="us-east-2" +fi + +if [ -z "${STAGE:-}" ] +then + STAGE="dev" +fi + +echo "Setting SSM parameter:" +echo " Name: ${PARAM_NAME}" +echo " Value: ${PARAM_VALUE}" +echo " Type: ${PARAM_TYPE}" +echo " Region: ${REGION}" +echo " Stage: ${STAGE}" +echo + +aws ssm put-parameter \ + --region "${REGION}" \ + --profile "lfproduct-${STAGE}" \ + --name "${PARAM_NAME}" \ + --value "${PARAM_VALUE}" \ + --type "${PARAM_TYPE}" \ + --overwrite + +echo "Done." + From 320a70994b561b1e261fa492b5c836ea81f1dee9 Mon Sep 17 00:00:00 2001 From: Lukasz Gryglicki Date: Thu, 26 Feb 2026 10:49:00 +0100 Subject: [PATCH 2/2] Updates: new py tool to chekc DDog spans and generate report Signed-off-by: Lukasz Gryglicki Assisted by [OpenAI](https://platform.openai.com/) Assisted by [GitHub Copilot](https://github.com/features/copilot) --- .github/workflows/yarn-scan-backend-go-pr.yml | 4 - .github/workflows/yarn-scan-backend-pr.yml | 4 - .gitignore | 3 + cla-backend-go/package.json | 3 + cla-backend-go/yarn.lock | 146 ++-- cla-backend/cla/routes.py | 3 +- cla-backend/package.json | 3 + cla-backend/yarn.lock | 99 +-- tests/functional/package-lock.json | 757 +++++++++++------- tests/functional/package.json | 6 + tests/functional/yarn.lock | 482 ++++++----- tests/rest/package.json | 3 + tests/rest/yarn.lock | 116 ++- utils/git_sync_to_dev.sh | 15 + utils/otel_dd/api_usage_stats_ddog.py | 279 +++++++ utils/set_ssm_logging_params.sh | 4 +- 16 files changed, 1275 insertions(+), 652 deletions(-) create mode 100755 utils/git_sync_to_dev.sh create mode 100755 utils/otel_dd/api_usage_stats_ddog.py diff --git a/.github/workflows/yarn-scan-backend-go-pr.yml b/.github/workflows/yarn-scan-backend-go-pr.yml index 32d6a8ba2..8b1054719 100644 --- a/.github/workflows/yarn-scan-backend-go-pr.yml +++ b/.github/workflows/yarn-scan-backend-go-pr.yml @@ -26,10 +26,6 @@ jobs: node-version: '20' - name: Setup run: yarn install -# - name: Yarn Audit -# working-directory: cla-backend-go -# run: | -# yarn audit - name: Yarn Audit working-directory: cla-backend-go run: | diff --git a/.github/workflows/yarn-scan-backend-pr.yml b/.github/workflows/yarn-scan-backend-pr.yml index d0bcad04f..b50387a79 100644 --- a/.github/workflows/yarn-scan-backend-pr.yml +++ b/.github/workflows/yarn-scan-backend-pr.yml @@ -26,10 +26,6 @@ jobs: node-version: '20' - name: Setup run: yarn install -# - name: Yarn Audit -# working-directory: cla-backend -# run: | -# yarn audit - name: Yarn Audit working-directory: cla-backend run: | diff --git a/.gitignore b/.gitignore index bd278f41a..34b0bf0a0 100755 --- a/.gitignore +++ b/.gitignore @@ -267,3 +267,6 @@ cla-backend/python-api.err cla-backend-go/golang-api.err cla-backend-go/golang-api.log utils/otel_dd_go/otel_dd +audit.json +spans*.json +api_usage.csv diff --git a/cla-backend-go/package.json b/cla-backend-go/package.json index 8c3e915bf..fa24791ef 100644 --- a/cla-backend-go/package.json +++ b/cla-backend-go/package.json @@ -47,6 +47,9 @@ "simple-git": "^3.16.0", "ws": ">=7.5.10", "xmlhttprequest-ssl": "^1.6.2", + "form-data": "^4.0.4", + "tar": "^7.5.8", + "minimatch": "^10.2.1", "fast-xml-parser": "^5.3.6" } } diff --git a/cla-backend-go/yarn.lock b/cla-backend-go/yarn.lock index e63f8510b..8f3a321ef 100644 --- a/cla-backend-go/yarn.lock +++ b/cla-backend-go/yarn.lock @@ -920,6 +920,13 @@ wraptile "^2.0.0" zames "^2.0.0" +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + "@kwsites/file-exists@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" @@ -1882,10 +1889,10 @@ axios@^0.30.3, axios@^1.6.2: form-data "^4.0.4" proxy-from-env "^1.1.0" -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" @@ -1924,20 +1931,12 @@ bowser@^2.11.0: resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== +brace-expansion@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.3.tgz#6a9c6c268f85b53959ec527aeafe0f7300258eef" + integrity sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA== dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" + balanced-match "^4.0.2" braces@^3.0.3, braces@~3.0.2: version "3.0.3" @@ -2117,10 +2116,10 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== ci-info@^3.8.0: version "3.9.0" @@ -2276,11 +2275,6 @@ compress-commons@^4.1.2: normalize-path "^3.0.0" readable-stream "^3.6.0" -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - content-disposition@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -2949,16 +2943,7 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -form-data@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" - integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -form-data@^4.0.4: +form-data@^4.0.0, form-data@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== @@ -3028,13 +3013,6 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3962,51 +3940,29 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== +minimatch@^10.2.1, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^5.0.1, minimatch@^5.1.0, minimatch@~3.0.4: + version "10.2.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" + integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1, minimatch@^5.1.0: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@~3.0.4: - version "3.0.8" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" - integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== - dependencies: - brace-expansion "^1.1.7" + brace-expansion "^5.0.2" minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" +minipass@^7.0.4, minipass@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== +minizlib@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c" + integrity sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw== dependencies: - minipass "^3.0.0" - yallist "^4.0.0" + minipass "^7.1.2" mkdirp@^0.5.6: version "0.5.6" @@ -4015,11 +3971,6 @@ mkdirp@^0.5.6: dependencies: minimist "^1.2.6" -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -5081,17 +5032,16 @@ tar-stream@^2.1.0, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.1.15: - version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" - integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== +tar@^6.1.15, tar@^7.5.8: + version "7.5.9" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.9.tgz#817ac12a54bc4362c51340875b8985d7dc9724b8" + integrity sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg== dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" throat@^5.0.0: version "5.0.0" @@ -5472,10 +5422,10 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== yaml-ast-parser@0.0.43: version "0.0.43" diff --git a/cla-backend/cla/routes.py b/cla-backend/cla/routes.py index 624f7a50c..f3fb2bcd9 100755 --- a/cla-backend/cla/routes.py +++ b/cla-backend/cla/routes.py @@ -8,7 +8,6 @@ import hug import os import re -import time from urllib.parse import urlparse import requests from falcon import HTTP_401, HTTP_400, HTTP_OK, HTTP_500, Response @@ -369,7 +368,7 @@ def _otel_start_request_span(request) -> None: from opentelemetry import context as otel_context from opentelemetry.propagate import extract - from opentelemetry.trace import SpanKind, Status, StatusCode, set_span_in_context + from opentelemetry.trace import SpanKind, set_span_in_context except Exception as e: try: cla.log.info(f"LG:api-log-otel-datadog-init-missing err={e}") diff --git a/cla-backend/package.json b/cla-backend/package.json index dd0102c77..992df2fac 100644 --- a/cla-backend/package.json +++ b/cla-backend/package.json @@ -57,8 +57,10 @@ "ansi-regex": "^5.0.1", "aws-sdk": "^2.1329.0", "axios": "^0.30.3", + "basic-ftp": "^5.2.0", "cookiejar": "^2.1.4", "file-type": "^16.5.4", + "form-data": "^4.0.4", "glob-parent": "^5.1.2", "http-cache-semantics": "^4.1.1", "ini": "^1.3.7", @@ -74,6 +76,7 @@ "shell-quote": "^1.7.3", "simple-git": "^3.16.0", "ws": ">=7.5.10", + "tar": "^7.5.8", "xmlhttprequest-ssl": "^1.6.2", "fast-xml-parser": "^5.3.6" } diff --git a/cla-backend/yarn.lock b/cla-backend/yarn.lock index cfe6be076..f2a5efb28 100644 --- a/cla-backend/yarn.lock +++ b/cla-backend/yarn.lock @@ -1153,6 +1153,13 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + "@kwsites/file-exists@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" @@ -2359,10 +2366,10 @@ base64-js@^1.0.2, base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -basic-ftp@^5.0.2: - version "5.0.5" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" - integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== +basic-ftp@^5.0.2, basic-ftp@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.2.0.tgz#7c2dff63c918bde60e6bad1f2ff93dcf5137a40a" + integrity sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw== binary-extensions@^2.0.0: version "2.3.0" @@ -2586,10 +2593,10 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== ci-info@^3.8.0: version "3.9.0" @@ -3454,16 +3461,7 @@ for-each@^0.3.3: dependencies: is-callable "^1.2.7" -form-data@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" - integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -form-data@^4.0.4: +form-data@^4.0.0, form-data@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== @@ -3533,13 +3531,6 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -4539,25 +4530,17 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +minipass@^7.0.4, minipass@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== +minizlib@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c" + integrity sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw== dependencies: - minipass "^3.0.0" - yallist "^4.0.0" + minipass "^7.1.2" mkdirp@^0.5.6: version "0.5.6" @@ -4566,11 +4549,6 @@ mkdirp@^0.5.6: dependencies: minimist "^1.2.6" -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -5778,17 +5756,16 @@ tar-stream@^2.1.0, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.1.15: - version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" - integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== +tar@^6.1.15, tar@^7.5.8: + version "7.5.9" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.9.tgz#817ac12a54bc4362c51340875b8985d7dc9724b8" + integrity sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg== dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" throat@^5.0.0: version "5.0.0" @@ -6179,10 +6156,10 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== yaml-ast-parser@0.0.43: version "0.0.43" diff --git a/tests/functional/package-lock.json b/tests/functional/package-lock.json index c2b50f1ae..7b89dffd1 100644 --- a/tests/functional/package-lock.json +++ b/tests/functional/package-lock.json @@ -22,30 +22,15 @@ "typescript": "^5.1.6" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -54,9 +39,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "license": "MIT", "peer": true, @@ -65,23 +50,23 @@ } }, "node_modules/@babel/core": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", - "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -108,15 +93,15 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -126,14 +111,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -143,17 +128,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -165,14 +139,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC", - "peer": true - }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", @@ -185,31 +151,31 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -239,9 +205,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -260,28 +226,28 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", - "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -307,35 +273,35 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -343,14 +309,14 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -448,9 +414,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "peer": true, "engines": { @@ -461,9 +427,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "peer": true, "engines": { @@ -499,9 +465,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "peer": true, "dependencies": { @@ -544,6 +510,18 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -564,9 +542,9 @@ "peer": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "peer": true, @@ -628,7 +606,8 @@ "version": "20.5.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.0.tgz", "integrity": "sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", @@ -856,10 +835,14 @@ "license": "MIT" }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "peer": true, + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base64-js": { "version": "1.5.1", @@ -881,6 +864,20 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -903,13 +900,16 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "license": "MIT", + "peer": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/braces": { @@ -933,9 +933,9 @@ "peer": true }, "node_modules/browserslist": { - "version": "4.25.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", - "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -954,10 +954,11 @@ "license": "MIT", "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001737", - "electron-to-chromium": "^1.5.211", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -1021,6 +1022,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -1034,9 +1048,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001739", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz", - "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==", + "version": "1.0.30001774", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", + "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==", "dev": true, "funding": [ { @@ -1411,7 +1425,8 @@ "node_modules/cypress/node_modules/@types/node": { "version": "16.18.40", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.40.tgz", - "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==" + "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==", + "license": "MIT" }, "node_modules/dashdash": { "version": "1.14.1", @@ -1503,9 +1518,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", "dev": true, "license": "BSD-2-Clause", "peer": true, @@ -1530,6 +1545,20 @@ "node": ">= 8.3.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1548,9 +1577,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.211", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz", - "integrity": "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==", + "version": "1.5.302", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", + "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", "dev": true, "license": "ISC", "peer": true @@ -1583,6 +1612,51 @@ "node": ">=8.6" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1856,14 +1930,17 @@ } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" }, "engines": { "node": ">= 0.12" @@ -1897,10 +1974,13 @@ "license": "MIT" }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "license": "MIT" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -1923,20 +2003,42 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -1971,20 +2073,22 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "license": "ISC", + "peer": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2039,24 +2143,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2066,10 +2170,10 @@ "node": ">=8" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2078,11 +2182,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, "engines": { "node": ">= 0.4" }, @@ -2090,6 +2197,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -2166,6 +2285,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -2361,9 +2481,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "peer": true, "dependencies": { @@ -2625,15 +2745,23 @@ } }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", + "peer": true, "dependencies": { - "yallist": "^4.0.0" - }, + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" } }, "node_modules/merge-stream": { @@ -2697,15 +2825,19 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.8.tgz", + "integrity": "sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw==", "license": "ISC", + "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -2718,19 +2850,19 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "license": "MIT", "peer": true, "dependencies": { @@ -2742,6 +2874,7 @@ "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^9.0.5", @@ -2763,16 +2896,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2786,43 +2909,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", - "peer": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mochawesome": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/mochawesome/-/mochawesome-7.1.3.tgz", @@ -2861,6 +2947,22 @@ "node": ">=10.0.0" } }, + "node_modules/mochawesome-merge/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/mochawesome-merge/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/mochawesome-merge/node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -2917,6 +3019,27 @@ "node": ">=6 <7 || >=8" } }, + "node_modules/mochawesome-merge/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mochawesome-merge/node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -2938,6 +3061,18 @@ "node": ">=8" } }, + "node_modules/mochawesome-merge/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/mochawesome-merge/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -3082,9 +3217,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT", "peer": true @@ -3381,6 +3516,7 @@ "version": "6.10.5", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.5.tgz", "integrity": "sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ==", + "deprecated": "when using stringify with arrayFormat comma, `[]` is appended on single-item arrays. Upgrade to v6.11.0 or downgrade to v6.10.4 to fix.", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.4" @@ -3522,6 +3658,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -3533,6 +3670,55 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -3607,6 +3793,24 @@ "node": ">=10" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -3961,9 +4165,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -4034,15 +4238,6 @@ "extsprintf": "^1.2.0" } }, - "node_modules/verror/node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4065,9 +4260,9 @@ "license": "ISC" }, "node_modules/workerpool": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.3.tgz", - "integrity": "sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "license": "Apache-2.0", "peer": true }, @@ -4123,10 +4318,12 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC", + "peer": true }, "node_modules/yargs": { "version": "17.7.2", diff --git a/tests/functional/package.json b/tests/functional/package.json index 4551281de..e4f81b30e 100644 --- a/tests/functional/package.json +++ b/tests/functional/package.json @@ -33,5 +33,11 @@ "cypress-mochawesome-reporter": "^3.5.1", "mochawesome-merge": "^4.3.0", "mochawesome-report-generator": "^6.2.0" + }, + "resolutions": { + "form-data": "2.5.5" + }, + "overrides": { + "form-data": "2.5.5" } } diff --git a/tests/functional/yarn.lock b/tests/functional/yarn.lock index 5a921cc15..cdd164458 100644 --- a/tests/functional/yarn.lock +++ b/tests/functional/yarn.lock @@ -2,66 +2,58 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.2.0": - version "2.3.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz" - integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@babel/code-frame@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== +"@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== dependencies: - "@babel/helper-validator-identifier" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.27.2": - version "7.28.0" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz" - integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== +"@babel/compat-data@^7.28.6": + version "7.29.0" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz" + integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== "@babel/core@^7.0.0", "@babel/core@^7.0.0-0": - version "7.28.3" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz" - integrity sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.3" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-module-transforms" "^7.28.3" - "@babel/helpers" "^7.28.3" - "@babel/parser" "^7.28.3" - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.3" - "@babel/types" "^7.28.2" + version "7.29.0" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz" + integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helpers" "^7.28.6" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.28.3": - version "7.28.3" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz" - integrity sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw== +"@babel/generator@^7.29.0": + version "7.29.1" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== dependencies: - "@babel/parser" "^7.28.3" - "@babel/types" "^7.28.2" + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" "@jridgewell/gen-mapping" "^0.3.12" "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" -"@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== +"@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== dependencies: - "@babel/compat-data" "^7.27.2" + "@babel/compat-data" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" browserslist "^4.24.0" lru-cache "^5.1.1" @@ -72,22 +64,22 @@ resolved "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== -"@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== +"@babel/helper-module-imports@^7.28.6": + version "7.28.6" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/helper-module-transforms@^7.28.3": - version "7.28.3" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz" - integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== +"@babel/helper-module-transforms@^7.28.6": + version "7.28.6" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz" + integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.28.3" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" "@babel/helper-plugin-utils@^7.27.1": version "7.27.1" @@ -99,30 +91,30 @@ resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz" - integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== "@babel/helper-validator-option@^7.27.1": version "7.27.1" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== -"@babel/helpers@^7.28.3": - version "7.28.3" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz" - integrity sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw== +"@babel/helpers@^7.28.6": + version "7.28.6" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz" + integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw== dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.2" + "@babel/template" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/parser@^7.27.2", "@babel/parser@^7.28.3": - version "7.28.3" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz" - integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA== +"@babel/parser@^7.27.2", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.0" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz" + integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww== dependencies: - "@babel/types" "^7.28.2" + "@babel/types" "^7.29.0" "@babel/plugin-syntax-jsx@^7.27.1": version "7.27.1" @@ -131,35 +123,35 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/template@^7.27.2": - version "7.27.2" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== +"@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3": - version "7.28.3" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz" - integrity sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ== +"@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": + version "7.29.0" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.3" + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.3" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.2" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" debug "^4.3.1" -"@babel/types@^7.27.1", "@babel/types@^7.28.2": - version "7.28.2" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz" - integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== +"@babel/types@^7.28.6", "@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== dependencies: "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" "@colors/colors@1.5.0": version "1.5.0" @@ -227,6 +219,14 @@ "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" @@ -238,9 +238,9 @@ integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": - version "0.3.30" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz" - integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q== + version "0.3.31" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -346,9 +346,9 @@ ansi-regex@^5.0.1: integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + version "6.2.2" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" @@ -358,9 +358,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: color-convert "^2.0.1" ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + version "6.2.3" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== arch@^2.2.0: version "2.2.0" @@ -424,11 +424,21 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +baseline-browser-mapping@^2.9.0: + version "2.10.0" + resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz" + integrity sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA== + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" @@ -454,12 +464,12 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz" - integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== +brace-expansion@^5.0.2: + version "5.0.3" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz" + integrity sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA== dependencies: - balanced-match "^1.0.0" + balanced-match "^4.0.2" braces@^3.0.3: version "3.0.3" @@ -474,14 +484,15 @@ browser-stdout@^1.3.1: integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== browserslist@^4.24.0, "browserslist@>= 4.21.0": - version "4.25.4" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz" - integrity sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg== + version "4.28.1" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== dependencies: - caniuse-lite "^1.0.30001737" - electron-to-chromium "^1.5.211" - node-releases "^2.0.19" - update-browserslist-db "^1.1.3" + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" + node-releases "^2.0.27" + update-browserslist-db "^1.2.0" buffer-crc32@~0.2.3: version "0.2.13" @@ -501,6 +512,14 @@ cachedir@^2.3.0: resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz" integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" @@ -519,10 +538,10 @@ camelcase@^6.0.0, camelcase@^6.3.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001737: - version "1.0.30001739" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz" - integrity sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA== +caniuse-lite@^1.0.30001759: + version "1.0.30001774" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz" + integrity sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA== caseless@~0.12.0: version "0.12.0" @@ -618,7 +637,7 @@ colorette@^2.0.16: resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -804,9 +823,18 @@ dotenv-parse-variables@^2.0.0: is-string-and-not-blank "^0.0.2" "dotenv@>= 10.x": - version "17.2.1" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz" - integrity sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ== + version "17.3.1" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz" + integrity sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA== + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" eastasianwidth@^0.2.0: version "0.2.0" @@ -821,10 +849,10 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -electron-to-chromium@^1.5.211: - version "1.5.211" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz" - integrity sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw== +electron-to-chromium@^1.5.263: + version "1.5.302" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz" + integrity sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg== emoji-regex@^8.0.0: version "8.0.0" @@ -851,6 +879,33 @@ enquirer@^2.3.6, "enquirer@>= 2.3.0 < 3": ansi-colors "^4.1.1" strip-ansi "^6.0.1" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" @@ -914,12 +969,7 @@ extract-zip@2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - -extsprintf@1.3.0: +extsprintf@^1.2.0, extsprintf@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== @@ -1019,14 +1069,17 @@ forever-agent@~0.6.1: resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== +form-data@2.5.5: + version "2.5.5" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz" + integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== dependencies: asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.35" + safe-buffer "^5.2.1" fs-extra@^10.0.0: version "10.1.0" @@ -1075,10 +1128,10 @@ fsu@^1.1.1: resolved "https://registry.npmjs.org/fsu/-/fsu-1.1.1.tgz" integrity sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -1090,15 +1143,29 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2: - version "1.2.1" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== +get-intrinsic@^1.0.2, get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-proto "^1.0.1" - has-symbols "^1.0.3" + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" @@ -1129,9 +1196,9 @@ glob-parent@^5.1.2: is-glob "^4.0.1" glob@^10.4.5: - version "10.4.5" - resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + version "10.5.0" + resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== dependencies: foreground-child "^3.1.0" jackspeak "^3.1.2" @@ -1140,7 +1207,19 @@ glob@^10.4.5: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^7.1.3, glob@^7.1.6: +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.6: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1171,6 +1250,11 @@ globby@^11.0.4: merge2 "^1.4.1" slash "^3.0.0" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" @@ -1181,22 +1265,24 @@ has-flag@^4.0.0: resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" -has@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: - function-bind "^1.1.1" + function-bind "^1.1.2" he@^1.2.0: version "1.2.0" @@ -1287,7 +1373,7 @@ is-number@^7.0.0: resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-inside@^3.0.2: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -1349,9 +1435,9 @@ jackspeak@^3.1.2: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + version "4.1.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" @@ -1523,6 +1609,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" @@ -1546,7 +1637,7 @@ mime-db@1.52.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.19: +mime-types@^2.1.35, mime-types@~2.1.19: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -1566,11 +1657,11 @@ minimatch@^3.1.1: brace-expansion "^1.1.7" minimatch@^9.0.4, minimatch@^9.0.5: - version "9.0.5" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + version "9.0.8" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.8.tgz" + integrity sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw== dependencies: - brace-expansion "^2.0.1" + brace-expansion "^5.0.2" minimist@^1.2.8: version "1.2.8" @@ -1578,14 +1669,14 @@ minimist@^1.2.8: integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + version "7.1.3" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== mocha@>=7: - version "11.7.1" - resolved "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz" - integrity sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A== + version "11.7.5" + resolved "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz" + integrity sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig== dependencies: browser-stdout "^1.3.1" chokidar "^4.0.1" @@ -1595,6 +1686,7 @@ mocha@>=7: find-up "^5.0.0" glob "^10.4.5" he "^1.2.0" + is-path-inside "^3.0.3" js-yaml "^4.1.0" log-symbols "^4.1.0" minimatch "^9.0.5" @@ -1656,10 +1748,10 @@ ms@^2.1.1, ms@^2.1.3: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== +node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== npm-run-path@^4.0.0: version "4.0.1" @@ -1937,7 +2029,7 @@ rxjs@^7.5.1: dependencies: tslib "^2.1.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -2087,9 +2179,9 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: ansi-regex "^5.0.1" strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + version "7.1.2" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== dependencies: ansi-regex "^6.0.1" @@ -2210,10 +2302,10 @@ untildify@^4.0.0: resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz" integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== -update-browserslist-db@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz" - integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw== +update-browserslist-db@^1.2.0: + version "1.2.3" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== dependencies: escalade "^3.2.0" picocolors "^1.1.1" @@ -2258,9 +2350,9 @@ which@^2.0.1: isexe "^2.0.0" workerpool@^9.2.0: - version "9.3.3" - resolved "https://registry.npmjs.org/workerpool/-/workerpool-9.3.3.tgz" - integrity sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw== + version "9.3.4" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz" + integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" diff --git a/tests/rest/package.json b/tests/rest/package.json index 670690ce9..4fdc265ac 100644 --- a/tests/rest/package.json +++ b/tests/rest/package.json @@ -7,5 +7,8 @@ }, "dependencies": { "newman": "^4.6.0" + }, + "resolutions": { + "form-data": "^4.0.4" } } diff --git a/tests/rest/yarn.lock b/tests/rest/yarn.lock index 1977528f5..a7b658c53 100644 --- a/tests/rest/yarn.lock +++ b/tests/rest/yarn.lock @@ -128,6 +128,14 @@ brotli@~1.3.2: dependencies: base64-js "^1.1.2" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -198,7 +206,7 @@ colors@1.4.0, colors@^1.1.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -280,6 +288,15 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -298,6 +315,33 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + escape-html@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -363,15 +407,46 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== +form-data@^4.0.4, form-data@~2.3.2: + version "4.0.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== dependencies: asynckit "^0.4.0" - combined-stream "^1.0.6" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" mime-types "^2.1.12" +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -379,6 +454,11 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + handlebars@4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.6.0.tgz#33af6c3eda930d7a924f5d8f1c6d8edc3180512e" @@ -415,6 +495,25 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + htmlparser2@^3.10.0: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" @@ -568,6 +667,11 @@ marked@0.7.0: resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + mime-db@1.42.0: version "1.42.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac" diff --git a/utils/git_sync_to_dev.sh b/utils/git_sync_to_dev.sh new file mode 100755 index 000000000..021b1f703 --- /dev/null +++ b/utils/git_sync_to_dev.sh @@ -0,0 +1,15 @@ +#!/bin/bash +echo 'To be used when you are on a feature branch based on main and want to sync it with current dev branch, so after merging current branch to main it will be the same as dev but you can commit this once' + +git fetch origin +git branch +git status +echo -n 'proceed (ctrl+c to stop)? ' +read + +git rm -r --cached . +# git checkout origin/dev -- . +git checkout dev -- . +git add -A + +echo "Now you can do: git commit -S -asm 'msg'; git push" diff --git a/utils/otel_dd/api_usage_stats_ddog.py b/utils/otel_dd/api_usage_stats_ddog.py new file mode 100755 index 000000000..003d6232c --- /dev/null +++ b/utils/otel_dd/api_usage_stats_ddog.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python3 +""" +Query Datadog span events and output per-route API usage statistics as CSV. + +Default behavior: + - Skips spans marked as attributes.custom.easycla.e2e == "true" + - Groups by templated route attributes.custom.http.route + - Outputs: api,n_calls,first,last (sorted by n_calls desc) + +Env vars required: + DD_SITE (e.g. datadoghq.com, datadoghq.eu, us3.datadoghq.com, ...) + DD_API_KEY + DD_APP_KEY + +Example: + ./utils/otel_dd/api_usage_stats_ddog.py --from now-60m --to now > api_usage.csv + ./utils/otel_dd/api_usage_stats_ddog.py --no-skip-e2e | head + ./utils/otel_dd/api_usage_stats_ddog.py --from now-24h --to now > api_usage.csv + ./utils/otel_dd/api_usage_stats_ddog.py --verbose | head +""" + +from __future__ import annotations + +import argparse +import csv +import datetime as dt +import json +import os +import sys +import urllib.error +import urllib.request +from typing import Any, Dict, List, Optional, Tuple + + +def eprint(*args: Any) -> None: + print(*args, file=sys.stderr) + + +def parse_ts(ts: str) -> dt.datetime: + # Datadog returns ISO 8601 with Z, e.g. "2026-02-26T08:25:15.686Z" + # Convert to timezone-aware UTC datetime. + ts = ts.strip() + if ts.endswith("Z"): + ts = ts[:-1] + "+00:00" + return dt.datetime.fromisoformat(ts).astimezone(dt.timezone.utc) + + +def fmt_ts(ts: dt.datetime) -> str: + # Format as "YYYY-MM-DD HH:MM:SS.mmm" (milliseconds) + ts_utc = ts.astimezone(dt.timezone.utc) + return ts_utc.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] + + +def is_e2e_true(span: Dict[str, Any]) -> bool: + attrs = span.get("attributes") or {} + custom = attrs.get("custom") or {} + easycla = custom.get("easycla") or {} + v = easycla.get("e2e", False) + return str(v).strip().lower() == "true" + + +def extract_route(span: Dict[str, Any]) -> Optional[str]: + """ + Prefer templated HTTP route: + attributes.custom.http.route -> "/v1/repository/{uuid}" + + Fallback: + attributes.resource_name -> "GET /v1/repository/{uuid}" (strip method) + """ + attrs = span.get("attributes") or {} + custom = attrs.get("custom") or {} + + http = custom.get("http") or {} + route = http.get("route") + if isinstance(route, str) and route.strip(): + return route.strip() + + resource_name = attrs.get("resource_name") + if isinstance(resource_name, str): + rn = resource_name.strip() + # Often "METHOD /path" + parts = rn.split(None, 1) + if len(parts) == 2 and parts[1].startswith("/"): + return parts[1].strip() + # Sometimes just "/path" + if rn.startswith("/"): + return rn + + return None + + +def extract_event_time(span: Dict[str, Any]) -> Optional[dt.datetime]: + attrs = span.get("attributes") or {} + ts = attrs.get("start_timestamp") or attrs.get("end_timestamp") + if not isinstance(ts, str) or not ts.strip(): + return None + try: + return parse_ts(ts) + except Exception: + return None + + +def datadog_post_json(url: str, headers: Dict[str, str], payload: Dict[str, Any], timeout_s: int = 30) -> Dict[str, Any]: + body = json.dumps(payload).encode("utf-8") + req = urllib.request.Request(url, data=body, headers=headers, method="POST") + try: + with urllib.request.urlopen(req, timeout=timeout_s) as resp: + raw = resp.read() + return json.loads(raw.decode("utf-8")) + except urllib.error.HTTPError as e: + raw = e.read().decode("utf-8", errors="replace") + raise RuntimeError(f"Datadog HTTP {e.code}: {raw}") from e + except urllib.error.URLError as e: + raise RuntimeError(f"Datadog request failed: {e}") from e + + +def fetch_spans( + dd_site: str, + dd_api_key: str, + dd_app_key: str, + query: str, + time_from: str, + time_to: str, + limit: int, + verbose: bool, +) -> List[Dict[str, Any]]: + url = f"https://api.{dd_site}/api/v2/spans/events/search" + headers = { + "Content-Type": "application/json", + "DD-API-KEY": dd_api_key, + "DD-APPLICATION-KEY": dd_app_key, + } + + payload: Dict[str, Any] = { + "data": { + "type": "search_request", + "attributes": { + "filter": { + "from": time_from, + "to": time_to, + "query": query, + }, + "sort": "timestamp", + "page": {"limit": limit}, + }, + } + } + + all_data: List[Dict[str, Any]] = [] + cursor: Optional[str] = None + page_num = 0 + + while True: + page_num += 1 + if cursor: + payload["data"]["attributes"]["page"]["cursor"] = cursor + else: + payload["data"]["attributes"]["page"].pop("cursor", None) + + if verbose: + eprint(f"[ddog] fetching page {page_num} (cursor={cursor!r}) ...") + + resp = datadog_post_json(url, headers, payload) + data = resp.get("data") or [] + if not isinstance(data, list): + raise RuntimeError("Unexpected Datadog response: 'data' is not a list") + + all_data.extend(data) + + meta = resp.get("meta") or {} + page = meta.get("page") or {} + + # Datadog APIs commonly use meta.page.after as the next cursor. + next_cursor = None + if isinstance(page, dict): + next_cursor = page.get("after") or page.get("cursor") or page.get("next_cursor") + + if not next_cursor: + break + if len(data) == 0: + break # safety + cursor = str(next_cursor) + + return all_data + + +def main() -> int: + p = argparse.ArgumentParser(description="Datadog span API usage stats (CSV)") + # Match your bash ergonomics, but default to SKIP + g = p.add_mutually_exclusive_group() + g.add_argument("--skip-e2e", action="store_true", help="Skip e2e spans (default)") + g.add_argument("--no-skip-e2e", action="store_true", help="Include e2e spans") + + p.add_argument("--from", dest="time_from", default="now-60m", help='Time range start (Datadog format), default "now-60m"') + p.add_argument("--to", dest="time_to", default="now", help='Time range end (Datadog format), default "now"') + p.add_argument("--query", default="service:easycla-backend env:dev", help='Datadog query string (default: "service:easycla-backend env:dev")') + p.add_argument("--limit", type=int, default=5000, help="Page limit per request (default: 5000)") + p.add_argument("--verbose", action="store_true", help="Log progress to stderr") + + args = p.parse_args() + + # Default skip-e2e unless explicitly --no-skip-e2e + skip_e2e = True + if args.no_skip_e2e: + skip_e2e = False + + dd_site = os.getenv("DD_SITE") + dd_api_key = os.getenv("DD_API_KEY") + dd_app_key = os.getenv("DD_APP_KEY") + + missing = [k for k, v in (("DD_SITE", dd_site), ("DD_API_KEY", dd_api_key), ("DD_APP_KEY", dd_app_key)) if not v] + if missing: + eprint(f"ERROR: missing env var(s): {', '.join(missing)}") + return 2 + + spans = fetch_spans( + dd_site=dd_site, # type: ignore[arg-type] + dd_api_key=dd_api_key, # type: ignore[arg-type] + dd_app_key=dd_app_key, # type: ignore[arg-type] + query=args.query, + time_from=args.time_from, + time_to=args.time_to, + limit=args.limit, + verbose=args.verbose, + ) + + # route -> (count, min_ts, max_ts) + stats: Dict[str, Tuple[int, dt.datetime, dt.datetime]] = {} + + kept = 0 + skipped_e2e = 0 + skipped_missing_route = 0 + skipped_missing_ts = 0 + + for span in spans: + if skip_e2e and is_e2e_true(span): + skipped_e2e += 1 + continue + + route = extract_route(span) + if not route: + skipped_missing_route += 1 + continue + + t = extract_event_time(span) + if not t: + skipped_missing_ts += 1 + continue + + kept += 1 + if route not in stats: + stats[route] = (1, t, t) + else: + cnt, tmin, tmax = stats[route] + stats[route] = (cnt + 1, min(tmin, t), max(tmax, t)) + + # Sort by count desc, then route + rows = sorted(((route, cnt, tmin, tmax) for route, (cnt, tmin, tmax) in stats.items()), + key=lambda x: (-x[1], x[0])) + + w = csv.writer(sys.stdout, lineterminator="\n") + w.writerow(["api", "n_calls", "first", "last"]) + for route, cnt, tmin, tmax in rows: + w.writerow([route, cnt, fmt_ts(tmin), fmt_ts(tmax)]) + + if args.verbose: + eprint(f"[ddog] spans fetched: {len(spans)}") + eprint(f"[ddog] spans kept: {kept}") + if skip_e2e: + eprint(f"[ddog] e2e skipped: {skipped_e2e}") + eprint(f"[ddog] no-route: {skipped_missing_route}") + eprint(f"[ddog] no-ts: {skipped_missing_ts}") + eprint(f"[ddog] routes: {len(stats)}") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/utils/set_ssm_logging_params.sh b/utils/set_ssm_logging_params.sh index c6452b1ac..36a59348e 100755 --- a/utils/set_ssm_logging_params.sh +++ b/utils/set_ssm_logging_params.sh @@ -11,11 +11,11 @@ if [ -z "$REGION" ] then export REGION='us-east-1' fi -if [ "$REGIN" = "us-east-1" ] +if [ "$REGION" = "us-east-1" ] then export REG_NUM=1 fi -if [ "$REGIN" = "us-east-2" ] +if [ "$REGION" = "us-east-2" ] then export REG_NUM=2 fi