← back to AbramsEgo
a2a-client: fix 3 Contrarian-flagged defects (TK-10381)
779ad4012d079f4cd02ffa183bc1a6acf05f738e · 2026-08-09 04:19:34 -0700 · steve@designerwallcoverings.com
- Add ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, REPLICATE_API_TOKEN
to BANNED_PAYLOAD_PATTERNS (these are the actual high-value secrets on this
box; original linter only blocked Shopify/GH tokens)
- Fix card validation: !card.name && !card.url → || so a card missing only
name OR only url correctly throws instead of silently passing
- Fix dead rpcUrl ternary: both branches produced identical strings; now uses
card.url (the A2A spec's declared JSON-RPC endpoint) with base-URL fallback
- Expand lintPayload tests to 9 assertions covering the 4 new AI key patterns
Files touched
M lib/a2a-client.jsM lib/a2a-client.test.jsM memos/a2a-rails.md
Diff
commit 779ad4012d079f4cd02ffa183bc1a6acf05f738e
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Sun Aug 9 04:19:34 2026 -0700
a2a-client: fix 3 Contrarian-flagged defects (TK-10381)
- Add ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, REPLICATE_API_TOKEN
to BANNED_PAYLOAD_PATTERNS (these are the actual high-value secrets on this
box; original linter only blocked Shopify/GH tokens)
- Fix card validation: !card.name && !card.url → || so a card missing only
name OR only url correctly throws instead of silently passing
- Fix dead rpcUrl ternary: both branches produced identical strings; now uses
card.url (the A2A spec's declared JSON-RPC endpoint) with base-URL fallback
- Expand lintPayload tests to 9 assertions covering the 4 new AI key patterns
---
lib/a2a-client.js | 10 ++++++----
lib/a2a-client.test.js | 7 ++++++-
memos/a2a-rails.md | 1 +
3 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/lib/a2a-client.js b/lib/a2a-client.js
index 853c3249..08d45985 100644
--- a/lib/a2a-client.js
+++ b/lib/a2a-client.js
@@ -32,6 +32,10 @@ const BANNED_PAYLOAD_PATTERNS = [
/sk_live_/,
/pk_live_/,
/ghp_[A-Za-z0-9]{36}/,
+ /ANTHROPIC_API_KEY/i,
+ /OPENAI_API_KEY/i,
+ /GEMINI_API_KEY/i,
+ /REPLICATE_API_TOKEN/i,
];
/** Throw if the outgoing question contains banned patterns (secrets, fleet internals). */
@@ -84,7 +88,7 @@ async function fetchAgentCard(baseUrl) {
if (!res.ok) throw new Error(`a2a: agent card fetch failed ${res.status} from ${cardUrl}`);
const card = await res.json();
if (!card || typeof card !== 'object') throw new Error('a2a: invalid agent card (not an object)');
- if (!card.name && !card.url) throw new Error('a2a: agent card missing name/url fields');
+ if (!card.name || !card.url) throw new Error('a2a: agent card missing name/url fields');
cardCache[cacheKey] = { card, fetchedAt: Date.now() };
return card;
}
@@ -119,9 +123,7 @@ function buildGetTask(taskId) {
* Reads the endpoint from the Agent Card or falls back to baseUrl + '/'.
*/
async function rpcCall(baseUrl, card, body, authHeader) {
- const rpcUrl = card?.defaultInputModes?.length
- ? `${baseUrl.replace(/\/$/, '')}/`
- : `${baseUrl.replace(/\/$/, '')}/`;
+ const rpcUrl = card?.url || `${baseUrl.replace(/\/$/, '')}/`;
const headers = { 'Content-Type': 'application/json', Accept: 'application/json' };
if (authHeader) headers['Authorization'] = authHeader;
diff --git a/lib/a2a-client.test.js b/lib/a2a-client.test.js
index e94050bb..65a5989e 100644
--- a/lib/a2a-client.test.js
+++ b/lib/a2a-client.test.js
@@ -40,9 +40,14 @@ assert.throws(() => lintPayload('my SHOPIFY_ADMIN_TOKEN=abc123'), /blocked/);
assert.throws(() => lintPayload('DATABASE_URL=postgres://...'), /blocked/);
// use split string to avoid triggering gitleaks on the test itself
assert.throws(() => lintPayload('key=' + 'sk_live_' + 'abcdefghijklmnop'), /blocked/);
+// AI keys — the actual high-value secrets on this box must also be blocked
+assert.throws(() => lintPayload('my ANTHROPIC_API_KEY=sk-ant-xyz'), /blocked/);
+assert.throws(() => lintPayload('export OPENAI_API_KEY=sk-proj-abc'), /blocked/);
+assert.throws(() => lintPayload('GEMINI_API_KEY=AIza...'), /blocked/);
+assert.throws(() => lintPayload('REPLICATE_API_TOKEN=r8_abc123'), /blocked/);
lintPayload('What is the best approach to rate limiting?'); // clean
lintPayload('How should I structure my agent for A2A?'); // clean
-console.log(' lintPayload: 5 assertions pass');
+console.log(' lintPayload: 9 assertions pass');
// --- loadAllowlist -----------------------------------------------------------
// The real file is data/a2a-agents.json (starts empty — [] by spec).
diff --git a/memos/a2a-rails.md b/memos/a2a-rails.md
index f03a640d..3bbd8729 100644
--- a/memos/a2a-rails.md
+++ b/memos/a2a-rails.md
@@ -8,6 +8,7 @@
2. **Payload linter** — `lintPayload()` blocks outbound text containing:
- `SHOPIFY_ADMIN_TOKEN`, `DATABASE_URL`, `sk_live_`, `pk_live_`, `ghp_…` (GitHub PATs)
+ - `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `REPLICATE_API_TOKEN` (AI keys)
- No fleet-snapshot fields ever leave the box.
3. **15 s timeout + no redirects** — `fetchSafe()` aborts after 15 s and sets `redirect: 'error'`
← aee628c5 feat: A2A client Phases A-C (TK-10381)
·
back to AbramsEgo
·
auto-data-snapshot: 2026-08-09T04:37:25 (1 data files) — dat c04a7bc7 →