CI: fork PRs run untrusted code on the self-hosted runner, and the SDK download is unverified #12

Open
opened 2026-09-02 11:04:59 +02:00 by julian · 0 comments
Owner

Split out of the #9 review so it does not become a scope grab there. Both landed in #6; neither is introduced by #9, which only edits comments in the same file.

1. pull_request on a self-hosted runner from a public repo

.forgejo/workflows/pr-checks.yml triggers on pull_request, runs on cth-ubuntu-latest (self-hosted), and executes ./gradlew from the PR head. This repo is public, so a PR opened from a fork gets its own build.gradle.kts, settings.gradle.kts, wrapper jar and task graph executed on the runner. The runner's filesystem, its Gradle and SDK caches, and its network position are all in reach of whatever that branch configures.

The keystore is genuinely not in this workflow — that separation was deliberate in #6 and it holds. What is exposed is the runner, not release signing.

Options, roughly in order of bluntness:

  • gate fork PRs behind a manual approval before any job runs, or
  • move this workflow to an ephemeral / disposable runner, or
  • accept it explicitly, in writing, on the grounds that the repo has no outside contributors today.

The last is a real option — it just deserves to be a decision rather than an oversight. The trigger's own comment currently reasons about a future default change widening the trigger while this goes unmentioned.

2. The cmdline-tools zip has no checksum

wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O tools.zip
unzip -q tools.zip

No signature check, no sha256sum -c. The URL is already version-pinned, so pinning the hash beside it is a one-line change. Whatever the archive contains becomes the build toolchain, and the result is cached at /opt/android-sdk under a key that does not cover the archive's content.

The same two lines exist in build.yml, which does touch the keystore.

Split out of the #9 review so it does not become a scope grab there. Both landed in #6; neither is introduced by #9, which only edits comments in the same file. ## 1. `pull_request` on a self-hosted runner from a public repo `.forgejo/workflows/pr-checks.yml` triggers on `pull_request`, runs on `cth-ubuntu-latest` (self-hosted), and executes `./gradlew` from the PR head. This repo is public, so a PR opened from a fork gets its own `build.gradle.kts`, `settings.gradle.kts`, wrapper jar and task graph executed on the runner. The runner's filesystem, its Gradle and SDK caches, and its network position are all in reach of whatever that branch configures. The keystore is genuinely not in this workflow — that separation was deliberate in #6 and it holds. What is exposed is the runner, not release signing. Options, roughly in order of bluntness: - gate fork PRs behind a manual approval before any job runs, or - move this workflow to an ephemeral / disposable runner, or - accept it explicitly, in writing, on the grounds that the repo has no outside contributors today. The last is a real option — it just deserves to be a decision rather than an oversight. The trigger's own comment currently reasons about a *future* default change widening the trigger while this goes unmentioned. ## 2. The cmdline-tools zip has no checksum ```sh wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O tools.zip unzip -q tools.zip ``` No signature check, no `sha256sum -c`. The URL is already version-pinned, so pinning the hash beside it is a one-line change. Whatever the archive contains becomes the build toolchain, and the result is cached at `/opt/android-sdk` under a key that does not cover the archive's content. The same two lines exist in `build.yml`, which does touch the keystore.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
OpsDeck/mobile#12
No description provided.