diff --git a/.github/workflows/gotip-testing.yml b/.github/workflows/gotip-testing.yml index a6f5dd7c1bd..a2dc7916fdf 100644 --- a/.github/workflows/gotip-testing.yml +++ b/.github/workflows/gotip-testing.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: { } # manually env: - TEST_RESULTS: /tmp/tip-results # path to where test results will be saved + TEST_RESULTS: ${{ github.workspace }}/tip-results # path to where test results will be saved permissions: id-token: write @@ -43,28 +43,88 @@ jobs: ref: ${{ github.ref }} repository: DataDog/dd-trace-go - - name: Get Gotip - run: |- - go install golang.org/dl/gotip@latest - echo "$HOME/go/bin" >> "$GITHUB_PATH" - echo "${{ github.workspace }}/bin" >> "$GITHUB_PATH" + - name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 + with: + go-version: stable + cache-dependency-path: '**/go.sum' + + - name: Capture trusted commit SHA + # Pinned before any sandboxed step so we can re-checkout this exact + # commit after the sandbox runs. Anything that escapes the sandbox + # could otherwise rewrite .git/HEAD or refs and trick the host into + # running attacker-controlled code via local actions. + id: trusted + run: echo "sha=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}" + + - name: Run tests with gotip (sandboxed) + # gotip pulls and runs the latest Go toolchain. Keep that toolchain and + # the test process inside gVisor, persisting only reports/coverage. + uses: geomys/sandboxed-step@7d75eb49d17fdeeb3656b3a57d35932d205bcfb9 # v1.2.1 + with: + persist-workspace-changes: 'true' + env: | + DD_APPSEC_WAF_TIMEOUT=1h + GO_CMD=gotip + TEST_RESULTS=${{ env.TEST_RESULTS }} + run: |- + mkdir -p "${GITHUB_WORKSPACE}/bin" + GOBIN="${GITHUB_WORKSPACE}/bin" go install golang.org/dl/gotip@latest + export PATH="${GITHUB_WORKSPACE}/bin:${PATH}" + ./scripts/install_tools.sh --bin-dir "${GITHUB_WORKSPACE}/bin" + gotip download + ./scripts/ci_test_core.sh - - name: Download dependencies - env: - GO_CMD: gotip + - name: Stage sandbox artifacts outside workspace + # Move the test results and coverage files to RUNNER_TEMP so the + # next checkout (which wipes the workspace) does not delete them. + # Only regular files are staged, to avoid following any symlinks + # that may have been planted inside the sandbox. + id: stage + if: always() run: |- - ./scripts/install_tools.sh --bin-dir ${{ github.workspace }}/bin - gotip download + set -euo pipefail + SAFE="${RUNNER_TEMP}/sandbox-artifacts" + rm -rf "${SAFE}" + mkdir -p "${SAFE}" + if [ -d "${TEST_RESULTS}" ]; then + ( cd "${TEST_RESULTS}" && find . -type f -print0 \ + | tar --null --no-recursion -T - -cf "${SAFE}/test-results.tar" ) + fi + ( cd "${GITHUB_WORKSPACE}" \ + && find . -type f \( -name 'coverage*.txt' -o -name 'profile.cov' \) -print0 \ + | tar --null --no-recursion -T - -cf "${SAFE}/coverage.tar" ) || true - - name: Run tests - env: - DD_APPSEC_WAF_TIMEOUT: 1h - TEST_RESULTS: ${{ env.TEST_RESULTS }} - GO_CMD: gotip - run: ./scripts/ci_test_core.sh + - name: Re-checkout trusted commit after sandbox + # Wipe the (potentially tampered) workspace and check out the SHA we + # captured before the sandbox ran. This restores .git, tracked files, + # and any local actions to a known-good state before any credentialed + # local action is invoked. + id: recheckout + if: always() + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + ref: ${{ steps.trusted.outputs.sha }} + repository: DataDog/dd-trace-go + clean: true + + - name: Restore sandbox artifacts + id: restore + if: always() && steps.recheckout.outcome == 'success' + run: |- + set -euo pipefail + SAFE="${RUNNER_TEMP}/sandbox-artifacts" + mkdir -p "${TEST_RESULTS}" + if [ -f "${SAFE}/test-results.tar" ]; then + tar -C "${TEST_RESULTS}" -xf "${SAFE}/test-results.tar" + fi + if [ -f "${SAFE}/coverage.tar" ]; then + tar -C "${GITHUB_WORKSPACE}" -xf "${SAFE}/coverage.tar" + fi - name: Upload the results to Datadog CI App - if: always() + if: always() && steps.restore.outcome == 'success' continue-on-error: true uses: ./.github/actions/dd-ci-upload with: @@ -73,7 +133,7 @@ jobs: tags: go:gotip,arch:${{ runner.arch }},os:${{ runner.os }} - name: Upload Coverage - if: always() + if: always() && steps.restore.outcome == 'success' continue-on-error: true uses: ./.github/actions/codecov-upload with: diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index 45c3faa9c85..7d95a6ec4c3 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -41,11 +41,11 @@ permissions: jobs: # Non-blocking: generates SARIF and uploads to GitHub Code Scanning. # Replaces Dependabot Security Alerts with reachability-aware findings. - # Uses the official golang/govulncheck-action — see PR description for + # Uses the official golang/govulncheck-action — see PR #4599 for # the enterprise allowlist request context. # NOTE: Only core packages are scanned here. Contrib modules are scanned - # separately in govulncheck-contribs-analysis (SARIF, non-blocking) and - # in govulncheck-tests (blocking, sandboxed). + # separately in govulncheck-contribs-analysis (SARIF, non-blocking, + # sandboxed) and in govulncheck-tests (blocking, sandboxed). govulncheck-analysis: runs-on: ubuntu-latest steps: @@ -114,15 +114,28 @@ jobs: with: go-version: stable cache-dependency-path: '**/go.sum' - - name: Run govulncheck on contrib modules (SARIF) + - name: Run govulncheck on contrib modules (SARIF, sandboxed) + # geomys/sandboxed-step uses gVisor to confine execution, preventing + # supply chain attacks from exfiltrating tokens or making network calls. # Installs govulncheck and runs govulncheck-contribs-sarif.sh, which # scans each contrib module and merges results into one SARIF file. # -format sarif exits 0 even when vulnerabilities are found. - run: | - go install golang.org/x/vuln/cmd/govulncheck@latest - ./.github/workflows/apps/govulncheck-contribs-sarif.sh govulncheck-contribs.sarif - - name: Upload contrib SARIF to GitHub Code Scanning + uses: geomys/sandboxed-step@7d75eb49d17fdeeb3656b3a57d35932d205bcfb9 # v1.2.1 + with: + persist-workspace-changes: 'true' + run: |- + mkdir -p "${GITHUB_WORKSPACE}/bin" + GOBIN="${GITHUB_WORKSPACE}/bin" go install golang.org/x/vuln/cmd/govulncheck@latest + export PATH="${GITHUB_WORKSPACE}/bin:${PATH}" + ./.github/workflows/apps/govulncheck-contribs-sarif.sh govulncheck-contribs.sarif + - name: Restore workspace after sandbox + id: restore if: always() + run: |- + git reset --hard HEAD + git clean -fdx -e govulncheck-contribs.sarif + - name: Upload contrib SARIF to GitHub Code Scanning + if: always() && steps.restore.outcome == 'success' && hashFiles('govulncheck-contribs.sarif') != '' uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: sarif_file: govulncheck-contribs.sarif @@ -130,6 +143,7 @@ jobs: # Blocking: fails the build if any reachable vulnerability is found. # Scans both core packages and all contrib modules (each with its own go.mod). + # govulncheck execution is sandboxed via geomys/sandboxed-step (gVisor). govulncheck-tests: runs-on: ubuntu-latest steps: @@ -142,9 +156,14 @@ jobs: with: go-version: stable cache-dependency-path: '**/go.sum' - - name: Install govulncheck - run: go install golang.org/x/vuln/cmd/govulncheck@latest - - name: Run govulncheck (core) - run: govulncheck ./ddtrace/... ./appsec/... ./profiler/... ./internal/... ./instrumentation/... - - name: Run govulncheck (contrib) - run: ./.github/workflows/apps/govulncheck-contribs-v2.sh + - name: Run govulncheck (sandboxed) + # geomys/sandboxed-step uses gVisor to confine execution, preventing + # supply chain attacks from exfiltrating tokens or making network calls. + uses: geomys/sandboxed-step@7d75eb49d17fdeeb3656b3a57d35932d205bcfb9 # v1.2.1 + with: + run: | + mkdir -p "${GITHUB_WORKSPACE}/bin" + GOBIN="${GITHUB_WORKSPACE}/bin" go install golang.org/x/vuln/cmd/govulncheck@latest + export PATH="${GITHUB_WORKSPACE}/bin:${PATH}" + govulncheck ./ddtrace/... ./appsec/... ./profiler/... ./internal/... ./instrumentation/... + ./.github/workflows/apps/govulncheck-contribs-v2.sh diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 5e36e2618a2..d35fea8244f 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -55,24 +55,29 @@ jobs: tools-dir: ${{ github.workspace }}/_tools tools-bin: ${{ github.workspace }}/bin - - name: go get -u - run: |- - mkdir -p "$TEST_RESULTS" - # shellcheck disable=SC2086 - go get -u -t $PACKAGES - go mod tidy - find . -iname go.mod -exec dirname {} \; | while read -r d; do pushd "$d" && go mod tidy && popd; done - - - name: Install requested go-libddwaf version - if: github.event_name == 'workflow_call' && inputs.go-libddwaf-ref != '' - run: |- - go get -u -t github.com/DataDog/go-libddwaf/v4@${{ inputs.go-libddwaf-ref }} - go mod tidy - - - name: Compile dd-trace-go - run: | - # shellcheck disable=SC2086 - go build $PACKAGES + - name: Compile dd-trace-go with updated dependencies (sandboxed) + # go get -u executes dependency code from the network. Keep those + # changes inside the gVisor sandbox; this setup job only needs to prove + # the updated dependency graph still compiles. + uses: geomys/sandboxed-step@7d75eb49d17fdeeb3656b3a57d35932d205bcfb9 # v1.2.1 + with: + env: | + GOPROXY=${{ env.GOPROXY }} + GO_LIBDDWAF_REF=${{ inputs.go-libddwaf-ref }} + PACKAGES=${{ env.PACKAGES }} + TEST_RESULTS=${{ env.TEST_RESULTS }} + run: |- + mkdir -p "$TEST_RESULTS" + # shellcheck disable=SC2086 + go get -u -t $PACKAGES + go mod tidy + find . -iname go.mod -exec dirname {} \; | while read -r d; do pushd "$d" && go mod tidy && popd; done + if [ -n "${GO_LIBDDWAF_REF:-}" ]; then + go get -u -t "github.com/DataDog/go-libddwaf/v4@${GO_LIBDDWAF_REF}" + go mod tidy + fi + # shellcheck disable=SC2086 + go build $PACKAGES - name: Compute Matrix id: matrix @@ -92,6 +97,8 @@ jobs: chunk: ${{ fromJson(needs.setup-env.outputs.matrix) }} runs-on: ubuntu-latest if: github.repository_owner == 'DataDog' # only run on DataDog's repository, not in forks + env: + TEST_RESULTS: ${{ github.workspace }}/test-results steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -108,19 +115,80 @@ jobs: go-version: stable tools-dir: ${{ github.workspace }}/_tools tools-bin: ${{ github.workspace }}/bin - - name: Test contribs + - name: Capture trusted commit SHA + # Pinned before any sandboxed step so we can re-checkout this exact + # commit after the sandbox runs. Anything that escapes the sandbox + # could otherwise rewrite .git/HEAD or refs and trick the host into + # running attacker-controlled code via local actions. + id: trusted + run: echo "sha=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}" + - name: Test contribs (sandboxed) # It needs to run before "Test dd-trace-go" to avoid TestTelemetryEnabled tests to fail. + # The smoke script runs go get -u inside each contrib module, so run it + # in gVisor and persist only the generated reports/coverage for uploads. + uses: geomys/sandboxed-step@7d75eb49d17fdeeb3656b3a57d35932d205bcfb9 # v1.2.1 + with: + persist-workspace-changes: 'true' + env: | + GOPROXY=${{ env.GOPROXY }} + TEST_RESULTS=${{ env.TEST_RESULTS }} + run: |- + export PATH="${GITHUB_WORKSPACE}/bin:${PATH}" + ./scripts/ci_test_contrib.sh smoke ${{ toJson(matrix.chunk) }} + - name: Stage sandbox artifacts outside workspace + # Move the test results and coverage files to RUNNER_TEMP so the + # next checkout (which wipes the workspace) does not delete them. + # Only regular files are staged, to avoid following any symlinks + # that may have been planted inside the sandbox. + id: stage + if: always() run: |- - export PATH="${{ github.workspace }}/bin:${PATH}" - ./scripts/ci_test_contrib.sh smoke ${{ toJson(matrix.chunk) }} + set -euo pipefail + SAFE="${RUNNER_TEMP}/sandbox-artifacts" + rm -rf "${SAFE}" + mkdir -p "${SAFE}" + if [ -d "${TEST_RESULTS}" ]; then + ( cd "${TEST_RESULTS}" && find . -type f -print0 \ + | tar --null --no-recursion -T - -cf "${SAFE}/test-results.tar" ) + fi + ( cd "${GITHUB_WORKSPACE}" \ + && find . -type f \( -name 'coverage*.txt' -o -name 'profile.cov' \) -print0 \ + | tar --null --no-recursion -T - -cf "${SAFE}/coverage.tar" ) || true + - name: Re-checkout trusted commit after sandbox + # Wipe the (potentially tampered) workspace and check out the SHA we + # captured before the sandbox ran. This restores .git, tracked files, + # and any local actions to a known-good state before any credentialed + # local action is invoked. + id: recheckout + if: always() + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + ref: ${{ steps.trusted.outputs.sha }} + repository: DataDog/dd-trace-go + clean: true + - name: Restore sandbox artifacts + id: restore + if: always() && steps.recheckout.outcome == 'success' + run: |- + set -euo pipefail + SAFE="${RUNNER_TEMP}/sandbox-artifacts" + mkdir -p "${TEST_RESULTS}" + if [ -f "${SAFE}/test-results.tar" ]; then + tar -C "${TEST_RESULTS}" -xf "${SAFE}/test-results.tar" + fi + if [ -f "${SAFE}/coverage.tar" ]; then + tar -C "${GITHUB_WORKSPACE}" -xf "${SAFE}/coverage.tar" + fi - name: Get Datadog credentials id: dd-sts + if: always() && steps.restore.outcome == 'success' continue-on-error: true uses: DataDog/dd-sts-action@2e8187910199bd93129520183c093e19aa585c75 with: policy: dd-trace-go - name: Upload the results to Datadog CI App - if: always() + if: always() && steps.restore.outcome == 'success' continue-on-error: true uses: ./.github/actions/dd-ci-upload with: @@ -128,7 +196,7 @@ jobs: path: ${{ env.TEST_RESULTS }} tags: go:stable,arch:${{ runner.arch }},os:${{ runner.os }} - name: Upload Coverage - if: always() + if: always() && steps.restore.outcome == 'success' continue-on-error: true uses: ./.github/actions/codecov-upload with: