From 4fafb767ce2cf4b928f634ec2ddb847c05af337f Mon Sep 17 00:00:00 2001 From: warelik <54947489+warelik@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:24:13 +0300 Subject: [PATCH] fix(identity-neutralize): neutralize Grok/xAI self-identification (a6-grok) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grok CLI's "You are Grok X.Y released by xAI." self-ID and its block trip Devin's content policy (permission_denied), live-confirmed by A/B. Same rule family as a1-a5. - src/handlers/identity-neutralize.js: rewrite the "You are Grok ... released by xAI" self-ID (and bare noun-phrase form) to a generic assistant line; strip the block - test/client-identity-neutralize.test.js: +7 — exact/variant/bare forms, multiline block strip, unrelated "Grok" untouched, opt-out gate respected --- src/handlers/identity-neutralize.js | 20 ++++++++ test/client-identity-neutralize.test.js | 67 +++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/handlers/identity-neutralize.js b/src/handlers/identity-neutralize.js index 28e1179..8773361 100644 --- a/src/handlers/identity-neutralize.js +++ b/src/handlers/identity-neutralize.js @@ -110,6 +110,26 @@ export function neutralizeClientIdentity(text, env = process.env, opts = {}) { /You are ([A-Z][\w.-]*), a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns,? and best practices\./g, 'You are $1, a software engineer.', ); + // (a6-grok) Grok / xAI self-identification (2026-07-16, live-confirmed to + // trip Devin's content policy → permission_denied). Grok CLI opens with + // "You are Grok 4.5 released by xAI. You are an interactive CLI tool that + // helps users with software engineering tasks." A/B on the live upstream: + // this exact line is what triggers the block — the same request with a + // generic assistant line passes. Match the full "You are Grok ... released + // by xAI" form and the bare noun phrase, any Grok version digit. + out = out.replace( + /You are Grok[\w .-]* released by xAI\.?/gi, + 'You are an AI coding assistant.', + ); + out = out.replace( + /\bGrok[\w .-]* released by xAI\.?/gi, + 'an AI coding assistant.', + ); + // (a6-grok2) Grok's executing_actions_with_care safety paragraph — a + // competitor-specific safety/policy framing that rides in the prompt body + // and has been observed in the same blocked request. Strip the whole + // ... block. + out = out.replace(/[\s\S]*?<\/executing_actions_with_care>/gi, ''); // (a6) SPECULATIVE / HYPOTHESIS-ONLY (2026-07-15), DEFAULT-OFF. Unlike a1-a5 // which are live-bisected confirmed triggers, this OBJECTIVE boast sentence is // only SUSPECTED to be in the same content-policy trigger family — NOT verified, diff --git a/test/client-identity-neutralize.test.js b/test/client-identity-neutralize.test.js index 1d2cb44..243c36c 100644 --- a/test/client-identity-neutralize.test.js +++ b/test/client-identity-neutralize.test.js @@ -211,6 +211,73 @@ describe('neutralizeClientIdentity', () => { }); }); +// 2026-07-16 (a6-grok/a6-grok2): Grok CLI's system prompt opens with "You are +// Grok 4.5 released by xAI. You are an interactive CLI tool that helps users +// with software engineering tasks." Live A/B on the Devin upstream proved that +// exact self-ID line trips the content policy (permission_denied); the same +// request with a generic assistant line passes. The competitor-specific +// safety paragraph rides in the same blocked +// prompt body and is stripped whole. Both rules are unconditional like a1-a5, +// gated only by the main WINDSURFAPI_NEUTRALIZE_CLIENT_ID switch (default on). +describe('neutralizeClientIdentity — Grok / xAI rules (a6-grok)', () => { + const GROK_ID = 'You are Grok 4.5 released by xAI.'; + + it('rewrites the exact Grok self-identification', () => { + const out = neutralizeClientIdentity(GROK_ID + ' You are an interactive CLI tool that helps users with software engineering tasks.'); + assert.ok(!/Grok/.test(out), 'no Grok'); + assert.ok(!/xAI/.test(out), 'no xAI'); + assert.match(out, /^You are an AI coding assistant\./, 'opens with the generic identity line'); + assert.match(out, /You are an interactive CLI tool that helps users with software engineering tasks\./, 'rest of prompt preserved'); + }); + + it('neutralizes the bare noun phrase without the leading "You are"', () => { + const out = neutralizeClientIdentity('Note: Grok 4 released by xAI is running.'); + assert.ok(!/Grok/.test(out), 'no Grok'); + assert.ok(!/released by xAI/i.test(out), 'no xAI attribution'); + assert.match(out, /an AI coding assistant\./, 'generic noun phrase substituted'); + }); + + it('matches any Grok version variant (4.5.1, 3 mini, ...)', () => { + assert.equal( + neutralizeClientIdentity('You are Grok 4.5.1 released by xAI.'), + 'You are an AI coding assistant.', + ); + assert.equal( + neutralizeClientIdentity('You are Grok 3 mini released by xAI.'), + 'You are an AI coding assistant.', + ); + }); + + it('strips the block entirely', () => { + const out = neutralizeClientIdentity('foo bar'); + assert.equal(out, '', 'inline block fully removed'); + }); + + it('strips a multiline block and keeps surrounding text', () => { + const src = 'Before.\n\nfoo bar\nDouble-check destructive commands before running them.\n\nAfter.'; + const out = neutralizeClientIdentity(src); + assert.ok(!/executing_actions_with_care/i.test(out), 'tags removed'); + assert.ok(!/foo bar/.test(out), 'block content removed'); + assert.ok(!/destructive commands/.test(out), 'multiline content removed'); + assert.match(out, /Before\./, 'content before kept'); + assert.match(out, /After\./, 'content after kept'); + }); + + it('leaves unrelated mentions of "Grok" alone (no "released by xAI" attribution)', () => { + // Bare mentions are safe — only the xAI-attributed self-ID phrasing is rewritten. + const src = 'Use the Logstash grok filter. Grok patterns parse logs.'; + assert.equal(neutralizeClientIdentity(src), src, 'unrelated text untouched'); + const plainId = 'You are Grok, a helpful assistant.'; + assert.equal(neutralizeClientIdentity(plainId), plainId, 'un-attributed identity line untouched'); + }); + + it('the Grok neutralization respects the opt-out flag', () => { + process.env.WINDSURFAPI_NEUTRALIZE_CLIENT_ID = '0'; + const src = GROK_ID + '\nTake care.'; + assert.equal(neutralizeClientIdentity(src), src, 'opt-out leaves it verbatim'); + }); +}); + // P2 — Claude Code compat layer: the ccActive gate on the opt-in (cc) block. // Contract: a1-a5 stay UNCONDITIONAL (default 529 / content-policy defense for // ALL clients); the (cc) block is opt-in and currently EMPTY, so activating it