Safeguard
Developer Tools•

What to cache in mobile CI, and what must never be cached

Cache Gradle, CocoaPods and Swift package downloads to speed builds, but never keystores, API keys or profiles. A safe split, with reasons for each.

Safeguard Security Team
4 min read

Cache downloaded dependencies and build outputs, such as Gradle and package manager downloads. Never cache keystores, certificates, API keys, service account files or anything decoded from a secret. Anyone who can read a cache can read what is in it, and pull requests can often read it.

The platform warning

The caching documentation of a hosted CI service is blunt: it recommends that you do not store sensitive information, such as access tokens or login credentials, in files in the cache path. It explains why: anyone with read access can create a pull request on the repository and access the contents of a cache, and forks can create pull requests on the base branch and access caches on that branch.

So a cache is a shared surface, closer to a public folder than a private vault.

A safe split for mobile projects

ItemCache it?Reason
Gradle dependency downloadsYesPublic artifacts, safe to rebuild
Gradle build cache outputsYes, with care about who writesOutputs can be trusted only if writers are trusted
CocoaPods downloadsYesReproducible from the lockfile
Swift package checkoutsYesReproducible from the resolved file
Ruby gems for the build toolYesPinned by a lockfile
Android keystore, .p12, .p8NeverSigning identity
Play service account JSONNeverPublishing credential
Decoded provisioning profilesNeverCreated from secrets at runtime
google-services style config with private valuesReview firstDepends on what it holds

Where cache directories overlap with secrets

The danger is accidental. A cache path set to a broad folder, such as the whole home directory or the workspace root, can sweep in a decoded keystore written there earlier in the job. Prefer narrow, named cache paths: the Gradle user home download area, the Pods folder, the package manager's cache folder.

Also decode secrets into the runner's temporary directory, not the workspace, and delete them at the end of the job.

Who may write the cache

Gradle's own documentation notes that the local build cache has push enabled by default, while a remote build cache has push disabled by default. For remote caches, the safe pattern is to let a trusted job on the default branch write and let other builds only read. The hosted CI documentation says the same for its cache: low-trust triggers get read-only access by default, and the guidance is to have a trusted, push-triggered workflow maintain the cache.

If an attacker can write a poisoned cache that a release job later restores, your release job executes their content. See the post on cache keys for mobile builds for how keys affect this.

After a compromise

Purge caches and rebuild from lockfiles. A cache entry that existed before the incident may not be trustworthy afterwards.

Try it yourself

Open the demo to see how a dependency inventory is produced from a project. Look at how many components appear that no one chose directly, because those are what a cache silently carries.

Click through it at your own pace, or open it full screen.

Where Safeguard fits

Safeguard provides SBOM and dependency scanning for mobile projects and can gate builds with policies. Start with a free scan.

Quick check

Is a private repository cache private to the team?

Not in the sense people assume. Anyone who can open a pull request may be able to read it, so keep secrets out.

Can I cache the Pods folder?

Yes for speed, ideally keyed to the lockfile, and never with anything secret in the same path.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.