Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false

# The project toolchain (rust-toolchain.toml = 1.96) plus the two shipped
# Android targets (arm64 ships; x86_64 is the emulator/CI ABI).
Expand Down Expand Up @@ -105,6 +107,8 @@ jobs:
needs: cross-build
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Install Android Rust targets
run: rustup target add aarch64-linux-android x86_64-linux-android
- uses: Swatinem/rust-cache@v2
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,19 @@ jobs:
# the base commit. (On `pull_request` it uses the GitHub API and needs no
# history.) Without this the push-event filter falls back to "everything
# changed" — safe, but it loses the docs-only skip on direct main pushes.
#
# This is the one job in the credential-hardening sweep (#318) where the
# change touches the git layer at all: paths-filter reads local git on
# `push` events. It needs no authentication — `fetch-depth: 0` means the
# base commit is already local, and the `pull_request` path goes through
# the GitHub API with its own token — so dropping the persisted credential
# is expected to be inert. Failure mode if that is ever wrong is benign
# (the filter reports "everything changed" and the full suite runs), but
# it would silently cost the docs-only skip the comment above describes.
- uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false
- uses: dorny/paths-filter@v4
id: filter
with:
Expand Down Expand Up @@ -168,6 +178,8 @@ jobs:
RUSTDOCFLAGS: -D warnings
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: ./.github/actions/rust-setup
with:
toolchain: 1.96.0
Expand Down Expand Up @@ -219,6 +231,8 @@ jobs:
os: ${{ fromJSON(needs.setup.outputs.os) }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: ./.github/actions/rust-setup
with:
cache-key: test
Expand Down Expand Up @@ -246,6 +260,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: ./.github/actions/rust-setup
with:
cache-key: test-roms
Expand All @@ -270,6 +286,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: ./.github/actions/rust-setup
with:
targets: thumbv7em-none-eabihf
Expand All @@ -288,6 +306,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: ./.github/actions/rust-setup
with:
targets: wasm32-unknown-unknown
Expand Down Expand Up @@ -402,6 +422,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: ./.github/actions/rust-setup
with:
apt: "false"
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ jobs:
if: ${{ github.event_name != 'schedule' || vars.IOS_SIGNING_READY == 'true' }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false

# v2.0.7 "Trim" — App Store submission floor. From 2026-04-28 every App Store
# Connect upload must be built with the iOS 26 SDK (Xcode 26). Select the newest
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/pgo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ jobs:
speedup_pct: ${{ steps.gate.outputs.speedup_pct }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false

# Pinned to the project toolchain (rust-toolchain.toml = 1.96.0), matching
# the other jobs. PGO needs the llvm-tools-preview component (profdata
Expand Down Expand Up @@ -193,6 +195,8 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: ./.github/actions/rust-setup
with:
toolchain: 1.96.0
Expand Down
32 changes: 30 additions & 2 deletions .github/workflows/release-auto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,37 @@ jobs:
- uses: actions/checkout@v7
with:
# Build the release from the exact commit CI went green on. A shallow
# checkout suffices: we read Cargo.toml + CHANGELOG.md, and the tag
# existence check uses `git ls-remote` (no local tag history needed).
# checkout suffices: we only read plain text files (Cargo.toml,
# CHANGELOG.md, and the optional `.github/release-notes/vX.Y.Z.md`
# override), and the tag existence check uses `git ls-remote` (no
# local tag history needed).
ref: ${{ github.event.workflow_run.head_sha }}
#
# DELIBERATELY the one checkout in this repo that KEEPS persisted Git
# credentials (every other one sets `persist-credentials: false`; see
# issue #318). The `git ls-remote --tags origin` below is the only
# operation in this repo that needs THIS checkout's `origin`
# credential. (It is not the only Git-over-network call in `.github/`
# — `fastlane match` in `ios.yml` clones the signing repository — but
# that one authenticates with its own `MATCH_GIT_*` secrets against a
# different remote, so it is unaffected by this setting.) This job is
# also unreachable from untrusted input — `workflow_run`, further
# gated to `event == 'push'` above — and executes no repository code
# at all: it only reads the text files named above. So hardening buys
# nothing here while risking a release path that is only exercised at
# a version cut.
#
# If a future sweep wants uniformity, first replace the `ls-remote`
# with `gh api repos/$GITHUB_REPOSITORY/git/ref/tags/$tag` (which uses
# GH_TOKEN and is unaffected by credential persistence), THEN drop the
# credentials. That swap also fixes a latent bug: the `>/dev/null 2>&1`
# below makes a transient network/auth failure indistinguishable from
# "tag does not exist", so a blip sends the workflow down the
# should_release=true path for a version that is already tagged.
# `gh release create` then either fails outright (if a release already
# exists for that tag) or publishes a release against the pre-existing
# tag — noisy or wrong, but not silently duplicated. Fail-closed
# handling belongs with that change, not here.

- name: Decide whether a new version needs releasing
id: decide
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ jobs:
bin_name: rustynes.exe
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false

# Shared toolchain + Linux deps (no-op on macOS/Windows) + cargo cache.
# The per-target cache key keeps each platform's artifacts separate.
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
# Install the PREBUILT binary, never `cargo install` (build-from-source):
# the repo pins rustc 1.96 (rust-toolchain.toml), but the current
# cargo-audit release needs >= 1.88 to COMPILE. The prebuilt binary RUNS
Expand All @@ -68,6 +70,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
# Prebuilt binary for the same reason as the audit job: the latest
# cargo-deny needs rustc >= 1.88 to build from source, while the repo is
# pinned to 1.96. Policy lives in `deny.toml`.
Expand All @@ -81,6 +85,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: ./.github/actions/rust-setup
with:
toolchain: 1.96.0
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false

# Shared toolchain + cargo cache. `apt: true` because the rustdoc build
# compiles the whole workspace (incl. rustynes-frontend) for the host,
Expand Down
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,38 @@ cycle-accurate core later replaced.
`get_fastforwarding`-gated audio-push skip during RetroArch's
fast-forward/rollback-netplay catch-up path.

### Security

- **`persist-credentials: false` on every CI checkout that compiles or runs
repository code (closes #318).** `actions/checkout` defaults to writing the
workflow `GITHUB_TOKEN` into `.git/config`, where any code the job then
executes from the checkout — Cargo build scripts, proc macros, test
binaries, Gradle build scripts, `scripts/*.sh`, the MkDocs build — can read
it. On a pull request that tree is by definition unreviewed code, and nearly
every CI job compiles or runs it — the exceptions being the `audit` / `deny`
jobs, which install prebuilt binaries and only parse `Cargo.lock`. Applied to
**18 of the 19** checkouts in
`.github/`. Highest-exposure site was not a `ci.yml` job but `web.yml`'s
`build`, whose workflow-level `permissions:` grant `pages: write` +
`id-token: write` and which is PR-reachable while running `trunk`,
`cargo doc`, `pip install`, and `mkdocs build`.
Audited rather than applied blanket: `.github/actions/rust-setup` performs
no checkout of its own, so call-site hardening is complete coverage; and
**every hardened job was confirmed not to need the checkout credential** —
nothing pushes commits, tags, or branches, there are no submodules, GitHub
Pages uses the OIDC flow (not a `gh-pages` push), and `gh release create` /
`softprops/action-gh-release` authenticate by API token while
`fastlane match` clones a different remote with its own `MATCH_GIT_*`
credentials. The single deliberate
exception is `release-auto.yml`'s `prepare`, the one job whose work needs
that credential (`git ls-remote --tags origin`); it is
unreachable from untrusted input and executes no repository code, so
hardening would buy nothing while risking a release path exercised only at a
version cut. Its checkout now carries an inline comment recording that, plus
the safe migration order for anyone wanting uniformity later. Purely
additive CI configuration — no source, build output, or emulation behavior
changes.

### Changed

- **Dependency version-bump consolidation (closes Dependabot #313–#315).**
Expand Down