How gstack Handles Unicode Sanitization at Server Egress
gstack’s browse daemon guarantees that any string leaving the server is free of lone UTF‑16 surrogate code points, which would otherwise break downstream JSON parsers (e.g., the Anthropic API).
gstack, an open‑source project by Garry Tan, implements a defensive Unicode sanitization strategy at server egress to prevent malformed strings from reaching external consumers. The system specifically targets isolated surrogates in the range U+D800–U+DFFF, ensuring that every byte stream exiting the command pipeline contains only valid Unicode scalar values or complete surrogate pairs.
The Lone Surrogate Threat
UTF‑16 surrogate pairs consist of a high surrogate (0xD800–0xDBFF) followed immediately by a low surrogate (0xDC00–0xDFFF). When these values appear in isolation—due to truncated buffers, encoding errors, or malicious input—they violate the Unicode standard and cause strict JSON parsers to throw errors. gstack’s sanitization layer detects and removes these lone surrogates before they reach the HTTP response layer.
Centralized Choke‑Point Architecture
Rather than scattering validation logic throughout the codebase, gstack consolidates all egress sanitation inside a single wrapper function. In browse/src/server.ts, every command result flows through handleCommandInternal before serialization. This design guarantees that no code path can accidentally bypass Unicode validation, while also preventing double‑sanitization that could corrupt legitimate data.
The sanitizeLoneSurrogates Implementation
The core filter resides in browse/src/server.ts (lines 63‑90). The function scans input strings for characters matching the surrogate pattern, validates whether each match is part of a legitimate pair, and strips any isolated halves:
function sanitizeLoneSurrogates(str: string): string {
return str.replace(/[\uD800-\uDFFF]/g, (match, offset) => {
const code = match.charCodeAt(0);
if (code >= 0xD800 && code <= 0xDBFF) {
const next = str.charCodeAt(offset + 1);
if (next >= 0xDC00 && next <= 0xDFFF) return match; // valid pair
}
if (code >= 0xDC00 && code <= 0xDFFF) {
const prev = str.charCodeAt(offset - 1);
if (prev >= 0xD800 && prev <= 0xDBFF) return match; // valid pair
}
return ''; // lone surrogate
});
}
The regular expression targets the entire surrogate block, while the offset checks ensure that valid high‑low sequences remain untouched. Isolated surrogates are replaced with empty strings, effectively removing them from the output stream.
Integration Points
Command Result Sanitization
After the business logic executes inside handleCommandInternalImpl, the wrapper handleCommandInternal (lines 88‑94) immediately sanitizes the result field:
async function handleCommandInternal(
body: { command: string; args?: string[]; tabId?: number },
tokenInfo?: TokenInfo | null,
opts?: { skipRateCheck?: boolean; skipActivity?: boolean; chainDepth?: number },
): Promise<CommandResult> {
const cr = await handleCommandInternalImpl(body, tokenInfo, opts);
return { ...cr, result: sanitizeLoneSurrogates(cr.result) };
}
This ensures that every command result—whether text extraction, DOM snapshots, or telemetry data—passes through the surrogate filter before reaching the response builder.
Final Response Safety
As a secondary guard, buildCommandResponse (lines 101‑106) invokes sanitizeBody on the output string. This catches any escaped surrogate sequences that might have survived the initial pass or been introduced during JSON stringification:
export function buildCommandResponse(cr: CommandResult): Response {
const contentType = cr.json ? 'application/json' : 'text/plain';
const safeBody = typeof cr.result === 'string'
? sanitizeBody(cr.result, !!cr.json) // second‑level guard
: cr.result;
return new Response(safeBody, { status: cr.status, headers: { 'Content-Type': contentType, ...cr.headers } });
}
Invariant Enforcement
The source code explicitly documents the architectural contract in a comment block (lines 70‑78) above the sanitizer:
// INVARIANT: every server egress path that ships page-content strings MUST
// route through this sanitizer. handleCommandInternal wraps the final
// cr.result string …
By placing the sanitization logic at the only exit point of the command pipeline, gstack maintains this invariant without requiring individual developers to remember validation calls.
Testing Strategy
The project includes comprehensive tests to verify that the sanitization pipeline functions correctly under edge cases:
test/server-sanitize-surrogates.test.ts– Confirms that lone surrogates are removed and that valid surrogate pairs (such as emoji characters) remain intact.test/telemetry.test.ts– Validates that server‑side telemetry strings are also sanitized before emission, ensuring that logging or analytics systems never receive malformed Unicode.
Summary
- Centralized validation: All egress strings pass through
handleCommandInternalinbrowse/src/server.ts, ensuring consistent Unicode sanitization. - Surrogate filtering: The
sanitizeLoneSurrogatesfunction removes isolated UTF‑16 surrogates in the range U+D800–U+DFFF while preserving valid surrogate pairs. - Defense in depth: A secondary
sanitizeBodycheck inbuildCommandResponseprovides redundant protection against escaped sequences. - Invariant guarantees: Architectural comments and test coverage enforce that no code path can emit unsanitized page content.
Frequently Asked Questions
Why does gstack target lone surrogates specifically?
Lone surrogates violate the JSON specification (RFC 8259) and cause parsing failures in strict decoders such as the Anthropic API. By stripping these code points at the server boundary, gstack ensures that downstream consumers can safely parse every response without encountering encoding errors.
What happens to invalid surrogate characters during sanitization?
Isolated high or low surrogates are removed entirely (replaced with empty strings). Valid surrogate pairs—where a high surrogate in the range 0xD800‑0xDBFF is immediately followed by a low surrogate in 0xDC00‑0xDFFF—are preserved intact, allowing emoji and other supplementary characters to pass through correctly.
Is every server response guaranteed to pass through the sanitizer?
Yes. The handleCommandInternal wrapper intercepts every CommandResult before it reaches the HTTP layer, and the buildCommandResponse function applies an additional sanitization layer. This dual‑layer approach ensures that all egress paths—including telemetry and error responses—emit safe Unicode.
Where is the primary sanitization logic located?
The main implementation resides in browse/src/server.ts, specifically within the sanitizeLoneSurrogates function (lines 63‑90) and its integration into the command pipeline through handleCommandInternal (lines 88‑94). Secondary guards are implemented in related pipeline utilities such as sanitizeBody.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →