diff --git a/.github/workflows/probe-cross-repo-token.yml b/.github/workflows/probe-cross-repo-token.yml new file mode 100644 index 0000000..e8e48f3 --- /dev/null +++ b/.github/workflows/probe-cross-repo-token.yml @@ -0,0 +1,163 @@ +# Throwaway probe (#238 Phase 0): can a workflow GITHUB_TOKEN read ANOTHER +# repository's Actions/Checks data when that repository is public? +# +# GitHub's docs never join the two axes this sits on. The installation-token +# side says the token is "limited to the repository that contains your +# workflow" and "cannot be granted access to repositories that the +# installation was not granted access to"; the endpoint side says each of +# these endpoints "can be used without authentication ... if only public +# resources are requested". Nothing states the evaluation order, so #238's +# receiver-side polling architecture rests on an unverified premise. +# +# A bare 200 is confounded: public Actions data is world-readable, so a 200 +# alone cannot distinguish "the token authorized me" from "the token was +# ignored". Hence four arms, with a private-repo discriminator. +# +# Also captures x-ratelimit-remaining across two target repos, to settle +# whether the 1,000 req/hr bucket is per-workflow-repo or per-target-repo. +# +# Closed unmerged; deleted with the probe branch. +name: probe-cross-repo-token + +on: + pull_request: + types: [opened, synchronize] + +permissions: {} + +jobs: + no-permissions: + name: GITHUB_TOKEN with permissions {} + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: {} + steps: + - name: Probe cross-repo reads + shell: bash + env: + PROBE_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -uo pipefail + + BASE=https://api.github.com/repos/melodic-software + Q='actions/runs?per_page=1' + + probe() { + local label="$1" auth="$2" url="$3" + local hdrs body code + hdrs="$(mktemp)" + body="$(mktemp)" + if [ "$auth" = yes ]; then + code="$(curl -sS -o "$body" -D "$hdrs" -w '%{http_code}' \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + -H "Authorization: Bearer ${PROBE_TOKEN}" \ + "$url")" || code=curl-error + else + code="$(curl -sS -o "$body" -D "$hdrs" -w '%{http_code}' \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "$url")" || code=curl-error + fi + echo "=== ${label}" + echo " auth=${auth} http=${code}" + grep -iE '^(x-ratelimit-(limit|remaining|used|resource)|x-accepted-github-permissions):' \ + "$hdrs" | tr -d '\r' | sed 's/^/ /' || true + if [ "$code" != 200 ]; then + echo " body: $(head -c 200 "$body" | tr -d '\n')" || true + fi + rm -f "$hdrs" "$body" + } + + probe "ARM A unauth -> PUBLIC claude-code-plugins" no "${BASE}/claude-code-plugins/${Q}" + probe "ARM B GITHUB_TOKEN -> PUBLIC claude-code-plugins" yes "${BASE}/claude-code-plugins/${Q}" + probe "ARM B2 GITHUB_TOKEN -> PUBLIC standards" yes "${BASE}/standards/${Q}" + probe "ARM C GITHUB_TOKEN -> PRIVATE medley" yes "${BASE}/medley/${Q}" + probe "ARM D GITHUB_TOKEN -> OWN ci-workflows" yes "${BASE}/ci-workflows/${Q}" + + echo "=== ARM E full detection chain -> PUBLIC claude-code-plugins" + RUN_ID="$(curl -sS \ + -H 'Accept: application/vnd.github+json' \ + -H "Authorization: Bearer ${PROBE_TOKEN}" \ + "${BASE}/claude-code-plugins/${Q}" | jq -r '.workflow_runs[0].id // empty')" || true + echo " run_id=${RUN_ID:-}" + if [ -n "${RUN_ID:-}" ]; then + CR_URL="$(curl -sS \ + -H 'Accept: application/vnd.github+json' \ + -H "Authorization: Bearer ${PROBE_TOKEN}" \ + "${BASE}/claude-code-plugins/actions/runs/${RUN_ID}/jobs" \ + | jq -r '.jobs[0].check_run_url // empty')" || true + echo " check_run_url=${CR_URL:-}" + if [ -n "${CR_URL:-}" ]; then + probe "ARM E GITHUB_TOKEN -> PUBLIC check-run annotations" yes "${CR_URL}/annotations" + fi + fi + + actions-checks-read: + name: GITHUB_TOKEN with actions+checks read + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + actions: read + checks: read + steps: + - name: Probe cross-repo reads + shell: bash + env: + PROBE_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -uo pipefail + + BASE=https://api.github.com/repos/melodic-software + Q='actions/runs?per_page=1' + + probe() { + local label="$1" auth="$2" url="$3" + local hdrs body code + hdrs="$(mktemp)" + body="$(mktemp)" + if [ "$auth" = yes ]; then + code="$(curl -sS -o "$body" -D "$hdrs" -w '%{http_code}' \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + -H "Authorization: Bearer ${PROBE_TOKEN}" \ + "$url")" || code=curl-error + else + code="$(curl -sS -o "$body" -D "$hdrs" -w '%{http_code}' \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "$url")" || code=curl-error + fi + echo "=== ${label}" + echo " auth=${auth} http=${code}" + grep -iE '^(x-ratelimit-(limit|remaining|used|resource)|x-accepted-github-permissions):' \ + "$hdrs" | tr -d '\r' | sed 's/^/ /' || true + if [ "$code" != 200 ]; then + echo " body: $(head -c 200 "$body" | tr -d '\n')" || true + fi + rm -f "$hdrs" "$body" + } + + probe "ARM A unauth -> PUBLIC claude-code-plugins" no "${BASE}/claude-code-plugins/${Q}" + probe "ARM B GITHUB_TOKEN -> PUBLIC claude-code-plugins" yes "${BASE}/claude-code-plugins/${Q}" + probe "ARM B2 GITHUB_TOKEN -> PUBLIC standards" yes "${BASE}/standards/${Q}" + probe "ARM C GITHUB_TOKEN -> PRIVATE medley" yes "${BASE}/medley/${Q}" + probe "ARM D GITHUB_TOKEN -> OWN ci-workflows" yes "${BASE}/ci-workflows/${Q}" + + echo "=== ARM E full detection chain -> PUBLIC claude-code-plugins" + RUN_ID="$(curl -sS \ + -H 'Accept: application/vnd.github+json' \ + -H "Authorization: Bearer ${PROBE_TOKEN}" \ + "${BASE}/claude-code-plugins/${Q}" | jq -r '.workflow_runs[0].id // empty')" || true + echo " run_id=${RUN_ID:-}" + if [ -n "${RUN_ID:-}" ]; then + CR_URL="$(curl -sS \ + -H 'Accept: application/vnd.github+json' \ + -H "Authorization: Bearer ${PROBE_TOKEN}" \ + "${BASE}/claude-code-plugins/actions/runs/${RUN_ID}/jobs" \ + | jq -r '.jobs[0].check_run_url // empty')" || true + echo " check_run_url=${CR_URL:-}" + if [ -n "${CR_URL:-}" ]; then + probe "ARM E GITHUB_TOKEN -> PUBLIC check-run annotations" yes "${CR_URL}/annotations" + fi + fi