From e7f883f08be41015b1fff97fa0ba58448548610d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Jul 2026 02:11:56 +0000 Subject: [PATCH] fix(hooks): seed Gradle distribution from a verified mirror in web sandbox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Code Web routes github.com through a git-only proxy, so the Gradle wrapper's distributionUrl (services.gradle.org, which 307-redirects to a github.com release asset) can't bootstrap — it 403s even at Full network access, and `./gradlew` fails before running any task. Seed the pinned distribution in the existing web-only SessionStart hook, reusing the same idempotent curl-download pattern already used there for the Android SDK and Kotlin/Native deps: skip if already installed, else fetch Gradle's OFFICIAL sha256 (served from services.gradle.org, reachable here), download the zip from a mirror, and verify before extracting so a tampered or wrong mirror file is rejected and never executed. The wrapper cache dir is derived as base36(md5(distributionUrl)) so it survives version bumps. Also pin distributionSha256Sum in gradle-wrapper.properties as defense in depth: Gradle then verifies any distribution it installs (mirror-seeded, CI, or local) against the known-good hash. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01A7bocRTSPyXwz6zVMzDbKR --- .claude/hooks/session-start.sh | 54 ++++++++++++++++++++++++ gradle/wrapper/gradle-wrapper.properties | 1 + 2 files changed, 55 insertions(+) diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh index d2e869fb85..88023f0909 100755 --- a/.claude/hooks/session-start.sh +++ b/.claude/hooks/session-start.sh @@ -211,6 +211,60 @@ install_konan_dep "llvm-19-x86_64-linux-essentials-109" \ install_konan_dep "libffi-3.2.1-2-linux-x86-64" \ "$KONAN_DEPS_URL/libffi-3.2.1-2-linux-x86-64.tar.gz" +# --- Gradle distribution: pre-seed the wrapper distribution --- +# The wrapper's distributionUrl (services.gradle.org) 307-redirects to +# github.com release assets, which the web sandbox's git-only GitHub proxy +# blocks (403) even at Full network access — so `./gradlew` can't bootstrap. +# Download the pinned distribution from a mirror instead, but verify it against +# Gradle's OFFICIAL sha256 (served from services.gradle.org, reachable here) +# so a tampered/wrong mirror file is rejected and never executed. Idempotent: +# skips entirely if the distribution is already installed. +seed_gradle_distribution() { + local props="$CLAUDE_PROJECT_DIR/gradle/wrapper/gradle-wrapper.properties" + [ -f "$props" ] || return 0 + local url zip name hash dir ver official mirror ok="" + url=$(sed -n 's/^distributionUrl=//p' "$props" | sed 's/\\//g') + [ -n "$url" ] || return 0 + zip=${url##*/}; name=${zip%.zip} + # Gradle stores the dist under base36(md5(distributionUrl)) — derive it so this + # keeps working across version bumps instead of hardcoding the hash dir. + hash=$(python3 - "$url" <<'PY' +import hashlib, sys +n = int.from_bytes(hashlib.md5(sys.argv[1].encode()).digest(), 'big') +d = "0123456789abcdefghijklmnopqrstuvwxyz"; s = "" +while n: + s = d[n % 36] + s; n //= 36 +print(s or "0") +PY +) + dir="${GRADLE_USER_HOME:-$HOME/.gradle}/wrapper/dists/$name/$hash" + ver=${name%-bin}; ver=${ver%-all} + if [ -x "$dir/$ver/bin/gradle" ]; then return 0; fi # already installed + echo "Seeding Gradle distribution $ver (github release blocked; using verified mirror)..." >&2 + mkdir -p "$dir" + official=$(curl -fsSL "https://services.gradle.org/distributions/${zip}.sha256") || { + echo "Could not fetch official Gradle checksum; leaving gradlew to fail as before." >&2 + return 0 + } + for mirror in \ + "https://mirrors.cloud.tencent.com/gradle" \ + "https://mirrors.huaweicloud.com/gradle"; do + if curl -fsSL -o "$dir/$zip" "$mirror/$zip" \ + && echo "${official} $dir/$zip" | sha256sum -c - >/dev/null 2>&1; then + ok=1; break + fi + echo "Mirror $mirror failed download/verify; trying next." >&2 + rm -f "$dir/$zip" + done + if [ -z "$ok" ]; then + echo "Gradle seed failed against all mirrors; leaving gradlew to fail as before." >&2 + return 0 + fi + unzip -q "$dir/$zip" -d "$dir" && touch "$dir/$zip.ok" + echo "Gradle $ver seeded and verified against official sha256." >&2 +} +seed_gradle_distribution + cd "$CLAUDE_PROJECT_DIR" ./gradlew --version > /dev/null 2>&1 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f36e9b504c..ad6fcfb8bd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ #Wed Jan 04 09:23:50 EST 2023 distributionBase=GRADLE_USER_HOME distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip +distributionSha256Sum=553c78f50dafcd54d65b9a444649057857469edf836431389695608536d6b746 distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME