fix(adapter-utils): read a serialized escape pair as one backslash run
A shell escape pair doubles its backslash with every serialization layer, so the continuation and opening escape-pair segments now consume the whole backslash run with the character it escapes. An escaped-space segment adjacent to a header value inside a serialized command is therefore part of the value at any depth. Claude-Session: https://claude.ai/code/session_01RYigf3eMFJjey9iKRApPGE
This commit is contained in:
parent
f675a08bbd
commit
205c82591b
|
|
@ -342,6 +342,27 @@ describe("redactCommandText header secrets", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("consumes an escaped-space continuation at every serialization depth", () => {
|
||||
// A shell escape pair doubles its backslash with each serialization
|
||||
// layer; the continuation reads the whole run as one pair.
|
||||
const bases = [
|
||||
'curl -H X-API-Key:"SECRET"\\ TAIL https://example.test',
|
||||
'curl -H X-API-Key:\\ SECRET https://example.test',
|
||||
'curl -H "X-API-Key: SECRET"\\ TAIL;echo safe',
|
||||
];
|
||||
for (const base of bases) {
|
||||
let text = base;
|
||||
for (let depth = 0; depth <= 2; depth += 1) {
|
||||
if (depth > 0) text = JSON.stringify(text);
|
||||
const output = redactCommandText(text);
|
||||
expect(output).not.toContain("SECRET");
|
||||
expect(output).not.toContain("TAIL");
|
||||
if (depth > 0) expect(() => JSON.parse(output)).not.toThrow();
|
||||
expect(redactCommandText(output)).toBe(output);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("redacts a bare apikey header value", () => {
|
||||
// Supabase sends the key under an unhyphenated `apikey` header.
|
||||
expect(redactCommandText("apikey: abc")).toBe(
|
||||
|
|
|
|||
|
|
@ -135,13 +135,13 @@ const COMMAND_SHELL_QUOTED_SEGMENT_PATTERNS = [
|
|||
String.raw`'[^'\r\n]*'`,
|
||||
String.raw`\$'(?:\\.|[^'\\\r\n])*'`,
|
||||
] as const;
|
||||
const COMMAND_SHELL_ESCAPE_PAIR_PATTERN = String.raw`\\[^\r\n]`;
|
||||
const COMMAND_SHELL_ESCAPE_PAIR_PATTERN = String.raw`\\+[^\r\n]`;
|
||||
// An opening escape pair carries the first byte of an unquoted value, as in
|
||||
// `X-API-Key:\ abc`. It excludes the escaped quote, so a `\"` opener falls to
|
||||
// the escaped branches. A deeper run such as `\\\"` opens with an escaped
|
||||
// backslash, which this pattern does accept; the escaped branches precede the
|
||||
// unquoted one in the alternation and take that value first.
|
||||
const COMMAND_SHELL_OPENING_ESCAPE_PAIR_PATTERN = String.raw`\\[^"\r\n]`;
|
||||
// `X-API-Key:\ abc`. The backslash run may be longer inside a serialized
|
||||
// command, where each layer doubles it. A run followed by a quote is excluded,
|
||||
// so a `\"` opener at any depth falls to the escaped branches, which precede
|
||||
// the unquoted one in the alternation.
|
||||
const COMMAND_SHELL_OPENING_ESCAPE_PAIR_PATTERN = String.raw`\\+[^"\r\n]`;
|
||||
// The first segment of an unquoted value is a raw token, bounded only by
|
||||
// whitespace, a quote, a backtick, or a backslash. A raw HTTP diagnostic
|
||||
// carries an opaque credential the same way, so a `;`, `|`, or `&` inside it
|
||||
|
|
|
|||
Loading…
Reference in New Issue