Infrastructure worth keeping — and kept patched.

Can someone poison our shared build cache?

Anyone who holds the write login can change what every other build restores. FosterStack Cache does not stop that holder. It lets you give everyone else a read-only login, and in our runs the server refused their writes.

The short answer

The server has two logins. The write login lets a client store and replace entries. The read-only login lets a client only read them.

Is a remote build cache safe? Who can write to it

Two kinds of failure, as background and not a test result:

What to do

Our advice, not a test result:

FosterStack Cache has one write login and one read-only login. There is no per-person identity: a write login is shared by whoever holds it, so the login alone does not say which writer did what. We did not test whether the server records who wrote an entry.

How we checked

We ran a real FosterStack Cache server with a write login and a read-only login. We built a project, then tried each login against the stored entries with plain HTTP requests.

WhoWhat they didWhat happened
Read-only loginFresh build, nothing changedAll four compile tasks came from the cache
Write loginPut a different valid entry over the core module's entryHTTP 201, entry replaced
Read-only loginFresh build from the same untouched sourceCore came from the cache. Its class file held text that is not in the source
Write loginPut 200 random bytes over another entryHTTP 201. The next build failed: Not in GZIP format
Read-only loginPut bytes over an entry, and over a new keyHTTP 403 both times. Entry unchanged, nothing stored
Read-only loginDelete an entry, and read oneDelete: HTTP 403, entry still there. Read: HTTP 200
No loginPut, and readHTTP 401 both times. A wrong password: HTTP 401
Write login, isPush = falseBuild with a changed fileNothing uploaded. Then a direct put of a new key: HTTP 201
Read-only login, isPush = trueBuild with a changed fileBuild succeeded. The upload got a 403 and nothing was stored. Gradle then switched the remote cache off for the rest of that build (see can feature branches share the build cache with main)

What the replaced entry did: the project's source says core-1-. The class file restored from the replaced entry contained core-1-TAMPERED-. Gradle did not complain.

What the damaged entry did: Gradle stopped with Failed to load cache entry ... Could not load from remote cache: Not in GZIP format. It did not skip the entry and compile instead.

Setup: FosterStack Cache 0.2.1 and Gradle 9.8.0 on a Mac (arm64), Temurin 21.0.12.1, all on one machine over plain HTTP on the loopback address. The project had four modules, 12 small classes each. Every build used a fresh copy of the project and a new empty Gradle home, with Gradle's local cache off, so only the server could supply results.

Limits: one server and one small project. Gradle only, not Maven. No HTTPS. We tested what each login can do to an entry. We did not test how anyone would get a login.

Related: security policy.