From c7afb372b460293424dae33a40307881de400fff Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Fri, 13 Nov 2026 12:25:43 -0500 Subject: [PATCH] Authenticate composer's API path too, not just its git path --- .github/workflows/security.yml | 36 ++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index d73b6b0..7f36469 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -88,16 +88,26 @@ jobs: exit 1 fi - # Composer clones the plain https URL and offers the token as `:x-oauth-basic`, which - # GitHub REFUSES for an app installation token. This rewrite supplies the documented form, - # `x-access-token:`, so the clone is attempted correctly the first time. - - name: Let git clone that owner's private repositories + # Composer needs the token on BOTH paths it can take, and which one it takes is not ours to + # choose -- a repository's own composer.json decides via `no-api`. + # + # dist (the default): it downloads https://api.github.com/repos///zipball/. For a + # private repository that answers 404 without credentials, so COMPOSER_AUTH's github-oauth + # entry is what makes it work. A 404 reads as "no such release", not "not authorised". + # source (`no-api`): it clones the plain https URL and offers the token as + # `:x-oauth-basic`, which GitHub REFUSES for an app installation token. The + # url.insteadOf rewrite supplies the documented `x-access-token:` form instead. + # + # Setting only one of the two fixes only half the callers, and the half it misses fails with + # an error that names neither auth nor the token. + - name: Let composer and git reach that owner's private repositories if: inputs.private-dependency-owner != '' env: TOKEN: ${{ steps.dep-token.outputs.token }} OWNER: ${{ inputs.private-dependency-owner }} run: | git config --global url."https://x-access-token:${TOKEN}@github.com/${OWNER}".insteadOf "https://github.com/${OWNER}" + printf 'COMPOSER_AUTH={"github-oauth":{"github.com":"%s"}}\n' "${TOKEN}" >> "${GITHUB_ENV}" - name: Install dependencies # `composer install` will create composer.lock for libraries that @@ -156,16 +166,26 @@ jobs: exit 1 fi - # Composer clones the plain https URL and offers the token as `:x-oauth-basic`, which - # GitHub REFUSES for an app installation token. This rewrite supplies the documented form, - # `x-access-token:`, so the clone is attempted correctly the first time. - - name: Let git clone that owner's private repositories + # Composer needs the token on BOTH paths it can take, and which one it takes is not ours to + # choose -- a repository's own composer.json decides via `no-api`. + # + # dist (the default): it downloads https://api.github.com/repos///zipball/. For a + # private repository that answers 404 without credentials, so COMPOSER_AUTH's github-oauth + # entry is what makes it work. A 404 reads as "no such release", not "not authorised". + # source (`no-api`): it clones the plain https URL and offers the token as + # `:x-oauth-basic`, which GitHub REFUSES for an app installation token. The + # url.insteadOf rewrite supplies the documented `x-access-token:` form instead. + # + # Setting only one of the two fixes only half the callers, and the half it misses fails with + # an error that names neither auth nor the token. + - name: Let composer and git reach that owner's private repositories if: inputs.private-dependency-owner != '' env: TOKEN: ${{ steps.dep-token.outputs.token }} OWNER: ${{ inputs.private-dependency-owner }} run: | git config --global url."https://x-access-token:${TOKEN}@github.com/${OWNER}".insteadOf "https://github.com/${OWNER}" + printf 'COMPOSER_AUTH={"github-oauth":{"github.com":"%s"}}\n' "${TOKEN}" >> "${GITHUB_ENV}" - name: composer install run: composer install --no-interaction --no-progress --prefer-dist