fix v2 hardening review findings
This commit is contained in:
1 parent
b82a6a7abc
commit
a7eae3ff81
35 files changed
+1147
-277
No files matched your search
@@ -42,6 +42,14 @@ export function getCustomWidgetHttpUrlIssue(value: string): CustomWidgetHttpUrlI
|
||||
const rawHostname = withoutTrailingRootDot(hostAndPort.hostname);
|
||||
const parsedHostname = withoutTrailingRootDot(url.hostname);
|
||||
if (!rawHostname || !hasCanonicalParsedHostname(parsedHostname)) return "invalid";
|
||||
if (
|
||||
rawHostname.startsWith("[") &&
|
||||
parsedHostname.startsWith("[") &&
|
||||
!rawHostname.includes(".") &&
|
||||
rawHostname.toLowerCase() !== parsedHostname
|
||||
) {
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
if (looksNumeric(rawHostname) && !isStrictIpv4(rawHostname)) return "invalid";
|
||||
if (isStrictIpv4(parsedHostname) && rawHostname !== parsedHostname) return "invalid";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { createPinnedAgent } from "./network-policy";
|
||||
|
||||
const DISPATCHER_CLOSE_GRACE_MS = 1_000;
|
||||
export const DISPATCHER_CLOSE_GRACE_MS = 1_000;
|
||||
|
||||
type CustomWidgetDispatcher = ReturnType<typeof createPinnedAgent>;
|
||||
|
||||
@@ -31,7 +31,10 @@ function detachDispatcherDestroy(dispatcher: CustomWidgetDispatcher): void {
|
||||
}
|
||||
|
||||
function abortable<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {
|
||||
if (signal.aborted) return Promise.reject(signal.reason ?? createAbortError());
|
||||
if (signal.aborted) {
|
||||
void operation.catch(() => undefined);
|
||||
return Promise.reject(signal.reason ?? createAbortError());
|
||||
}
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const onAbort = () => {
|
||||
|
||||
@@ -15,6 +15,7 @@ vi.mock("../server/network-policy", async (importOriginal) => {
|
||||
});
|
||||
|
||||
import { executeCustomWidgetRequest, MAX_REQUEST_DURATION_MS } from "../server/request-executor";
|
||||
import { closeDispatcher, DISPATCHER_CLOSE_GRACE_MS } from "../server/request-dispatcher-lifecycle";
|
||||
import { resolveAndValidateHost } from "../server/network-policy";
|
||||
|
||||
const query = {
|
||||
@@ -83,7 +84,7 @@ describe("custom widget request deadline", () => {
|
||||
logError,
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
await vi.advanceTimersByTimeAsync(DISPATCHER_CLOSE_GRACE_MS);
|
||||
|
||||
await expect(request).resolves.toMatchObject({ ok: true, status: 200, data: {} });
|
||||
expect(close).toHaveBeenCalledOnce();
|
||||
@@ -133,6 +134,28 @@ describe("custom widget request deadline", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("consumes a late close rejection when cleanup starts after the deadline", async () => {
|
||||
const abortReason = new Error("request deadline elapsed");
|
||||
const closeFailure = new Error("late close failure");
|
||||
const controller = new AbortController();
|
||||
controller.abort(abortReason);
|
||||
let rejectClose: ((reason: Error) => void) | undefined;
|
||||
const close = vi.fn(
|
||||
() =>
|
||||
new Promise<void>((_resolve, reject) => {
|
||||
rejectClose = reject;
|
||||
}),
|
||||
);
|
||||
const destroy = vi.fn(async () => undefined);
|
||||
|
||||
await expect(closeDispatcher({ close, destroy } as never, controller.signal)).rejects.toBe(abortReason);
|
||||
rejectClose?.(closeFailure);
|
||||
await Promise.resolve();
|
||||
|
||||
expect(close).toHaveBeenCalledOnce();
|
||||
expect(destroy).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("redacts apiKeyQuery credentials from transport errors and structured log events", async () => {
|
||||
const apiKey = "api-key-query-secret";
|
||||
const request = vi.fn(async ({ path }: { path: string }) => {
|
||||
|
||||
@@ -128,6 +128,8 @@ describe("lean Custom Widget schema", () => {
|
||||
"https://example.com:65536",
|
||||
"https://[fe80::1%25eth0]",
|
||||
"https://[::ffff:192.168.001.001]",
|
||||
"https://[0:0:0:0:0:0:0:1]",
|
||||
"https://[2001:0db8::1]",
|
||||
])("rejects ambiguous persisted URL spelling %s for sources and icons", (url) => {
|
||||
expect(
|
||||
customWidgetDefinitionSchema.safeParse({
|
||||
|
||||
Reference in new issue
Block a user