From a8ef80833b30aae12e4bdbf9f62d3e378af7f190 Mon Sep 17 00:00:00 2001 From: "Vincent (Wen Yu) Ge" Date: Tue, 15 Sep 2026 12:27:52 -0400 Subject: [PATCH] fix(ci): auth warlock triage with the CI gateway token file The wizard gateway moved CI callers to a pre-issued bearer supplied through a token file against ai-gateway..posthog.com (see PostHog/wizard#1240). Write the shared CI bot gateway secret to a file and pass it via CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE, sent as Authorization: Bearer, replacing the personal-API-key auth. Generated-By: PostHog Desktop Task-Id: 5f9d544a-ef47-488d-bd47-a8e7c61a6583 Co-Authored-By: Claude Fable 5 --- .github/workflows/build-release.yml | 7 ++++- .github/workflows/build.yml | 7 ++++- scripts/scan-warlock.js | 41 ++++++++++++++++++----------- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index a3924a3b..67d4be41 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -118,9 +118,14 @@ jobs: BUILD_VERSION: ${{ env.RELEASE_VERSION }} run: pnpm run build + - name: Write gateway token + run: printf '%s' "$GATEWAY_TOKEN" > "$RUNNER_TEMP/gateway-token" + env: + GATEWAY_TOKEN: ${{ secrets.GH_APP_POSTHOG_WIZARD_CI_BOT_POSTHOG_GATEWAY_TOKEN }} + - name: Scan skills with Warlock env: - CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY: ${{ secrets.CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY }} + CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE: ${{ runner.temp }}/gateway-token run: node scripts/scan-warlock.js dist/skills - name: List build artifacts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 865deeef..b0c60e46 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,11 @@ jobs: - name: Build run: pnpm build + - name: Write gateway token + run: printf '%s' "$GATEWAY_TOKEN" > "$RUNNER_TEMP/gateway-token" + env: + GATEWAY_TOKEN: ${{ secrets.GH_APP_POSTHOG_WIZARD_CI_BOT_POSTHOG_GATEWAY_TOKEN }} + - name: Scan skills with Warlock # Fork PRs don't get repo secrets, so Warlock can't reach the LLM gateway to # triage matches and reports every hit (incl. false positives) as a threat. @@ -46,7 +51,7 @@ jobs: # full triaged scan still gates trusted runs (push-to-main, release). continue-on-error: ${{ github.event.pull_request.head.repo.fork == true }} env: - CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY: ${{ secrets.CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY }} + CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE: ${{ runner.temp }}/gateway-token run: node scripts/scan-warlock.js dist/skills - name: Lint env var naming conventions diff --git a/scripts/scan-warlock.js b/scripts/scan-warlock.js index adddb94f..e71cb6dc 100644 --- a/scripts/scan-warlock.js +++ b/scripts/scan-warlock.js @@ -68,11 +68,13 @@ const isCI = Boolean(process.env.CI); // matches. The LLM decides whether each match is a real threat or a // false positive // -// The gateway's `ci` product serves CI runs with a personal API key. The -// legacy `wizard` product is retired and refuses every caller with a 403. -// US: https://gateway.us.posthog.com/ci -// EU: https://gateway.eu.posthog.com/ci -// Local: http://localhost:3308/ci +// Auth is a pre-issued gateway bearer read from the file named by +// CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE (CI writes the secret there), the +// same mechanism the wizard's CI uses (WIZARD_CI_GATEWAY_TOKEN_FILE). +// The URL carries no path; the SDK appends /v1/messages. +// US: https://ai-gateway.us.posthog.com +// EU: https://ai-gateway.eu.posthog.com +// ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN override both for local runs. function getGatewayUrl() { const host = process.env.POSTHOG_HOST || "https://us.posthog.com"; @@ -88,25 +90,32 @@ function getGatewayUrl() { } } - if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") { - return "http://localhost:3308/ci"; - } if (hostname === "eu.posthog.com" || hostname === "eu.i.posthog.com") { - return "https://gateway.eu.posthog.com/ci"; + return "https://ai-gateway.eu.posthog.com"; + } + return "https://ai-gateway.us.posthog.com"; +} + +function readGatewayToken() { + if (process.env.ANTHROPIC_AUTH_TOKEN) return process.env.ANTHROPIC_AUTH_TOKEN; + const tokenFile = process.env.CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE; + if (!tokenFile) return null; + try { + return fs.readFileSync(tokenFile, "utf8").trim() || null; + } catch { + return null; } - return "https://gateway.us.posthog.com/ci"; } function createLLMProvider() { - // Prefer the wizard's local proxy if available (ANTHROPIC_BASE_URL), - // otherwise fall back to the PostHog gateway. const baseURL = process.env.ANTHROPIC_BASE_URL || getGatewayUrl(); - const apiKey = process.env.ANTHROPIC_AUTH_TOKEN || process.env.CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY; - if (!apiKey) return null; + const authToken = readGatewayToken(); + if (!authToken) return null; + // authToken sends `Authorization: Bearer`; the gateway rejects `x-api-key`. const client = new Anthropic({ baseURL, - apiKey, + authToken, }); return async (prompt) => { @@ -273,7 +282,7 @@ async function main() { console.log("LLM triage enabled (using PostHog gateway).\n"); } else { console.log( - "LLM triage disabled (no API key configured). All matches will be treated as threats.\n", + "LLM triage disabled (no gateway token configured). All matches will be treated as threats.\n", ); }