From 0c1d65283504eceecdcdc5494accb688220e14bc Mon Sep 17 00:00:00 2001 From: Peter Amiri Date: Wed, 14 Oct 2026 01:09:36 -0700 Subject: [PATCH 1/2] ci: verify the SQLite JDBC download before installing it into Lucee The PR, snapshot and shared test-env setup steps fetched sqlite-jdbc with `curl -sL`, which exits 0 on an HTTP error page or a truncated transfer. A damaged jar then failed much later: Tomcat logged "zip END header not found" and the core suite errored with org.sqlite.JDBC not found. All three now run tools/ci/fetch-sqlite-jdbc.sh. It downloads with `curl --fail` and retries, checks the jar against Maven Central's published SHA-256, downloads again on a mismatch (three attempts), and otherwise fails the step with the URL, the hash and the size it got. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Peter Amiri --- .../actions/setup-wheels-test-env/action.yml | 3 +- .github/workflows/pr.yml | 3 +- .github/workflows/snapshot.yml | 3 +- tools/ci/fetch-sqlite-jdbc.sh | 47 +++++++++++++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100755 tools/ci/fetch-sqlite-jdbc.sh diff --git a/.github/actions/setup-wheels-test-env/action.yml b/.github/actions/setup-wheels-test-env/action.yml index 10102f69bd..4fe3ae59a7 100644 --- a/.github/actions/setup-wheels-test-env/action.yml +++ b/.github/actions/setup-wheels-test-env/action.yml @@ -81,8 +81,7 @@ runs: - name: Download SQLite JDBC driver shell: bash run: | - curl -sL --retry 3 --retry-delay 2 --retry-all-errors "https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.50.3.0/sqlite-jdbc-3.50.3.0.jar" \ - -o /tmp/sqlite-jdbc.jar + bash tools/ci/fetch-sqlite-jdbc.sh /tmp/sqlite-jdbc.jar - name: Start Lucee server shell: bash diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 314ff263fd..5a71a8e487 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -92,8 +92,7 @@ jobs: - name: Download SQLite JDBC driver run: | - curl -sL --retry 3 --retry-delay 2 --retry-all-errors "https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.50.3.0/sqlite-jdbc-3.50.3.0.jar" \ - -o /tmp/sqlite-jdbc.jar + bash tools/ci/fetch-sqlite-jdbc.sh /tmp/sqlite-jdbc.jar - name: Start Lucee server run: | diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 4a3c411aaf..7ae3fcff0b 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -81,8 +81,7 @@ jobs: - name: Download SQLite JDBC driver run: | - curl -sL --retry 3 --retry-delay 2 --retry-all-errors "https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.50.3.0/sqlite-jdbc-3.50.3.0.jar" \ - -o /tmp/sqlite-jdbc.jar + bash tools/ci/fetch-sqlite-jdbc.sh /tmp/sqlite-jdbc.jar - name: Start Lucee server run: | diff --git a/tools/ci/fetch-sqlite-jdbc.sh b/tools/ci/fetch-sqlite-jdbc.sh new file mode 100755 index 0000000000..375209e5f2 --- /dev/null +++ b/tools/ci/fetch-sqlite-jdbc.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Download the SQLite JDBC driver that CI installs into Lucee Express, and fail +# loudly unless the file is exactly the pinned jar. +# +# A plain `curl -sL` exits 0 on an HTTP error page or a truncated transfer, and +# the damaged jar only surfaced later as Tomcat's "zip END header not found" +# and `org.sqlite.JDBC` missing at test time. Each attempt here must pass +# `curl --fail` and the SHA-256 check; a mismatch is downloaded again, up to +# three attempts. +# +# Usage: bash tools/ci/fetch-sqlite-jdbc.sh [output-path] (default /tmp/sqlite-jdbc.jar) +set -euo pipefail + +OUT="${1:-/tmp/sqlite-jdbc.jar}" +VERSION="3.50.3.0" +# Maven Central's published sqlite-jdbc-3.50.3.0.jar.sha256. +SHA256="a3f53a2aa15ae9425a9e793bbe9c8e5288febeb4b65ef5c1a4e80d4c2045cf08" +URL="https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/${VERSION}/sqlite-jdbc-${VERSION}.jar" + +sha256_of() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | cut -d' ' -f1 + else + shasum -a 256 "$1" | cut -d' ' -f1 + fi +} + +tmp="${OUT}.download" +for attempt in 1 2 3; do + rm -f "$tmp" + if ! curl -fsSL --retry 3 --retry-delay 2 --retry-all-errors -o "$tmp" "$URL"; then + echo "Attempt ${attempt}: could not download ${URL}" >&2 + else + actual="$(sha256_of "$tmp")" + if [ "$actual" = "$SHA256" ]; then + mv "$tmp" "$OUT" + echo "sqlite-jdbc ${VERSION} downloaded and verified (sha256 ${SHA256}): ${OUT}" + exit 0 + fi + echo "Attempt ${attempt}: sqlite-jdbc ${VERSION} has sha256 ${actual} ($(wc -c <"$tmp" | tr -d ' ') bytes), expected ${SHA256}" >&2 + fi + [ "$attempt" -lt 3 ] && sleep 5 +done + +rm -f "$tmp" +echo "::error::Could not download a verified sqlite-jdbc ${VERSION} from ${URL}" >&2 +exit 1 From 9154b4fdf47b8b861f84ede220df20b22264bebd Mon Sep 17 00:00:00 2001 From: Peter Amiri Date: Wed, 14 Oct 2026 01:21:56 -0700 Subject: [PATCH 2/2] ci: fetch every SQLite JDBC driver download through the verified helper tools/ci/fetch-sqlite-jdbc.sh takes an optional version and SHA-256, so the 3.49.1.0 driver the CLI stages is now downloaded through it as well: test-cli-local.sh (the deploy-ci CLI suite), test-cli-e2e.sh and test-local.sh pin it by the SHA-256 the release packages already use. No `curl -sL` download of the driver remains. CliHardenerS1S10Spec's S9 hold read the 3.50.3.0 URL from pr.yml, which now lives in the helper. It now checks that pr.yml calls the helper with its default, that the helper defaults to 3.50.3.0, and that test-cli-local.sh passes 3.49.1.0 with a SHA-256. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Peter Amiri --- .../specs/commands/CliHardenerS1S10Spec.cfc | 11 +++++++++-- tools/ci/fetch-sqlite-jdbc.sh | 19 +++++++++++++++---- tools/test-cli-e2e.sh | 4 ++-- tools/test-cli-local.sh | 5 +++-- tools/test-local.sh | 7 +++---- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/cli/lucli/tests/specs/commands/CliHardenerS1S10Spec.cfc b/cli/lucli/tests/specs/commands/CliHardenerS1S10Spec.cfc index 2ae3c874e8..8fda00faed 100644 --- a/cli/lucli/tests/specs/commands/CliHardenerS1S10Spec.cfc +++ b/cli/lucli/tests/specs/commands/CliHardenerS1S10Spec.cfc @@ -145,11 +145,18 @@ component extends="wheels.wheelstest.system.BaseSpec" { it("wheels start stages 3.49.1.0; test-cli-local.sh downloads 3.49.1.0; CI pr.yml uses 3.50.3.0", () => { expect(moduleSrc).toInclude("sqlite-jdbc-3.49.1.0"); expect(find("3.50.3.0", moduleSrc)).toBe(0); + // Both download through tools/ci/fetch-sqlite-jdbc.sh, which defaults to the CI + // version; test-cli-local.sh passes 3.49.1.0 and its SHA-256 explicitly. + var fetcher = fileRead(variables.repoRoot & "tools/ci/fetch-sqlite-jdbc.sh"); + expect(fetcher).toInclude('VERSION="3.50.3.0"'); + expect(find("3.49.1.0", fetcher)).toBe(0); var sh = fileRead(cliLocalScript); - expect(sh).toInclude("sqlite-jdbc/3.49.1.0/sqlite-jdbc-3.49.1.0.jar"); + expect(sh).toInclude("tools/ci/fetch-sqlite-jdbc.sh"); + expect(sh).toInclude("sqlite-jdbc-3.49.1.0.jar"); + expect(reFind("fetch-sqlite-jdbc\.sh""[^\n]*\\\n\s*3\.49\.1\.0 [0-9a-f]{64}", sh)).toBeGT(0); expect(find("3.50.3.0", sh)).toBe(0); var pr = fileRead(prYml); - expect(pr).toInclude("sqlite-jdbc/3.50.3.0/sqlite-jdbc-3.50.3.0.jar"); + expect(reFind("bash tools/ci/fetch-sqlite-jdbc\.sh /tmp/sqlite-jdbc\.jar\s*\n", pr)).toBeGT(0); expect(find("3.49.1.0", pr)).toBe(0); }); diff --git a/tools/ci/fetch-sqlite-jdbc.sh b/tools/ci/fetch-sqlite-jdbc.sh index 375209e5f2..7584ff3114 100755 --- a/tools/ci/fetch-sqlite-jdbc.sh +++ b/tools/ci/fetch-sqlite-jdbc.sh @@ -8,13 +8,24 @@ # `curl --fail` and the SHA-256 check; a mismatch is downloaded again, up to # three attempts. # -# Usage: bash tools/ci/fetch-sqlite-jdbc.sh [output-path] (default /tmp/sqlite-jdbc.jar) +# Usage: bash tools/ci/fetch-sqlite-jdbc.sh [output-path [version sha256]] +# output-path defaults to /tmp/sqlite-jdbc.jar. Without a version, it fetches the +# 3.50.3.0 jar CI installs. A different version must come with its SHA-256. set -euo pipefail OUT="${1:-/tmp/sqlite-jdbc.jar}" -VERSION="3.50.3.0" -# Maven Central's published sqlite-jdbc-3.50.3.0.jar.sha256. -SHA256="a3f53a2aa15ae9425a9e793bbe9c8e5288febeb4b65ef5c1a4e80d4c2045cf08" +if [ "$#" -ge 2 ]; then + if [ "$#" -ne 3 ] || [ -z "$2" ] || [ -z "$3" ]; then + echo "::error::Usage: $0 [output-path [version sha256]]" >&2 + exit 2 + fi + VERSION="$2" + SHA256="$3" +else + VERSION="3.50.3.0" + # Maven Central's published sqlite-jdbc-3.50.3.0.jar.sha256. + SHA256="a3f53a2aa15ae9425a9e793bbe9c8e5288febeb4b65ef5c1a4e80d4c2045cf08" +fi URL="https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/${VERSION}/sqlite-jdbc-${VERSION}.jar" sha256_of() { diff --git a/tools/test-cli-e2e.sh b/tools/test-cli-e2e.sh index 41af4919ab..0e3708b324 100755 --- a/tools/test-cli-e2e.sh +++ b/tools/test-cli-e2e.sh @@ -508,8 +508,8 @@ sleep 1 LUCEE_LIB=$(find ~/.lucli/express -path "*/lib/ext" -type d 2>/dev/null | head -1) if [ -n "$LUCEE_LIB" ] && ! ls "$LUCEE_LIB"/sqlite-jdbc*.jar 1>/dev/null 2>&1; then echo " Downloading SQLite JDBC driver..." - curl -sL "https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.49.1.0/sqlite-jdbc-3.49.1.0.jar" \ - -o "$LUCEE_LIB/sqlite-jdbc-3.49.1.0.jar" + bash "$PROJECT_ROOT/tools/ci/fetch-sqlite-jdbc.sh" "$LUCEE_LIB/sqlite-jdbc-3.49.1.0.jar" \ + 3.49.1.0 5c8609d2ca341deb8c6f71778974b5ba4995c7d32d7c7c89d9392a3e72c39291 fi echo " Starting LuCLI server on port $PORT..." diff --git a/tools/test-cli-local.sh b/tools/test-cli-local.sh index 2b69c29a8b..20a813e414 100755 --- a/tools/test-cli-local.sh +++ b/tools/test-cli-local.sh @@ -271,8 +271,9 @@ else fi if [ -n "$LUCEE_LIB" ] && ! ls "$LUCEE_LIB"/sqlite-jdbc*.jar 1>/dev/null 2>&1; then echo "Downloading SQLite JDBC driver to $LUCEE_LIB..." - curl -sL "https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.49.1.0/sqlite-jdbc-3.49.1.0.jar" \ - -o "$LUCEE_LIB/sqlite-jdbc-3.49.1.0.jar" + # The CLI stages 3.49.1.0 (`wheels start`); pinned by SHA-256 like the release packages. + bash "$PROJECT_ROOT/tools/ci/fetch-sqlite-jdbc.sh" "$LUCEE_LIB/sqlite-jdbc-3.49.1.0.jar" \ + 3.49.1.0 5c8609d2ca341deb8c6f71778974b5ba4995c7d32d7c7c89d9392a3e72c39291 echo "Restarting ${CLI_BIN} server to pick up new JAR..." kill "$SERVER_PID" 2>/dev/null || true wait "$SERVER_PID" 2>/dev/null || true diff --git a/tools/test-local.sh b/tools/test-local.sh index 5ccc5a55ea..b86c4ed147 100755 --- a/tools/test-local.sh +++ b/tools/test-local.sh @@ -381,6 +381,7 @@ stop_test_server() { # pins, in the CLI home, not whichever install `find` lists first. EXPRESS_ROOT="$CLI_HOME/express" SQLITE_JDBC_VERSION="3.49.1.0" +SQLITE_JDBC_SHA256="5c8609d2ca341deb8c6f71778974b5ba4995c7d32d7c7c89d9392a3e72c39291" # The Lucee version a lucee.json pins: the first "version" inside its "lucee" block. pinned_lucee_version() { @@ -398,10 +399,8 @@ stage_sqlite_driver() { [ -n "$1" ] && [ -d "$lib" ] || return 1 ls "$lib"/sqlite-jdbc*.jar 1>/dev/null 2>&1 && return 1 echo "Downloading SQLite JDBC driver into Lucee Express $1..." - if ! curl -fsSL --retry 3 --retry-delay 2 \ - "https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/${SQLITE_JDBC_VERSION}/sqlite-jdbc-${SQLITE_JDBC_VERSION}.jar" \ - -o "$lib/sqlite-jdbc-${SQLITE_JDBC_VERSION}.jar"; then - rm -f "$lib/sqlite-jdbc-${SQLITE_JDBC_VERSION}.jar" + if ! bash "$PROJECT_ROOT/tools/ci/fetch-sqlite-jdbc.sh" "$lib/sqlite-jdbc-${SQLITE_JDBC_VERSION}.jar" \ + "$SQLITE_JDBC_VERSION" "$SQLITE_JDBC_SHA256"; then echo "::warning::Could not download the SQLite JDBC driver into $lib" >&2 return 2 fi