test(e2e): add playwright e2e harness
Process and API-only Playwright harness: binary and image setup, server/CLI runners, fake Docker Engine socket, read-only database inspection, secret redaction in reports, seeded fuzzing, leak detection and harness self-checks.
This commit is contained in:
1 parent
a02eef13a7
commit
fe0cd4bf6d
33 files changed
+6370
No files matched your search
@@ -0,0 +1,168 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
/**
|
||||
* Go string quoting/unquoting compatible with strconv.Quote / strconv.Unquote,
|
||||
* as used by `token list` (DESCRIPTION column), slog's text handler and the
|
||||
* configuration error messages.
|
||||
*/
|
||||
|
||||
/** Characters Go's strconv.IsPrint accepts: letters, marks, numbers, punctuation, symbols, and ASCII space. */
|
||||
const PRINTABLE = /^[\p{L}\p{M}\p{N}\p{P}\p{S} ]$/u;
|
||||
|
||||
/**
|
||||
* Go strconv.Quote of a byte string (Buffer) or a JS string (encoded as UTF-8).
|
||||
* Note: relies on Node's Unicode tables; avoid code points newer than Go's tables in tests.
|
||||
*/
|
||||
export function goQuote(input: string | Buffer): string {
|
||||
const bytes = typeof input === 'string' ? Buffer.from(input, 'utf8') : input;
|
||||
let out = '"';
|
||||
let i = 0;
|
||||
while (i < bytes.length) {
|
||||
const b = bytes[i];
|
||||
if (b < 0x80) {
|
||||
i++;
|
||||
const c = String.fromCharCode(b);
|
||||
switch (c) {
|
||||
case '"':
|
||||
out += '\\"';
|
||||
continue;
|
||||
case '\\':
|
||||
out += '\\\\';
|
||||
continue;
|
||||
case '\x07':
|
||||
out += '\\a';
|
||||
continue;
|
||||
case '\b':
|
||||
out += '\\b';
|
||||
continue;
|
||||
case '\f':
|
||||
out += '\\f';
|
||||
continue;
|
||||
case '\n':
|
||||
out += '\\n';
|
||||
continue;
|
||||
case '\r':
|
||||
out += '\\r';
|
||||
continue;
|
||||
case '\t':
|
||||
out += '\\t';
|
||||
continue;
|
||||
case '\v':
|
||||
out += '\\v';
|
||||
continue;
|
||||
}
|
||||
if (b < 0x20 || b === 0x7f) out += '\\x' + hex2(b);
|
||||
else out += c;
|
||||
continue;
|
||||
}
|
||||
// Multi-byte: decode one UTF-8 sequence strictly
|
||||
const cp = decodeUtf8At(bytes, i);
|
||||
if (!cp) {
|
||||
out += '\\x' + hex2(b);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const ch = String.fromCodePoint(cp.codePoint);
|
||||
if (PRINTABLE.test(ch)) out += ch;
|
||||
else if (cp.codePoint < 0x10000) out += '\\u' + cp.codePoint.toString(16).padStart(4, '0');
|
||||
else out += '\\U' + cp.codePoint.toString(16).padStart(8, '0');
|
||||
i += cp.length;
|
||||
}
|
||||
return out + '"';
|
||||
}
|
||||
|
||||
function hex2(b: number): string {
|
||||
return b.toString(16).padStart(2, '0');
|
||||
}
|
||||
|
||||
/** Strict UTF-8 decode of the sequence at i (rejects overlongs, surrogates, > U+10FFFF). */
|
||||
function decodeUtf8At(buf: Buffer, i: number): { codePoint: number; length: number } | undefined {
|
||||
const b0 = buf[i];
|
||||
let len: number;
|
||||
let cp: number;
|
||||
let min: number;
|
||||
if (b0 >= 0xc2 && b0 <= 0xdf) {
|
||||
len = 2;
|
||||
cp = b0 & 0x1f;
|
||||
min = 0x80;
|
||||
} else if (b0 >= 0xe0 && b0 <= 0xef) {
|
||||
len = 3;
|
||||
cp = b0 & 0x0f;
|
||||
min = 0x800;
|
||||
} else if (b0 >= 0xf0 && b0 <= 0xf4) {
|
||||
len = 4;
|
||||
cp = b0 & 0x07;
|
||||
min = 0x10000;
|
||||
} else return undefined;
|
||||
if (i + len > buf.length) return undefined;
|
||||
for (let k = 1; k < len; k++) {
|
||||
const b = buf[i + k];
|
||||
if ((b & 0xc0) !== 0x80) return undefined;
|
||||
cp = (cp << 6) | (b & 0x3f);
|
||||
}
|
||||
if (cp < min || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff)) return undefined;
|
||||
return { codePoint: cp, length: len };
|
||||
}
|
||||
|
||||
/**
|
||||
* Go strconv.Unquote for double-quoted strings. Returns the raw bytes (descriptions may
|
||||
* contain invalid UTF-8). Throws on malformed input.
|
||||
*/
|
||||
export function goUnquoteBytes(quoted: string): Buffer {
|
||||
if (quoted.length < 2 || quoted[0] !== '"' || quoted[quoted.length - 1] !== '"') {
|
||||
throw new Error(`not a Go double-quoted string: ${JSON.stringify(quoted)}`);
|
||||
}
|
||||
const s = quoted.slice(1, -1);
|
||||
const out: number[] = [];
|
||||
const pushStr = (str: string) => out.push(...Buffer.from(str, 'utf8'));
|
||||
for (let i = 0; i < s.length; ) {
|
||||
const c = s[i];
|
||||
if (c === '"') throw new Error(`unescaped quote in Go string: ${JSON.stringify(quoted)}`);
|
||||
if (c !== '\\') {
|
||||
const cp = s.codePointAt(i)!;
|
||||
const ch = String.fromCodePoint(cp);
|
||||
pushStr(ch);
|
||||
i += ch.length;
|
||||
continue;
|
||||
}
|
||||
const e = s[i + 1];
|
||||
const simple: Record<string, number> = { a: 7, b: 8, f: 12, n: 10, r: 13, t: 9, v: 11, '\\': 92, '"': 34, "'": 39 };
|
||||
if (e in simple) {
|
||||
out.push(simple[e]);
|
||||
i += 2;
|
||||
} else if (e === 'x') {
|
||||
out.push(parseHex(s.slice(i + 2, i + 4), quoted));
|
||||
i += 4;
|
||||
} else if (e === 'u') {
|
||||
pushStr(String.fromCodePoint(parseHex(s.slice(i + 2, i + 6), quoted)));
|
||||
i += 6;
|
||||
} else if (e === 'U') {
|
||||
pushStr(String.fromCodePoint(parseHex(s.slice(i + 2, i + 10), quoted)));
|
||||
i += 10;
|
||||
} else if (e >= '0' && e <= '7') {
|
||||
const oct = s.slice(i + 1, i + 4);
|
||||
if (!/^[0-7]{3}$/.test(oct)) throw new Error(`bad octal escape in ${JSON.stringify(quoted)}`);
|
||||
out.push(parseInt(oct, 8));
|
||||
i += 4;
|
||||
} else {
|
||||
throw new Error(`bad escape \\${e} in Go string ${JSON.stringify(quoted)}`);
|
||||
}
|
||||
}
|
||||
return Buffer.from(out);
|
||||
}
|
||||
|
||||
/** goUnquoteBytes decoded as UTF-8 (invalid sequences become U+FFFD). */
|
||||
export function goUnquote(quoted: string): string {
|
||||
return goUnquoteBytes(quoted).toString('utf8');
|
||||
}
|
||||
|
||||
function parseHex(h: string, ctx: string): number {
|
||||
if (!/^[0-9a-fA-F]+$/.test(h)) throw new Error(`bad hex escape in ${JSON.stringify(ctx)}`);
|
||||
return parseInt(h, 16);
|
||||
}
|
||||
|
||||
/** POSIX sh single-quoting: the result is always one shell word with the exact bytes of s. */
|
||||
export function shellQuote(s: string): string {
|
||||
return `'${s.split("'").join(`'\\''`)}'`;
|
||||
}
|
||||
Reference in new issue
Block a user