Skip to content

feat: cross-repo cloud preview dispatch on PRs#7

Open
khaliqgant wants to merge 7 commits intomainfrom
feat/cloud-preview-dispatch
Open

feat: cross-repo cloud preview dispatch on PRs#7
khaliqgant wants to merge 7 commits intomainfrom
feat/cloud-preview-dispatch

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented Mar 26, 2026

Summary

  • Adds .github/workflows/cloud-preview.yml that triggers ephemeral CF Worker preview deploys in the cloud repo when PRs are opened/updated
  • Each PR gets its own isolated D1 database with fresh migrations
  • Preview resources (D1, KV, worker) are cleaned up automatically on PR close

How it works

PR opened/synced → dispatches cloud/preview-relayauth.yml (action=deploy)
  → creates relayauth-pr-{N} D1 + KV
  → runs all migrations from zero
  → deploys relayauth-api-pr-{N} worker
  → smoke test
  → reports ✅/❌ commit status on this PR

PR closed → dispatches cleanup (action=cleanup)
  → deletes D1, KV, worker

Prerequisites

  • CLOUD_DISPATCH_TOKEN secret (GitHub App installation token with actions:write on AgentWorkforce/cloud)

Test plan

  • Open a test PR and verify the cloud repo workflow is triggered
  • Verify commit status appears on the PR
  • Verify cleanup runs on PR close

🤖 Generated with Claude Code


Open with Devin

khaliqgant and others added 6 commits March 26, 2026 15:40
- Add "*" to Plane union type to support wildcard plane matching
- Simplify ParsedScope.plane to just Plane (since "*" is now included)
- Add "budget.exceeded", "budget.alert", and "scope.escalation_denied"
  to AuditAction type to match AUDIT_ACTIONS set in audit-query.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…failures

Replace hardcoded NOW = 1_774_432_800 with int(time.time()) so token
expiration tests remain valid regardless of when they run. Also increase
the expired token offset from 1s to 60s to exceed the 30s clock skew
leeway in the verifier.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All 12 test files using node:test runner were incompatible with the
vitest-based test infrastructure, causing "No test suite found" errors.
This converts them to use vitest imports (test, onTestFinished) while
keeping node:assert/strict for assertions. Also updates vitest.config.ts
to include all test files and unifies the test script to use vitest run.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…auth consistency

- Fix Uint8Array/BufferSource incompatibility in scope.ts and auth.ts by
  casting to ArrayBuffer for crypto.subtle.verify
- Prevent scope escalation errors from returning 500 by catching
  matchScopeFn exceptions in authenticateAndAuthorize and handling
  non-RelayAuthError objects in scope middleware's jsonErrorResponse
- Add bare wildcard "*" support in matchScope so tokens with scopes: ["*"]
  are correctly authorized
- Implement JWKS endpoint (GET /.well-known/jwks.json) serving HMAC key
  in JWK format, registered in worker.ts
- Add missing code field to auth error responses (missing_authorization,
  invalid_authorization, invalid_token, insufficient_scope) in auth.ts
  and propagate it in roles.ts for 401/403 response consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Triggers ephemeral CF Worker preview deploys in the cloud repo when PRs
are opened/updated. Cleans up (deletes D1, KV, worker) on PR close.
Each PR gets its own isolated D1 database with fresh migrations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +8 to +29
jwks.get("/jwks.json", (c) => {
const signingKey = c.env.SIGNING_KEY;
const keyId = c.env.SIGNING_KEY_ID;

// Encode the HMAC shared secret as a base64url JWK for symmetric key verification.
// This is used in dev/test mode where HMAC (HS256) signing is active.
const encodedKey = base64UrlEncode(new TextEncoder().encode(signingKey));

const jwksResponse = {
keys: [
{
kty: "oct",
use: "sig",
alg: "HS256",
kid: keyId,
k: encodedKey,
},
],
};

c.header("Cache-Control", CACHE_CONTROL_HEADER);
return c.json(jwksResponse, 200);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 JWKS endpoint publicly exposes the HMAC shared secret, enabling arbitrary token forgery

The new jwks.ts route serves the HMAC SIGNING_KEY as a JWK oct key at /.well-known/jwks.json. Because all /.well-known/* paths bypass the auth middleware (packages/server/src/worker.ts:27), any unauthenticated request can retrieve the key material (the k field). Since HMAC (HS256) is a symmetric algorithm, possessing the key allows both signing and verification — an attacker can forge valid tokens with arbitrary claims (sub, org, scopes, etc.), bypassing all authentication and authorization. The comment says "dev/test mode" but no environment guard is applied; the route is unconditionally registered in production via app.route("/.well-known", jwks) at packages/server/src/worker.ts:100.

Prompt for agents
The JWKS endpoint at packages/server/src/routes/jwks.ts exposes the HMAC shared secret (SIGNING_KEY) publicly. For symmetric keys (kty: oct), the k field IS the secret key material. Anyone who reads /.well-known/jwks.json can forge arbitrary HS256 JWTs.

Options to fix:
1. Do NOT expose HMAC keys via JWKS at all. JWKS endpoints are designed for asymmetric keys (RSA, EC) where the public key can safely be shared. Remove the jwks route entirely, or migrate to asymmetric signing (RS256) and only expose the public key.
2. If this must exist for dev/test only, gate the route behind an environment check (e.g. c.env.NODE_ENV === 'development') and ensure it is never registered in production.
3. At minimum, require authentication on this endpoint so only authorized clients can access it — but even this is risky since the secret should not be shared at all.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Take main's version which avoids exposing symmetric key material in the
JWKS endpoint to prevent token forgery.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant