Any build of main installed by a route other than Play cannot install a
toolchain. It fails before a single byte of the payload moves, and the log line
is the one written for a tampered download.
Why
ToolchainManager.downloadViaHttp() calls publishedDigestFor() before the
transfer (ToolchainManager.kt:699). That builds the manifest URL beside the
ZIP (manifestUrlFor, :1577, with MANIFEST_NAME = "toolchains.sha256" at
:1548) and, when the manifest does not name the ZIP, throws rather than
falling back (:922-925). Failing closed there is deliberate and correct.
releases/latest currently resolves to v1.0.0, and that release predates the
digest manifest: its assets are the APK, checksums.sha256 and the three
toolchain ZIPs, with no toolchains.sha256. Measured:
releases/latest/download/toolchain_go.zip -> 200
releases/latest/download/toolchains.sha256 -> 404
The guarantee written at ToolchainManager.kt:907-909, that a release cannot
reach this state unnoticed, holds for releases the current release.yml
produces: release.yml requires toolchain-zips/toolchains.sha256 among its
artifacts and fails if a packaged ZIP is absent from it. What it cannot cover is
which release the latest pointer names, and that pointer can name a release
built before the requirement existed.
Who is affected
Only builds newer than the commit that introduced the check, which is not an
ancestor of v1.0.0. Installed v1.0.0 apps never request the manifest, so they
are unaffected and do install toolchains today.
That leaves developers running a debug APK or a sideloaded build of main,
which CONTRIBUTING.md treats as the normal way to work on this project.
Resolution
Publishing 1.1.0 fixes it: that release carries toolchains.sha256 and takes
the latest pointer. Deferred until then rather than editing a historical
release.
Worth considering separately, because the same pointer is read twice with a
gap: manifestUrlFor's own docstring (:1570-1575) notes that the manifest and
the payload are fetched through releases/latest/download/ at different
moments, so a release published in between yields a digest and a payload from
two different releases. It asks for the redirect chain to be measured against a
real release before anything is built on it. That measurement now exists:
releases/latest -> 302 -> releases/tag/<tag>
releases/latest/download/<asset> -> 302 -> releases/download/<tag>/<asset>
both with cache-control: no-cache, and with per-asset-path edge caching that
can serve a stale mapping for a path recently requested. Resolving latest to a
concrete tag once and building both URLs from it is therefore implementable.
Any build of
maininstalled by a route other than Play cannot install atoolchain. It fails before a single byte of the payload moves, and the log line
is the one written for a tampered download.
Why
ToolchainManager.downloadViaHttp()callspublishedDigestFor()before thetransfer (
ToolchainManager.kt:699). That builds the manifest URL beside theZIP (
manifestUrlFor,:1577, withMANIFEST_NAME = "toolchains.sha256"at:1548) and, when the manifest does not name the ZIP, throws rather thanfalling back (
:922-925). Failing closed there is deliberate and correct.releases/latestcurrently resolves to v1.0.0, and that release predates thedigest manifest: its assets are the APK,
checksums.sha256and the threetoolchain ZIPs, with no
toolchains.sha256. Measured:The guarantee written at
ToolchainManager.kt:907-909, that a release cannotreach this state unnoticed, holds for releases the current
release.ymlproduces:
release.ymlrequirestoolchain-zips/toolchains.sha256among itsartifacts and fails if a packaged ZIP is absent from it. What it cannot cover is
which release the
latestpointer names, and that pointer can name a releasebuilt before the requirement existed.
Who is affected
Only builds newer than the commit that introduced the check, which is not an
ancestor of v1.0.0. Installed v1.0.0 apps never request the manifest, so they
are unaffected and do install toolchains today.
That leaves developers running a debug APK or a sideloaded build of
main,which
CONTRIBUTING.mdtreats as the normal way to work on this project.Resolution
Publishing 1.1.0 fixes it: that release carries
toolchains.sha256and takesthe
latestpointer. Deferred until then rather than editing a historicalrelease.
Worth considering separately, because the same pointer is read twice with a
gap:
manifestUrlFor's own docstring (:1570-1575) notes that the manifest andthe payload are fetched through
releases/latest/download/at differentmoments, so a release published in between yields a digest and a payload from
two different releases. It asks for the redirect chain to be measured against a
real release before anything is built on it. That measurement now exists:
both with
cache-control: no-cache, and with per-asset-path edge caching thatcan serve a stale mapping for a path recently requested. Resolving
latestto aconcrete tag once and building both URLs from it is therefore implementable.