Tests: stub SSRF DNS pinning (#6619) (thanks @joshp123)
This commit is contained in:
parent
5676a6b38d
commit
991ed3ab58
4 changed files with 65 additions and 1 deletions
|
|
@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
|
||||||
|
|
||||||
- Security: guard skill installer downloads with SSRF checks (block private/localhost URLs).
|
- Security: guard skill installer downloads with SSRF checks (block private/localhost URLs).
|
||||||
- Media understanding: apply SSRF guardrails to provider fetches; allow private baseUrl overrides explicitly.
|
- Media understanding: apply SSRF guardrails to provider fetches; allow private baseUrl overrides explicitly.
|
||||||
|
- Tests: stub SSRF DNS pinning in web auto-reply + Gemini video coverage. (#6619) Thanks @joshp123.
|
||||||
|
|
||||||
## 2026.2.1
|
## 2026.2.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import { describe, expect, it } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import * as ssrf from "../../../infra/net/ssrf.js";
|
||||||
import { describeGeminiVideo } from "./video.js";
|
import { describeGeminiVideo } from "./video.js";
|
||||||
|
|
||||||
|
const TEST_NET_IP = "203.0.113.10";
|
||||||
|
|
||||||
const resolveRequestUrl = (input: RequestInfo | URL) => {
|
const resolveRequestUrl = (input: RequestInfo | URL) => {
|
||||||
if (typeof input === "string") {
|
if (typeof input === "string") {
|
||||||
return input;
|
return input;
|
||||||
|
|
@ -12,6 +15,28 @@ const resolveRequestUrl = (input: RequestInfo | URL) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("describeGeminiVideo", () => {
|
describe("describeGeminiVideo", () => {
|
||||||
|
let resolvePinnedHostnameSpy: ReturnType<typeof vi.spyOn> | undefined;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
resolvePinnedHostnameSpy = vi
|
||||||
|
.spyOn(ssrf, "resolvePinnedHostnameWithPolicy")
|
||||||
|
.mockImplementation(async (hostname) => {
|
||||||
|
// SSRF guard pins DNS; stub resolution to avoid live lookups in unit tests.
|
||||||
|
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
||||||
|
const addresses = [TEST_NET_IP];
|
||||||
|
return {
|
||||||
|
hostname: normalized,
|
||||||
|
addresses,
|
||||||
|
lookup: ssrf.createPinnedLookup({ hostname: normalized, addresses }),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
resolvePinnedHostnameSpy?.mockRestore();
|
||||||
|
resolvePinnedHostnameSpy = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
it("respects case-insensitive x-goog-api-key overrides", async () => {
|
it("respects case-insensitive x-goog-api-key overrides", async () => {
|
||||||
let seenKey: string | null = null;
|
let seenKey: string | null = null;
|
||||||
const fetchFn = async (_input: RequestInfo | URL, init?: RequestInit) => {
|
const fetchFn = async (_input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import * as ssrf from "../infra/net/ssrf.js";
|
||||||
|
|
||||||
|
const TEST_NET_IP = "203.0.113.10";
|
||||||
|
|
||||||
vi.mock("../agents/pi-embedded.js", () => ({
|
vi.mock("../agents/pi-embedded.js", () => ({
|
||||||
abortEmbeddedPiRun: vi.fn().mockReturnValue(false),
|
abortEmbeddedPiRun: vi.fn().mockReturnValue(false),
|
||||||
|
|
@ -95,13 +98,29 @@ const _makeSessionStore = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("web auto-reply", () => {
|
describe("web auto-reply", () => {
|
||||||
|
let resolvePinnedHostnameSpy: ReturnType<typeof vi.spyOn> | undefined;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
resetBaileysMocks();
|
resetBaileysMocks();
|
||||||
resetLoadConfigMock();
|
resetLoadConfigMock();
|
||||||
|
resolvePinnedHostnameSpy = vi
|
||||||
|
.spyOn(ssrf, "resolvePinnedHostname")
|
||||||
|
.mockImplementation(async (hostname) => {
|
||||||
|
// SSRF guard pins DNS; stub resolution to avoid live lookups in unit tests.
|
||||||
|
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
||||||
|
const addresses = [TEST_NET_IP];
|
||||||
|
return {
|
||||||
|
hostname: normalized,
|
||||||
|
addresses,
|
||||||
|
lookup: ssrf.createPinnedLookup({ hostname: normalized, addresses }),
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
resolvePinnedHostnameSpy?.mockRestore();
|
||||||
|
resolvePinnedHostnameSpy = undefined;
|
||||||
resetLogger();
|
resetLogger();
|
||||||
setLoggerOverride(null);
|
setLoggerOverride(null);
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import * as ssrf from "../infra/net/ssrf.js";
|
||||||
|
|
||||||
|
const TEST_NET_IP = "203.0.113.10";
|
||||||
|
|
||||||
vi.mock("../agents/pi-embedded.js", () => ({
|
vi.mock("../agents/pi-embedded.js", () => ({
|
||||||
abortEmbeddedPiRun: vi.fn().mockReturnValue(false),
|
abortEmbeddedPiRun: vi.fn().mockReturnValue(false),
|
||||||
|
|
@ -94,13 +97,29 @@ const _makeSessionStore = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("web auto-reply", () => {
|
describe("web auto-reply", () => {
|
||||||
|
let resolvePinnedHostnameSpy: ReturnType<typeof vi.spyOn> | undefined;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
resetBaileysMocks();
|
resetBaileysMocks();
|
||||||
resetLoadConfigMock();
|
resetLoadConfigMock();
|
||||||
|
resolvePinnedHostnameSpy = vi
|
||||||
|
.spyOn(ssrf, "resolvePinnedHostname")
|
||||||
|
.mockImplementation(async (hostname) => {
|
||||||
|
// SSRF guard pins DNS; stub resolution to avoid live lookups in unit tests.
|
||||||
|
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
||||||
|
const addresses = [TEST_NET_IP];
|
||||||
|
return {
|
||||||
|
hostname: normalized,
|
||||||
|
addresses,
|
||||||
|
lookup: ssrf.createPinnedLookup({ hostname: normalized, addresses }),
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
resolvePinnedHostnameSpy?.mockRestore();
|
||||||
|
resolvePinnedHostnameSpy = undefined;
|
||||||
resetLogger();
|
resetLogger();
|
||||||
setLoggerOverride(null);
|
setLoggerOverride(null);
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue