From ca38917cb93a75d60e1ecac5861846b4f5281a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 22 Jul 2026 19:33:12 +0200 Subject: [PATCH 1/2] ci: replace pull_request_target fork CI with pull_request + public rotated token actions/checkout v4.4.0+ refuses to check out fork PR code from pull_request_target workflows (pwn-request hardening). Instead of opting into the unsafe checkout, drop the privileged fork workflow entirely: fork PRs now run the regular pull_request CI, falling back to a public rotatable Mapbox token fetched from rnmapbox/ci-config when secrets are unavailable. The Mapbox SDK download no longer requires a secret token, so the only token needed is the semi-public runtime access token. --- .github/workflows/android-actions.yml | 19 ++++++++----------- .github/workflows/ci-for-forked-repos.yml | 16 ---------------- .github/workflows/ci-requiring-tokens.yml | 6 ------ .github/workflows/ios-actions.yml | 19 +++++++------------ .github/workflows/on-push.yml | 4 +++- .github/workflows/web-actions.yml | 19 ++++++++----------- 6 files changed, 26 insertions(+), 57 deletions(-) delete mode 100644 .github/workflows/ci-for-forked-repos.yml diff --git a/.github/workflows/android-actions.yml b/.github/workflows/android-actions.yml index 68b0c151d0..e173bb4461 100644 --- a/.github/workflows/android-actions.yml +++ b/.github/workflows/android-actions.yml @@ -7,9 +7,6 @@ on: required: true default: default type: string - ref: - required: false - type: string NVMRC: required: true type: string @@ -37,13 +34,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - if: ${{ inputs.ref == '' }} - - - name: Checkout fork - uses: actions/checkout@v4 - if: ${{ inputs.ref != '' }} - with: - ref: ${{ inputs.ref }} - name: Setup node ${{ inputs.NVMRC }} uses: actions/setup-node@v3.5.1 @@ -57,7 +47,14 @@ jobs: java-version: '17' - - run: echo $MAPBOX_ACCESS_TOKEN > ./accesstoken + # Fork PRs get no secrets; fall back to the public rotated CI token (see rnmapbox/ci-config) + - name: Access token + run: | + if [ -n "$MAPBOX_ACCESS_TOKEN" ]; then + echo "$MAPBOX_ACCESS_TOKEN" > ./accesstoken + else + curl -fsSL https://raw.githubusercontent.com/rnmapbox/ci-config/main/mapbox-ci-token > ./accesstoken + fi working-directory: example env: MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN || secrets.ENV_MAPBOX_ACCESS_TOKEN }} diff --git a/.github/workflows/ci-for-forked-repos.yml b/.github/workflows/ci-for-forked-repos.yml deleted file mode 100644 index 16bf1149f4..0000000000 --- a/.github/workflows/ci-for-forked-repos.yml +++ /dev/null @@ -1,16 +0,0 @@ -on: - pull_request_target: - branches: [ main ] - -jobs: - call_ci_requiring_tokens: - if: github.repository == 'rnmapbox/maps' && github.event.pull_request.head.repo.fork == true - name: "CI requiring tokens" - uses: ./.github/workflows/ci-requiring-tokens.yml - with: - NVMRC: v22.16.0 - env_name: CI with Mapbox Tokens - ref: ${{ github.event.pull_request.head.sha }} - secrets: - MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} - diff --git a/.github/workflows/ci-requiring-tokens.yml b/.github/workflows/ci-requiring-tokens.yml index 118c38fabe..0625902fcc 100644 --- a/.github/workflows/ci-requiring-tokens.yml +++ b/.github/workflows/ci-requiring-tokens.yml @@ -10,9 +10,6 @@ on: required: false type: string default: default - ref: - required: false - type: string secrets: MAPBOX_ACCESS_TOKEN: required: false @@ -32,7 +29,6 @@ jobs: uses: ./.github/workflows/android-actions.yml with: env_name: ${{ inputs.env_name }} - ref: ${{ inputs.ref }} NVMRC: ${{ inputs.NVMRC }} MAP_IMPL: mapbox11 NEW_ARCH: true @@ -45,7 +41,6 @@ jobs: uses: ./.github/workflows/ios-actions.yml with: env_name: ${{ inputs.env_name }} - ref: ${{ inputs.ref }} NVMRC: ${{ inputs.NVMRC }} MAP_IMPL: mapbox11 NEW_ARCH: true @@ -58,7 +53,6 @@ jobs: uses: ./.github/workflows/web-actions.yml with: env_name: ${{ inputs.env_name }} - ref: ${{ inputs.ref }} NVMRC: ${{ inputs.NVMRC }} secrets: MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} diff --git a/.github/workflows/ios-actions.yml b/.github/workflows/ios-actions.yml index 49fe171cc7..72b47732cb 100644 --- a/.github/workflows/ios-actions.yml +++ b/.github/workflows/ios-actions.yml @@ -7,9 +7,6 @@ on: required: true default: default type: string - ref: - required: false - type: string NVMRC: required: true type: string @@ -43,20 +40,18 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - if: ${{ inputs.ref == '' }} - - - name: Checkout fork - uses: actions/checkout@v4 - if: ${{ inputs.ref != '' }} - with: - ref: ${{ inputs.ref }} + # Fork PRs get no secrets; fall back to the public rotated CI token (see rnmapbox/ci-config) - name: Access Token - run: echo $MAPBOX_ACCESS_TOKEN > ./accesstoken + run: | + if [ -n "$MAPBOX_ACCESS_TOKEN" ]; then + echo "$MAPBOX_ACCESS_TOKEN" > ./accesstoken + else + curl -fsSL https://raw.githubusercontent.com/rnmapbox/ci-config/main/mapbox-ci-token > ./accesstoken + fi env: MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN || secrets.ENV_MAPBOX_ACCESS_TOKEN }} - - name: Setup node ${{ inputs.NVMRC }} uses: actions/setup-node@v3.5.1 with: diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml index bcb1b73e4d..1b306c88da 100644 --- a/.github/workflows/on-push.yml +++ b/.github/workflows/on-push.yml @@ -74,7 +74,9 @@ jobs: uses: ./.github/workflows/ci-requiring-tokens.yml with: NVMRC: ${{ needs.lint_test_generate.outputs.NVMRC }} - if: needs.has_mapbox_token.outputs.has-mapbox-token == 'true' + # Fork PRs have no secrets but can still build with the public rotated CI token, + # so always run in rnmapbox/maps; forks of the repo still need their own token. + if: needs.has_mapbox_token.outputs.has-mapbox-token == 'true' || github.repository == 'rnmapbox/maps' secrets: MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }} diff --git a/.github/workflows/web-actions.yml b/.github/workflows/web-actions.yml index b836f28041..12762e2146 100644 --- a/.github/workflows/web-actions.yml +++ b/.github/workflows/web-actions.yml @@ -7,9 +7,6 @@ on: required: true default: default type: string - ref: - required: false - type: string NVMRC: required: true type: string @@ -27,20 +24,20 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - if: ${{ inputs.ref == '' }} - - - name: Checkout fork - uses: actions/checkout@v4 - if: ${{ inputs.ref != '' }} - with: - ref: ${{ inputs.ref }} - name: Setup node ${{ inputs.NVMRC }} uses: actions/setup-node@v3.5.1 with: node-version: ${{ inputs.NVMRC }} - - run: echo $MAPBOX_ACCESS_TOKEN > ./accesstoken + # Fork PRs get no secrets; fall back to the public rotated CI token (see rnmapbox/ci-config) + - name: Access token + run: | + if [ -n "$MAPBOX_ACCESS_TOKEN" ]; then + echo "$MAPBOX_ACCESS_TOKEN" > ./accesstoken + else + curl -fsSL https://raw.githubusercontent.com/rnmapbox/ci-config/main/mapbox-ci-token > ./accesstoken + fi working-directory: example env: MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN || secrets.ENV_MAPBOX_ACCESS_TOKEN }} From 1ce80da4647c10bc34d3648e2d7486dd16c175bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 22 Jul 2026 19:38:35 +0200 Subject: [PATCH 2/2] ci: fetch fork-PR Mapbox token via obfuscated rotating blob from rnmapbox/ci-config The token is public by design (any fork PR could print it); obfuscation only keeps it out of secret scanners. Rotation 2x/week in ci-config is the actual defense. Obfuscation key is versioned (v1) so it can be rotated by bumping the URL/key in scripts/ci/fetch-mapbox-token.sh. --- .github/workflows/android-actions.yml | 2 +- .github/workflows/ios-actions.yml | 2 +- .github/workflows/web-actions.yml | 2 +- scripts/ci/fetch-mapbox-token.sh | 11 +++++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100755 scripts/ci/fetch-mapbox-token.sh diff --git a/.github/workflows/android-actions.yml b/.github/workflows/android-actions.yml index e173bb4461..324882a58c 100644 --- a/.github/workflows/android-actions.yml +++ b/.github/workflows/android-actions.yml @@ -53,7 +53,7 @@ jobs: if [ -n "$MAPBOX_ACCESS_TOKEN" ]; then echo "$MAPBOX_ACCESS_TOKEN" > ./accesstoken else - curl -fsSL https://raw.githubusercontent.com/rnmapbox/ci-config/main/mapbox-ci-token > ./accesstoken + "$GITHUB_WORKSPACE/scripts/ci/fetch-mapbox-token.sh" > ./accesstoken fi working-directory: example env: diff --git a/.github/workflows/ios-actions.yml b/.github/workflows/ios-actions.yml index 72b47732cb..86d044a2ba 100644 --- a/.github/workflows/ios-actions.yml +++ b/.github/workflows/ios-actions.yml @@ -47,7 +47,7 @@ jobs: if [ -n "$MAPBOX_ACCESS_TOKEN" ]; then echo "$MAPBOX_ACCESS_TOKEN" > ./accesstoken else - curl -fsSL https://raw.githubusercontent.com/rnmapbox/ci-config/main/mapbox-ci-token > ./accesstoken + "$GITHUB_WORKSPACE/scripts/ci/fetch-mapbox-token.sh" > ./accesstoken fi env: MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN || secrets.ENV_MAPBOX_ACCESS_TOKEN }} diff --git a/.github/workflows/web-actions.yml b/.github/workflows/web-actions.yml index 12762e2146..e6d9a87c0b 100644 --- a/.github/workflows/web-actions.yml +++ b/.github/workflows/web-actions.yml @@ -36,7 +36,7 @@ jobs: if [ -n "$MAPBOX_ACCESS_TOKEN" ]; then echo "$MAPBOX_ACCESS_TOKEN" > ./accesstoken else - curl -fsSL https://raw.githubusercontent.com/rnmapbox/ci-config/main/mapbox-ci-token > ./accesstoken + "$GITHUB_WORKSPACE/scripts/ci/fetch-mapbox-token.sh" > ./accesstoken fi working-directory: example env: diff --git a/scripts/ci/fetch-mapbox-token.sh b/scripts/ci/fetch-mapbox-token.sh new file mode 100755 index 0000000000..faf22f52a3 --- /dev/null +++ b/scripts/ci/fetch-mapbox-token.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Fetches the Mapbox access token used by CI for fork PRs (no secrets there). +# The token is public by design — obfuscation only defeats token scrapers / +# secret scanning; rotation (2x/week, see rnmapbox/ci-config) is the real defense. +# -md sha256 instead of -pbkdf2 for LibreSSL compat on macOS runners. +set -eu + +URL="https://raw.githubusercontent.com/rnmapbox/ci-config/main/mapbox-ci-token.v1.enc" +OBFUSCATION_KEY="93b0fa94d67b3e1a5f2e59d920bc4854" + +curl -fsSL "$URL" | openssl enc -d -aes-256-cbc -base64 -A -md sha256 -k "$OBFUSCATION_KEY"