mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-21 06:54:59 +00:00
fix(feishu): address fifth-round codex bot review feedback
- parseColorMarkup: restrict opening tag regex to known colour/style names (bg:*, bold, red, orange, yellow, green, blue, purple, grey/gray) so that ordinary bracket tokens like [Q1] can no longer consume a subsequent real closing tag ([/red]) and corrupt the surrounding styled spans. Unknown tags now fall through to the plain-text alternatives and are emitted literally. - resolveUploadInput: estimate decoded byte count from base64 input length (ceil(len * 3 / 4)) BEFORE allocating the full Buffer, preventing oversized payloads from spiking memory before the maxBytes limit is enforced. Applies to both the data-URI branch and the plain-base64 branch.
This commit is contained in:
@@ -55,13 +55,22 @@ interface Segment {
|
||||
*/
|
||||
export function parseColorMarkup(content: string): Segment[] {
|
||||
const segments: Segment[] = [];
|
||||
// Match [tag]...[/tag], plain text, or a bare '[' that is not part of a
|
||||
// complete tag pair. Without the trailing `|\[` fallback, a '[' that has no
|
||||
// matching '[/...]' closer (e.g. "[Q1]" with no "[/...]") would be silently
|
||||
// dropped, corrupting the surrounding text. The closing tag name is not
|
||||
// validated against the opening tag: [red]text[/green] is treated as
|
||||
// [red]text[/red] — opening tag style applies, closing tag is consumed.
|
||||
const tagPattern = /\[([^\]]+)\](.*?)\[\/(?:[^\]]+)\]|([^[]+|\[)/gs;
|
||||
// Only [known_tag]...[/...] pairs are treated as markup. Using an open
|
||||
// pattern like \[([^\]]+)\] would match any bracket token — e.g. [Q1] —
|
||||
// and cause it to consume a later real closing tag ([/red]), silently
|
||||
// corrupting the surrounding styled spans. Restricting the opening tag to
|
||||
// the set of recognised colour/style names prevents that: [Q1] does not
|
||||
// match the tag alternative and each of its characters falls through to the
|
||||
// plain-text alternatives instead.
|
||||
//
|
||||
// Closing tag name is still not validated against the opening tag:
|
||||
// [red]text[/green] is treated as [red]text[/red] — opening style applies
|
||||
// and the closing tag is consumed regardless of its name.
|
||||
const KNOWN = "(?:bg:[a-z]+|bold|red|orange|yellow|green|blue|purple|gr[ae]y)";
|
||||
const tagPattern = new RegExp(
|
||||
`\\[(${KNOWN}(?:\\s+${KNOWN})*)\\](.*?)\\[\\/(?:[^\\]]+)\\]|([^[]+|\\[)`,
|
||||
"gis",
|
||||
);
|
||||
let match;
|
||||
|
||||
while ((match = tagPattern.exec(content)) !== null) {
|
||||
|
||||
@@ -413,10 +413,15 @@ async function resolveUploadInput(
|
||||
const [header, data] = imageInput.split(",");
|
||||
const mimeMatch = header.match(/data:([^;]+)/);
|
||||
const ext = mimeMatch?.[1]?.split("/")[1] ?? "png";
|
||||
const buffer = Buffer.from(data, "base64");
|
||||
if (buffer.length > maxBytes) {
|
||||
throw new Error(`Image data URI exceeds limit: ${buffer.length} bytes > ${maxBytes} bytes`);
|
||||
// Estimate decoded byte count from base64 length BEFORE allocating the
|
||||
// full buffer to avoid spiking memory on oversized payloads.
|
||||
const estimatedBytes = Math.ceil((data.length * 3) / 4);
|
||||
if (estimatedBytes > maxBytes) {
|
||||
throw new Error(
|
||||
`Image data URI exceeds limit: estimated ${estimatedBytes} bytes > ${maxBytes} bytes`,
|
||||
);
|
||||
}
|
||||
const buffer = Buffer.from(data, "base64");
|
||||
return { buffer, fileName: explicitFileName ?? `image.${ext}` };
|
||||
}
|
||||
|
||||
@@ -459,13 +464,18 @@ async function resolveUploadInput(
|
||||
`Use a data URI (data:image/png;base64,...) or a local file path instead.`,
|
||||
);
|
||||
}
|
||||
// Estimate decoded byte count from base64 length BEFORE allocating the
|
||||
// full buffer to avoid spiking memory on oversized payloads.
|
||||
const estimatedBytes = Math.ceil((trimmed.length * 3) / 4);
|
||||
if (estimatedBytes > maxBytes) {
|
||||
throw new Error(
|
||||
`Base64 image exceeds limit: estimated ${estimatedBytes} bytes > ${maxBytes} bytes`,
|
||||
);
|
||||
}
|
||||
const buffer = Buffer.from(trimmed, "base64");
|
||||
if (buffer.length === 0) {
|
||||
throw new Error("Base64 image decoded to empty buffer; check the input.");
|
||||
}
|
||||
if (buffer.length > maxBytes) {
|
||||
throw new Error(`Base64 image exceeds limit: ${buffer.length} bytes > ${maxBytes} bytes`);
|
||||
}
|
||||
return { buffer, fileName: explicitFileName ?? "image.png" };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user