diff --git a/.github/workflows/runner-full-stack-e2e.yml b/.github/workflows/runner-full-stack-e2e.yml index e9a66c55a3..da37b32b5f 100644 --- a/.github/workflows/runner-full-stack-e2e.yml +++ b/.github/workflows/runner-full-stack-e2e.yml @@ -982,7 +982,7 @@ jobs: needs: [authorize, catalog, daytona_image, test] outputs: history_source_ready: ${{ steps.history_source_ready.outputs.ready }} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 15 permissions: actions: read @@ -1108,12 +1108,52 @@ jobs: if: always() run: | set -euo pipefail - if ! command -v convert >/dev/null 2>&1 || ! command -v tesseract >/dev/null 2>&1; then - sudo apt-get update -qq - sudo apt-get install --no-install-recommends -y imagemagick tesseract-ocr + . /etc/os-release + if [ "${VERSION_CODENAME:-}" != "noble" ]; then + echo "::error::Public preview sanitizer requires Ubuntu Noble, got ${VERSION_CODENAME:-unknown}" >&2 + exit 1 fi - convert -version - tesseract --version + sudo apt-get update -qq + sudo apt-get install --no-install-recommends -y \ + "imagemagick=8:6.9.12.98+dfsg1-5.2build2" \ + "imagemagick-6.q16=8:6.9.12.98+dfsg1-5.2build2" \ + "libgif7=5.2.2-1ubuntu1.2" \ + "liblept5=1.82.0-3build4" \ + "libtesseract5=5.3.4-1build5" \ + "tesseract-ocr=5.3.4-1build5" \ + "tesseract-ocr-eng=1:4.1.0-2" \ + "tesseract-ocr-osd=1:4.1.0-2" + + verify_package_version() { + local package="$1" + local expected="$2" + local actual + actual="$(dpkg-query --show --showformat='${Version}' "$package")" + if [ "$actual" != "$expected" ]; then + echo "::error::Unexpected $package version: expected $expected, got $actual" >&2 + return 1 + fi + } + verify_package_version imagemagick "8:6.9.12.98+dfsg1-5.2build2" + verify_package_version imagemagick-6.q16 "8:6.9.12.98+dfsg1-5.2build2" + verify_package_version libgif7 "5.2.2-1ubuntu1.2" + verify_package_version liblept5 "1.82.0-3build4" + verify_package_version libtesseract5 "5.3.4-1build5" + verify_package_version tesseract-ocr "5.3.4-1build5" + verify_package_version tesseract-ocr-eng "1:4.1.0-2" + verify_package_version tesseract-ocr-osd "1:4.1.0-2" + + imagemagick_version="$(convert -version | sed -n '1p')" + tesseract_version="$(tesseract --version | sed -n '1p')" + if [[ "$imagemagick_version" != "Version: ImageMagick 6.9.12-98 "* ]]; then + echo "::error::Unexpected ImageMagick binary version: $imagemagick_version" >&2 + exit 1 + fi + if [ "$tesseract_version" != "tesseract 5.3.4" ]; then + echo "::error::Unexpected Tesseract binary version: $tesseract_version" >&2 + exit 1 + fi + printf '%s\n' "$imagemagick_version" "$tesseract_version" - name: Prepare public history bundle with redacted layout previews id: prepare_public_history diff --git a/tests/runner-e2e/workflow-security.test.ts b/tests/runner-e2e/workflow-security.test.ts index 1321c8f00b..3e10e951d4 100644 --- a/tests/runner-e2e/workflow-security.test.ts +++ b/tests/runner-e2e/workflow-security.test.ts @@ -671,6 +671,12 @@ describe("public repository paid workflow security", () => { workflow.indexOf(" report:"), workflow.indexOf(" publish_history:"), ); + const sanitizer = report.slice( + report.indexOf(" - name: Qualify public preview raster sanitizer"), + report.indexOf( + " - name: Prepare public history bundle with redacted layout previews", + ), + ); const publisher = workflow.slice(workflow.indexOf(" publish_history:")); expect(publisher).toContain("id-token: write"); expect(publisher).toContain("name: runner-e2e-history"); @@ -690,8 +696,41 @@ describe("public repository paid workflow security", () => { expect(workflow).toContain("unexpected_png="); expect(workflow).toContain("passed_count="); expect(workflow).toContain("pnpm test:e2e:runner:history:prepare"); - expect(report).toContain("tesseract-ocr"); - expect(report).toContain("tesseract --version"); + expect(report).toContain("runs-on: ubuntu-24.04"); + const sanitizerPackagePins = [ + "imagemagick=8:6.9.12.98+dfsg1-5.2build2", + "imagemagick-6.q16=8:6.9.12.98+dfsg1-5.2build2", + "libgif7=5.2.2-1ubuntu1.2", + "liblept5=1.82.0-3build4", + "libtesseract5=5.3.4-1build5", + "tesseract-ocr=5.3.4-1build5", + "tesseract-ocr-eng=1:4.1.0-2", + "tesseract-ocr-osd=1:4.1.0-2", + ]; + const sanitizerInstall = sanitizer.match( + /sudo apt-get install --no-install-recommends -y \\\n((?:\s+"[^"\n]+"(?: \\\n)?)+)/u, + ); + expect(sanitizer.match(/sudo apt-get install/gu)).toHaveLength(1); + expect( + [...(sanitizerInstall?.[1] ?? "").matchAll(/"([^"\n]+)"/gu)].map( + (match) => match[1], + ), + ).toEqual(sanitizerPackagePins); + for (const packagePin of sanitizerPackagePins) { + expect(sanitizer).toContain(`"${packagePin}"`); + const separator = packagePin.indexOf("="); + expect(sanitizer).toContain( + `verify_package_version ${packagePin.slice(0, separator)} "${packagePin.slice(separator + 1)}"`, + ); + } + expect(sanitizer).toContain('[ "${VERSION_CODENAME:-}" != "noble" ]'); + expect(sanitizer).toContain("dpkg-query --show --showformat='${Version}'"); + expect(sanitizer).toContain( + '[[ "$imagemagick_version" != "Version: ImageMagick 6.9.12-98 "* ]]', + ); + expect(sanitizer).toContain( + '[ "$tesseract_version" != "tesseract 5.3.4" ]', + ); expect(report).not.toContain("id-token: write"); expect(report).not.toMatch( /(?:OPENAI|ANTHROPIC|OPENROUTER|DAYTONA)_API_KEY/,