-
Notifications
You must be signed in to change notification settings - Fork 3k
Expand file tree
/
Copy pathinference-set-endpoint-security.test.ts
More file actions
133 lines (117 loc) · 5.49 KB
/
Copy pathinference-set-endpoint-security.test.ts
File metadata and controls
133 lines (117 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import { describe, expect, it, vi } from "vitest";
import { rewriteConfigUrlsWithDnsPinning } from "../sandbox/config";
import type { ConfigValue } from "../security/credential-filter";
import { normalizeCustomEndpointUrl } from "./inference-set";
describe("custom inference endpoint DNS pinning", () => {
it.each([
1024, 65535,
])("allows the exact OpenShell bridge exemption at port %i without DNS rewriting", async (port) => {
const rewriteUrl = vi.fn(async () => {
throw new Error("bridge exemption unexpectedly reached DNS validation");
});
await expect(
normalizeCustomEndpointUrl(`http://host.openshell.internal:${port}/v1/`, rewriteUrl),
).resolves.toBe(`http://host.openshell.internal:${port}/v1`);
expect(rewriteUrl).not.toHaveBeenCalled();
});
it.each([
["no explicit port", "http://host.openshell.internal/v1"],
["privileged port", "http://host.openshell.internal:1023/v1"],
["HTTPS bridge", "https://host.openshell.internal:1234/v1"],
["localhost", "http://localhost:1234/v1"],
["loopback", "http://127.0.0.1:1234/v1"],
["RFC1918", "http://10.0.0.1:1234/v1"],
["non-allowlisted internal DNS", "http://other.internal:1234/v1"],
])("rejects the adjacent %s bypass shape", async (_kind, endpointUrl) => {
const lookup = vi.fn(async () => [{ address: "10.0.0.8", family: 4 }]);
await expect(
normalizeCustomEndpointUrl(endpointUrl, (value) =>
rewriteConfigUrlsWithDnsPinning(value, lookup),
),
).rejects.toThrow(/endpoint-url is not allowed:.*private\/internal address/i);
});
it.each([
[
"shell metacharacters",
"http://public.example/v1$(id)",
/endpoint-url must contain only URL-safe ASCII characters\./,
],
[
"percent-encoded control characters",
"http://public.example/v1%0ainjected",
/endpoint-url must not contain percent-encoded control characters\./,
],
[
"a leading tab",
"\thttp://public.example/v1",
/endpoint-url must not contain control characters\./,
],
[
"a trailing newline",
"http://public.example/v1\n",
/endpoint-url must not contain control characters\./,
],
[
"a leading no-break space",
"\u00a0http://public.example/v1",
/endpoint-url must contain only URL-safe ASCII characters\./,
],
] as const)(
"rejects an endpoint URL with %s before DNS validation or any mutation (#9301)",
async (_label, endpointUrl, message) => {
const rewriteUrl = vi.fn(async () => {
throw new Error("unsafe endpoint unexpectedly reached DNS validation");
});
await expect(normalizeCustomEndpointUrl(endpointUrl, rewriteUrl)).rejects.toThrow(message);
expect(rewriteUrl).not.toHaveBeenCalled();
},
);
it("pins validated public HTTP endpoints before they become durable metadata", async () => {
const lookup = vi.fn(async () => [{ address: "93.184.216.34", family: 4 }]);
await expect(
normalizeCustomEndpointUrl("http://public-endpoint.example/v1/", (value) =>
rewriteConfigUrlsWithDnsPinning(value, lookup),
),
).resolves.toBe("http://93.184.216.34/v1");
expect(lookup).toHaveBeenCalledWith("public-endpoint.example", { all: true });
});
it.each([
["userinfo", "https://user:secret@public-endpoint.example/v1"],
["query", "https://public-endpoint.example/v1?api_key=secret"],
["fragment", "https://public-endpoint.example/v1#secret"],
])("rejects a source endpoint with %s instead of silently stripping it", async (_kind, endpointUrl) => {
const rewriteUrl = vi.fn(async (value: ConfigValue) => value);
const ensureAdapter = vi.fn(async () => "http://host.openshell.internal:11438/route/test");
await expect(
normalizeCustomEndpointUrl(endpointUrl, rewriteUrl, ensureAdapter),
).rejects.toThrow("without userinfo, query, or fragment components");
expect(rewriteUrl).not.toHaveBeenCalled();
expect(ensureAdapter).not.toHaveBeenCalled();
});
it("fails closed for DNS-backed HTTPS endpoints until runtime-aware pinning exists", async () => {
const lookup = vi.fn(async () => [{ address: "93.184.216.34", family: 4 }]);
await expect(
normalizeCustomEndpointUrl("https://public-endpoint.example/v1/", (value) =>
rewriteConfigUrlsWithDnsPinning(value, lookup),
),
).rejects.toThrow(/DNS-backed HTTPS URLs are not supported/);
});
it("adds the HTTPS Pin Runtime adapter hint only at the inference-set call site, not in the generic config validator's own message (#6141)", async () => {
const lookup = vi.fn(async () => [{ address: "93.184.216.34", family: 4 }]);
// The generic validator (also used by plain `config set` for arbitrary
// fields) must not mention inference set or the adapter -- it has no way
// to know the field it's validating is an inference endpoint.
await expect(
rewriteConfigUrlsWithDnsPinning("https://public-endpoint.example/v1/", lookup),
).rejects.toThrow(/^(?!.*(?:inference set|HTTPS Pin Runtime adapter)).*$/is);
// normalizeCustomEndpointUrl is only ever called for `inference set
// --endpoint-url`, so it appends the adapter-specific hint itself.
await expect(
normalizeCustomEndpointUrl("https://public-endpoint.example/v1/", (value) =>
rewriteConfigUrlsWithDnsPinning(value, lookup),
),
).rejects.toThrow(/HTTPS Pin Runtime adapter/);
});
});