From 93a7324b98e72e5488143f85d54c2d0ef8b7267f Mon Sep 17 00:00:00 2001 From: sparkysolutions10-pixelmy Date: Mon, 19 Oct 2026 22:44:32 +0530 Subject: [PATCH] fix(mcp): extend env drift guard to examples and scripts directories (#413) --- .env.example | 7 +++++++ src/__tests__/envDrift.test.ts | 28 ++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 3937aedc..6fc11bbf 100644 --- a/.env.example +++ b/.env.example @@ -162,6 +162,10 @@ BAZAAR_CATALOG_WRITES_PER_DAY=200 # --- Oracle Relay Example --- # Lens API base URL consumed by the relay example. ORACLE_RELAY_API_URL=http://localhost:3002 +# Soroban RPC endpoint for transaction submission. +ORACLE_RELAY_RPC_URL=https://soroban-testnet.stellar.org +# Stellar network passphrase for transaction signing. +ORACLE_RELAY_NETWORK_PASSPHRASE=Test SDF Network ; September 2015 # Soroban contract ID for the relay example. ORACLE_RELAY_CONTRACT_ID=CC... # Secret key for the relay's source account. @@ -207,6 +211,9 @@ FACILITATOR_FEE_STROOPS_MAINNET=50000 # fetches a gated resource. These spend money, so keep them out of any other # process and fund only what you are willing to lose. MCP_AGENT_SECRET_KEY_TESTNET= +# WARNING: MCP_AGENT_SECRET_KEY_MAINNET signs transactions on the public network +# and can spend real funds. Keep this out of any other process and fund only +# what you are willing to lose. MCP_AGENT_SECRET_KEY_MAINNET= # Ceiling per paid_call, enforced by the x402 fetch wrapper. MCP_MAX_PAYMENT_PRICE=$1.00 diff --git a/src/__tests__/envDrift.test.ts b/src/__tests__/envDrift.test.ts index 2187798b..cd1c2627 100644 --- a/src/__tests__/envDrift.test.ts +++ b/src/__tests__/envDrift.test.ts @@ -5,6 +5,8 @@ import ts from 'typescript' const rootDir = resolve(__dirname, '../..') const srcDir = resolve(rootDir, 'src') +const examplesDir = resolve(rootDir, 'examples') +const scriptsDir = resolve(rootDir, 'scripts') const envExamplePath = resolve(rootDir, '.env.example') /** @@ -156,7 +158,11 @@ describe('.env.example drift guard', () => { // vitest's 5s default and makes this guard flaky. Give it real headroom. it('asserts each process.env read in source appears in .env.example or the allow-list', { timeout: 60_000 }, () => { const documentedVars = parseEnvExample(envExamplePath) - const sourceFiles = getSourceFiles(srcDir) + const sourceFiles = [ + ...getSourceFiles(srcDir), + ...getSourceFiles(examplesDir), + ...getSourceFiles(scriptsDir), + ] const allReads: EnvRead[] = [] for (const file of sourceFiles) { @@ -186,7 +192,7 @@ describe('.env.example drift guard', () => { } }) - it('fails when a new un-documented process.env read is encountered', () => { + it('fails when a new un-documented process.env read is encountered in src/', () => { const sampleCode = ` export function init() { const secret = process.env.SOME_BRAND_NEW_SECRET_VAR @@ -204,6 +210,24 @@ describe('.env.example drift guard', () => { expect(missing[0].name).toBe('SOME_BRAND_NEW_SECRET_VAR') }) + it('fails when an un-documented process.env read is encountered in examples/ (#413)', () => { + const sampleExampleCode = ` + export function runExample() { + const secret = process.env.MCP_UNDOCUMENTED_TEST_VAR + return secret + } + ` + const reads = extractEnvReadsFromSource(sampleExampleCode, 'examples/mcp-server/sample.ts') + const documentedVars = parseEnvExample(envExamplePath) + + const missing = reads.filter( + (read) => !documentedVars.has(read.name) && !(read.name in UNDOCUMENTED_ENV_ALLOW_LIST) + ) + + expect(missing).toHaveLength(1) + expect(missing[0].name).toBe('MCP_UNDOCUMENTED_TEST_VAR') + }) + it('requires every allow-list entry to have an explicit rationale', () => { const entries = Object.entries(UNDOCUMENTED_ENV_ALLOW_LIST) expect(entries.length).toBeGreaterThan(0)