From c3cf13432ed21d7b6ed7a2b523fd8c9b9c3426c5 Mon Sep 17 00:00:00 2001 From: Leo Date: Sat, 14 Nov 2026 10:09:03 +0800 Subject: [PATCH] ci: read the Console client id from a repository variable --- .github/workflows/ci.yml | 9 ++++++--- test/keyed-probes.test.ts | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02841db..8b68edf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -476,8 +476,11 @@ jobs: # OIDC endpoint nor secrets, so the step exits with a notice and the keyed # cases skip exactly as they do when the tier is `none`, while the keyless # cases still check that the endpoints answer. The Console CI token's client - # id is public by design: the exchange binds it to the audience and the - # GitHub OIDC subject, so the id alone grants nothing. + # id is public by design (the exchange binds it to the audience and the + # GitHub OIDC subject, so the id alone grants nothing) and it lives in the + # `OPENCODE_CONSOLE_CLIENT_ID` repository variable, not here: its `sk_` shape + # is indistinguishable from a Checkout.com secret key to a scanner, and a + # literal in this file raised a false-positive secret-scanning alert. # # Both probe suites write a dated table of what each URL shape answered # (docs/redirect-planes.md §13); the job appends them to the run summary and @@ -543,7 +546,7 @@ jobs: if: steps.tier.outputs.keyed != 'none' env: AUDIENCE: https://opencode.ai/console - CLIENT_ID: sk_baf57eb6-1b63-4eb7-a538-737dc3aea54d + CLIENT_ID: ${{ vars.OPENCODE_CONSOLE_CLIENT_ID }} run: | # A fork or Dependabot pull request is issued no OIDC endpoint; leave # the suite keyless, as its keyed cases already expect. diff --git a/test/keyed-probes.test.ts b/test/keyed-probes.test.ts index 1f08cab..bc04eae 100644 --- a/test/keyed-probes.test.ts +++ b/test/keyed-probes.test.ts @@ -1371,6 +1371,13 @@ describe("keyed probes · the CI wiring", () => { for (const step of steps) { expect(step).not.toContain("secrets.OPENCODE"); } + // The client id is an identifier, not a credential, and it is read from a + // repository variable: a literal here would be a `sk_`-shaped string that a + // secret scanner reads as a Checkout.com production key (it once did). + expect(signIn).toContain( + `CLIENT_ID: ${expression("vars.OPENCODE_CONSOLE_CLIENT_ID")}` + ); + expect(job).not.toMatch(/\bsk_[0-9a-f]{8}-/u); }); it("the test step names the tier and the record directory, and no key: the sign-in step owns them", () => {