mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:00:42 +00:00
QA: address check triage findings
This commit is contained in:
@@ -74,4 +74,18 @@ describe("qa-lab server ui helpers", () => {
|
||||
expect(tryResolveUiAsset("/", uiDistDir, rootDir)).toBe(path.join(uiDistDir, "index.html"));
|
||||
expect(tryResolveUiAsset("/../dist-other/secret.txt", uiDistDir, rootDir)).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects malformed percent-encoded UI asset paths", async () => {
|
||||
const uiDistDir = await mkdtemp(path.join(os.tmpdir(), "qa-lab-ui-malformed-"));
|
||||
cleanups.push(async () => {
|
||||
await rm(uiDistDir, { recursive: true, force: true });
|
||||
});
|
||||
await writeFile(
|
||||
path.join(uiDistDir, "index.html"),
|
||||
"<!doctype html><html><body>bundle-root</body></html>",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
expect(tryResolveUiAsset("/%E0%A4", uiDistDir, uiDistDir)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -269,7 +269,12 @@ export function tryResolveUiAsset(
|
||||
return null;
|
||||
}
|
||||
const safePath = pathname === "/" ? "/index.html" : pathname;
|
||||
const decoded = decodeURIComponent(safePath);
|
||||
let decoded: string;
|
||||
try {
|
||||
decoded = decodeURIComponent(safePath);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const candidate = path.resolve(distDir, `.${decoded.startsWith("/") ? decoded : `/${decoded}`}`);
|
||||
const relative = path.relative(distDir, candidate);
|
||||
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
||||
|
||||
@@ -179,6 +179,21 @@ describe("qa suite planning helpers", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores prototype-mutating keys in scenario startup config patches", () => {
|
||||
const scenarios = [
|
||||
makeQaSuiteTestScenario("polluted", {
|
||||
gatewayConfigPatch: JSON.parse(
|
||||
`{"plugins":{"entries":{}},"__proto__":{"polluted":true},"constructor":{"prototype":{"polluted":true}}}`,
|
||||
) as Record<string, unknown>,
|
||||
}),
|
||||
];
|
||||
|
||||
const patch = collectQaSuiteGatewayConfigPatch(scenarios);
|
||||
|
||||
expect(patch).toEqual({ plugins: { entries: {} } });
|
||||
expect(({} as { polluted?: boolean }).polluted).toBeUndefined();
|
||||
});
|
||||
|
||||
it("collects gateway runtime options across selected scenarios", () => {
|
||||
const scenarios = [
|
||||
makeQaSuiteTestScenario("plain"),
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { QaTransportId } from "./qa-transport-registry.js";
|
||||
import { readQaBootstrapScenarioCatalog } from "./scenario-catalog.js";
|
||||
|
||||
const DEFAULT_QA_SUITE_CONCURRENCY = 64;
|
||||
const QA_MERGE_PATCH_BLOCKED_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
||||
|
||||
type QaSeedScenario = ReturnType<typeof readQaBootstrapScenarioCatalog>["scenarios"][number];
|
||||
|
||||
@@ -108,6 +109,9 @@ function applyQaMergePatch(base: unknown, patch: unknown): unknown {
|
||||
}
|
||||
const result = isQaPlainObject(base) ? { ...base } : {};
|
||||
for (const [key, value] of Object.entries(patch)) {
|
||||
if (QA_MERGE_PATCH_BLOCKED_KEYS.has(key)) {
|
||||
continue;
|
||||
}
|
||||
if (value === null) {
|
||||
delete result[key];
|
||||
continue;
|
||||
|
||||
@@ -238,8 +238,9 @@ describe("matrix driver client", () => {
|
||||
expect(requests[0]?.url).toBe(
|
||||
"http://127.0.0.1:28008/_matrix/media/v3/upload?filename=red-top-blue-bottom.png",
|
||||
);
|
||||
expect(requests[0]?.body instanceof Uint8Array || Buffer.isBuffer(requests[0]?.body)).toBe(
|
||||
true,
|
||||
expect(requests[0]?.body).toBeInstanceOf(Uint8Array);
|
||||
expect(Array.from(requests[0]?.body as Uint8Array)).toEqual(
|
||||
Array.from(Buffer.from("png-bytes")),
|
||||
);
|
||||
expect(requests[1]?.url).toContain(
|
||||
"/_matrix/client/v3/rooms/!room%3Amatrix-qa.test/send/m.room.message/",
|
||||
|
||||
@@ -263,6 +263,10 @@ async function uploadMatrixQaContent(params: {
|
||||
if (fileName) {
|
||||
url.searchParams.set("filename", fileName);
|
||||
}
|
||||
const uploadBody: Uint8Array<ArrayBuffer> =
|
||||
params.buffer.buffer instanceof ArrayBuffer
|
||||
? new Uint8Array(params.buffer.buffer, params.buffer.byteOffset, params.buffer.byteLength)
|
||||
: Uint8Array.from(params.buffer);
|
||||
const response = await params.fetchImpl(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -270,7 +274,7 @@ async function uploadMatrixQaContent(params: {
|
||||
"content-type": params.contentType ?? "application/octet-stream",
|
||||
...(params.accessToken ? { authorization: `Bearer ${params.accessToken}` } : {}),
|
||||
},
|
||||
body: new Uint8Array(params.buffer),
|
||||
body: uploadBody,
|
||||
signal: AbortSignal.timeout(20_000),
|
||||
});
|
||||
const body = (await response.json().catch(() => ({}))) as {
|
||||
|
||||
@@ -159,6 +159,27 @@ describe("matrix observed event normalization", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("treats filename-like Matrix media bodies as attachment filenames", () => {
|
||||
expect(
|
||||
normalizeMatrixQaObservedEvent("!room:matrix-qa.test", {
|
||||
event_id: "$image",
|
||||
sender: "@sut:matrix-qa.test",
|
||||
type: "m.room.message",
|
||||
content: {
|
||||
body: "qa-lighthouse.png",
|
||||
msgtype: "m.image",
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
attachment: {
|
||||
kind: "image",
|
||||
filename: "qa-lighthouse.png",
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("normalizes membership events with explicit membership kind", () => {
|
||||
expect(
|
||||
normalizeMatrixQaObservedEvent("!room:matrix-qa.test", {
|
||||
|
||||
@@ -104,6 +104,10 @@ function resolveMatrixQaAttachmentKind(msgtype: string | undefined) {
|
||||
}
|
||||
}
|
||||
|
||||
function isLikelyMatrixQaFilenameBody(value: string) {
|
||||
return !value.includes("\n") && /\.[a-z0-9][a-z0-9._-]{0,24}$/i.test(value);
|
||||
}
|
||||
|
||||
function resolveMatrixQaAttachmentSummary(params: {
|
||||
body?: string;
|
||||
filename?: string;
|
||||
@@ -114,10 +118,14 @@ function resolveMatrixQaAttachmentSummary(params: {
|
||||
return undefined;
|
||||
}
|
||||
const body = params.body?.trim() ?? "";
|
||||
const filename = params.filename?.trim() ?? "";
|
||||
const explicitFilename = params.filename?.trim() ?? "";
|
||||
const inferredFilename =
|
||||
!explicitFilename && body && isLikelyMatrixQaFilenameBody(body) ? body : "";
|
||||
const filename = explicitFilename || inferredFilename;
|
||||
const caption = body && body !== filename ? body : "";
|
||||
return {
|
||||
kind,
|
||||
...(body && body !== filename ? { caption: body } : {}),
|
||||
...(caption ? { caption } : {}),
|
||||
...(filename ? { filename } : {}),
|
||||
};
|
||||
}
|
||||
@@ -164,6 +172,11 @@ export function normalizeMatrixQaObservedEvent(
|
||||
type === "m.reaction" && typeof relatesTo?.event_id === "string"
|
||||
? relatesTo.event_id
|
||||
: undefined;
|
||||
const attachment = resolveMatrixQaAttachmentSummary({
|
||||
body: typeof messageContent.body === "string" ? messageContent.body : undefined,
|
||||
filename: normalizedFilename,
|
||||
msgtype: normalizedMsgtype,
|
||||
});
|
||||
|
||||
return {
|
||||
kind: resolveMatrixQaObservedEventKind({ msgtype: normalizedMsgtype, type }),
|
||||
@@ -208,18 +221,6 @@ export function normalizeMatrixQaObservedEvent(
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(resolveMatrixQaAttachmentSummary({
|
||||
body: typeof messageContent.body === "string" ? messageContent.body : undefined,
|
||||
filename: normalizedFilename,
|
||||
msgtype: normalizedMsgtype,
|
||||
})
|
||||
? {
|
||||
attachment: resolveMatrixQaAttachmentSummary({
|
||||
body: typeof messageContent.body === "string" ? messageContent.body : undefined,
|
||||
filename: normalizedFilename,
|
||||
msgtype: normalizedMsgtype,
|
||||
}),
|
||||
}
|
||||
: {}),
|
||||
...(attachment ? { attachment } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user