From c4aea4fea199d59a66205fd333f1f580165ada4a Mon Sep 17 00:00:00 2001 From: Mayumi Hara Date: Tue, 12 May 2026 15:11:41 +0900 Subject: [PATCH 1/2] fix(security): redact LEMMA_API_KEY from committed wrangler.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the 4/18 assessment Phase 1-A: the worker's packages/worker/wrangler.toml had LEMMA_API_KEY committed as a literal string under [vars]. In a public repository every prior commit still contains the value, so the key should be rotated out-of-band; this change removes the literal from HEAD and aligns the codebase with Cloudflare Workers' standard secret pattern. ### Changes - `packages/worker/wrangler.toml`: drop the literal LEMMA_API_KEY line from [vars]. Add a comment that points to `wrangler secret put LEMMA_API_KEY --cwd packages/worker` and `packages/worker/.dev.vars` (already gitignored). - `.env.example`: rewrite the LEMMA_API_KEY block with the same guidance (Cloudflare secret for deploy, .dev.vars for local). - `README.md`: extend the Configure step to put LEMMA_API_KEY into `.dev.vars` alongside the CDP keys, and update the note that the worker no longer ships with a demo LEMMA_API_KEY value. - `packages/worker/src/index.test.ts`: add a regression test that reads `packages/worker/wrangler.toml` and asserts no literal `LEMMA_API_KEY = ...` assignment under [vars]. The test fails loudly if anyone reintroduces a committed key value. ### Worker code (unchanged) The worker already reads LEMMA_API_KEY exclusively from `c.env` (types in `Env`, used by `lemmaHeaders()`). No code change is needed for the read path — only the source of the value changes. ### Out of scope (per the brief) - Phase 1-B (TypeError) and Phase 1-C (API path): not actioned in this PR. The 4/18 assessment doc isn't in the repo and no matching TypeError reproduces in the current test suite. Phase 1-C paths already match `@lemmaoracle/spec@0.0.21` (`/v1/verified-attributes/query`) and PR #6 added path- consistency tests for the agent / worker endpoints. - Phase 2: already completed in PR #6 (DEMO_MODE removal). - Phase 3 docs: only the LEMMA_API_KEY-related lines touched here. - Bedrock AgentCore integration, KYC attribute-proof demo, x402 Bazaar metadata: out of scope per the brief. ### Operational follow-up (not in this PR) Rotate the LEMMA_API_KEY on the Lemma side and store the new value via `wrangler secret put`. The previously committed value remains visible in git history. Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.example | 8 ++++++-- README.md | 8 +++++--- packages/worker/src/index.test.ts | 24 +++++++++++++++++++++--- packages/worker/wrangler.toml | 5 +++-- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index 781919f..9694286 100644 --- a/.env.example +++ b/.env.example @@ -10,8 +10,12 @@ AGENT_PRIVATE_KEY=0x... # Set via: npx wrangler secret put PAY_TO_ADDRESS --cwd packages/worker PAY_TO_ADDRESS=0x... -# Lemma API (pre-configured for demo, optional) -# LEMMA_API_KEY=your_lemma_api_key_here +# Lemma API key. +# For deployed workers: set as an encrypted Cloudflare Workers secret — +# npx wrangler secret put LEMMA_API_KEY --cwd packages/worker +# For local dev: create packages/worker/.dev.vars (gitignored) with +# LEMMA_API_KEY="..." +# Optional — the demo path works without a key, but rate limits apply. # Blog URL to verify (optional, defaults to example) # BLOG_URL=https://example-blog.com/articles/zk-proofs diff --git a/README.md b/README.md index 8578e13..4d7cb0c 100644 --- a/README.md +++ b/README.md @@ -81,15 +81,17 @@ pnpm install cp .env.example .env # Required: PAY_TO_ADDRESS, AGENT_PRIVATE_KEY -# Worker CDP credentials (for x402 facilitator auth) -# Get keys from https://portal.cdp.coinbase.com/ +# Worker secrets (kept out of git via `.dev.vars`) +# - CDP keys for x402 facilitator auth: https://portal.cdp.coinbase.com/ +# - LEMMA_API_KEY: optional for demo; required for higher rate limits cat > packages/worker/.dev.vars << 'EOF' CDP_API_KEY_ID=your_key_id CDP_API_KEY_SECRET=your_key_secret +LEMMA_API_KEY=your_lemma_api_key EOF ``` -> The worker's `wrangler.toml` includes a demo `LEMMA_API_KEY` and `FACILITATOR_URL` pre-configured for Base Sepolia — no extra setup needed. +> The worker's `wrangler.toml` ships `LEMMA_API_BASE` and `FACILITATOR_URL` for Base Sepolia. `LEMMA_API_KEY` is treated as a secret and is not committed — set it as a Cloudflare Workers secret for deployments (`npx wrangler secret put LEMMA_API_KEY --cwd packages/worker`) and via `packages/worker/.dev.vars` for local dev. ### 2. Start the worker diff --git a/packages/worker/src/index.test.ts b/packages/worker/src/index.test.ts index bac7f72..aeb456a 100644 --- a/packages/worker/src/index.test.ts +++ b/packages/worker/src/index.test.ts @@ -1,11 +1,12 @@ /** - * Worker tests — 402 response shape verification. - * - * Tests that x402 payment-required responses have the correct structure. + * Worker tests — 402 response shape verification and secret-redaction + * regression guard. */ import { describe, it, expect } from "vitest"; import { Hono } from "hono"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; // --------------------------------------------------------------------------- // Types (mirrored from index.ts for testing) @@ -168,6 +169,23 @@ describe("Worker", () => { }); }); + describe("wrangler.toml secret redaction", () => { + // Regression guard: a previous revision committed LEMMA_API_KEY as a + // literal value under [vars] in packages/worker/wrangler.toml. Secrets + // must live in `wrangler secret put` (production) or .dev.vars (local). + // If this test fails, someone re-introduced a literal key — move it back + // to a secret and refresh the test fixture. + it("does not contain a literal LEMMA_API_KEY in vars", () => { + const wranglerToml = readFileSync( + join(__dirname, "..", "wrangler.toml"), + "utf-8", + ); + // Match `LEMMA_API_KEY` followed by `=` and any non-comment value. + const literalAssignment = /^\s*LEMMA_API_KEY\s*=/m; + expect(wranglerToml).not.toMatch(literalAssignment); + }); + }); + describe("Health check endpoint", () => { it("should return ok status", async () => { const app = new Hono<{ Bindings: Env }>(); diff --git a/packages/worker/wrangler.toml b/packages/worker/wrangler.toml index fa2f118..5b9be2b 100644 --- a/packages/worker/wrangler.toml +++ b/packages/worker/wrangler.toml @@ -8,8 +8,9 @@ port = 8787 [vars] LEMMA_API_BASE = "https://workers.lemma.workers.dev" -# Demo-only API key — safe to commit (read-only, scoped to demo data) -LEMMA_API_KEY = "b6363aa6265322ed0d786a11d5b6d3264947052ca72deba4cbe1685d099af892" +# LEMMA_API_KEY is set as an encrypted Cloudflare Workers secret, not in vars. +# wrangler secret put LEMMA_API_KEY --cwd packages/worker +# For local dev, put it in packages/worker/.dev.vars (gitignored). FACILITATOR_URL = "https://api.cdp.coinbase.com/platform/v2/x402" LEMMA_RELAY_URL = "https://p01--lemma-relay-api--svxwx5rc5jzx.code.run/" PAY_TO_ADDRESS = "0x000000000000000000000000000000000000dEaD" From e4925669fe614b2fa8853fb45b185ca5c40e59aa Mon Sep 17 00:00:00 2001 From: Mayumi Hara Date: Tue, 12 May 2026 15:22:48 +0900 Subject: [PATCH 2/2] docs(worker): mirror "demo path works without key" note into wrangler.toml PR #9 review feedback: the same demo-without-key + rate-limit caveat that lives in .env.example was missing from the wrangler.toml comment header. Add it so both files describe the optional-key behavior the same way. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/worker/wrangler.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/worker/wrangler.toml b/packages/worker/wrangler.toml index 5b9be2b..253a894 100644 --- a/packages/worker/wrangler.toml +++ b/packages/worker/wrangler.toml @@ -11,6 +11,7 @@ LEMMA_API_BASE = "https://workers.lemma.workers.dev" # LEMMA_API_KEY is set as an encrypted Cloudflare Workers secret, not in vars. # wrangler secret put LEMMA_API_KEY --cwd packages/worker # For local dev, put it in packages/worker/.dev.vars (gitignored). +# The demo path works without a key, but is rate-limited. FACILITATOR_URL = "https://api.cdp.coinbase.com/platform/v2/x402" LEMMA_RELAY_URL = "https://p01--lemma-relay-api--svxwx5rc5jzx.code.run/" PAY_TO_ADDRESS = "0x000000000000000000000000000000000000dEaD"