fix(admin): repair model discovery and simplify model setup
Staging Backend Quality Gate / validate (push) Successful in 14m16s
Staging Backend Quality Gate / publish (push) Successful in 1h2m51s

This commit is contained in:
Jesse_Chen
2026-08-08 17:29:17 +08:00
parent f1a4db8a90
commit fc47939ba9
9 changed files with 143 additions and 89 deletions
+17 -1
View File
@@ -2492,7 +2492,7 @@
## BUG-146 | staging 后台写请求把 Caddy 上游协议误当公开来源
- 状态:resolvedlocal candidate,未 push / deploy
- 状态:resolved
- 首次发现:2026-08-07
- 最近更新:2026-08-07
- 影响面:所有经 `requireAdminMutation` 的后台写接口、`admin.staging.jyotisha.chat` 模型管理写操作;普通 staging 用户域名、其他后台功能的通用 `ReasonActionModal` 与 production 未改动。
@@ -2520,3 +2520,19 @@
- 相关记录:BUG-124、BUG-135、BUG-145、BUG-146
- 复发自:无
- 修复版本:待本次 staging SHA
## BUG-148 | Node 22/24 all-address lookup 使模型发现 DNS pinning 失效
- 状态:resolved
- 首次发现:2026-08-08
- 最近更新:2026-08-08
- 影响面:共享 `requestAllowedModelProvider` 的固定 DNS HTTPS 请求;OpenAI-compatible 供应商模型发现无法到达已通过公网 SSRF 校验的上游。支付网关、SSRF 边界、重定向拒绝、超时与响应大小限制未放宽。
- 用户现象:staging Web 容器内使用同一 DNS pinning/request 链路请求模型列表时失败,脱敏错误为 `ERR_INVALID_IP_ADDRESS`,已固定的地址族为 IPv4。
- 触发条件:Node 22/24 的 `https.request` / `net.connect``lookup` 选项 `all=true` 调用自定义 DNS callback;旧实现无条件调用 `callback(null, address, family)`,而 all-address 契约要求第二参数为 `[{ address, family }]`
- 根因:共享 HTTPS helper 只实现了旧的单地址 lookup callback 形态,没有根据 Node 传入的 `options.all` 切换返回值;因此安全校验和 DNS 解析均成功后,Node 在建连前把字符串结果按地址数组读取并抛出 `ERR_INVALID_IP_ADDRESS`。discover route 的 provider 读取、密钥解密、`/models` URL、响应解析和错误翻译均不是本次失败根因。
- 修复:抽出 `pinnedAddressLookup` 供共享 HTTPS 请求使用;`options.all=true` 时返回单元素已验证地址数组,其他模式继续返回 `address, family`。仍只使用已通过完整公网校验的固定地址,不重新解析、不跟随重定向,也不删除任何 SSRF/信任边界校验。
- 验证:`npx tsx --test tests/model-provider-encrypted-credentials.test.ts` 的真实本地 TCP 回归在修复前稳定失败为 `ERR_INVALID_IP_ADDRESS`4 passed / 1 failed),修复后对 `autoSelectFamily=true` 的 all-address 模式和 `autoSelectFamily=false` 的单地址模式均实际建连成功(5 passed / 0 failed)。
- 防复发:DNS-pinned 请求测试必须通过 Node 网络栈实际调用自定义 lookup,并同时覆盖地址数组与单地址 callback 契约;不得用只匹配源码字符串的断言替代该回归。
- 相关记录:BUG-146、BUG-147
- 复发自:无
- 修复版本:2026-08-08 staging 变更
@@ -0,0 +1,8 @@
export async function createDiscoveredModelId(providerId: string, providerModel: string) {
const digest = await globalThis.crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(`${providerId}\0${providerModel}`),
);
const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
return `m-${hex.slice(0, 62)}`;
}
@@ -24,6 +24,7 @@ import {
import { useCallback, useEffect, useState } from "react";
import { adminRequestJson, type AdminIdentity } from "@/lib/admin/providers";
import { createDiscoveredModelId } from "./model-id";
import { formatAdminDate } from "./resource-table";
const { Text } = Typography;
@@ -72,10 +73,7 @@ type ProviderForm = {
apiKey?: string;
enabled: boolean;
};
type ModelForm = Omit<ModelVersion, "id" | "configId" | "version" | "providerCode" | "status" | "createdAt" | "publishedAt" | "settings"> & {
versionId?: string;
settingsJson: string;
};
type ModelForm = Pick<ModelVersion, "providerId" | "providerModel" | "creditCost" | "enabled" | "isDefault">;
type ModelsPayload = { data: ModelVersion[]; total: number; providers: Provider[] };
type DiscoveredModel = { id: string; label?: string };
@@ -106,7 +104,6 @@ export default function ModelManagement() {
const [modelForm] = Form.useForm<ModelForm>();
const [filterForm] = Form.useForm<ModelFilters>();
const selectedProviderId = Form.useWatch("providerId", modelForm);
const selectedProviderModel = Form.useWatch("providerModel", modelForm);
const [models, setModels] = useState<ModelVersion[]>([]);
const [providers, setProviders] = useState<Provider[]>([]);
const [loading, setLoading] = useState(true);
@@ -167,38 +164,19 @@ export default function ModelManagement() {
function openModel(model?: ModelVersion, providerId?: string) {
setEditingModel(model ?? null);
setDiscoveredModels([]);
setDiscoveredModels(model ? [{ id: model.providerModel, label: model.label }] : []);
modelForm.setFieldsValue(model ? {
modelId: model.modelId,
versionId: model.id,
providerId: model.providerId,
label: model.label,
description: model.description,
providerModel: model.providerModel,
modelTier: model.modelTier,
creditCost: model.creditCost,
contextWindow: model.contextWindow,
inputCostMicrousdPerMillion: model.inputCostMicrousdPerMillion,
outputCostMicrousdPerMillion: model.outputCostMicrousdPerMillion,
enabled: model.enabled,
isDefault: model.isDefault,
fallbackModelId: model.fallbackModelId,
settingsJson: JSON.stringify(model.settings, null, 2),
} : {
modelId: "",
providerId: providerId ?? providers[0]?.id,
label: "",
description: "",
providerModel: "",
modelTier: "standard",
creditCost: 1,
contextWindow: null,
inputCostMicrousdPerMillion: 0,
outputCostMicrousdPerMillion: 0,
enabled: false,
isDefault: false,
fallbackModelId: null,
settingsJson: "{}",
});
setModelOpen(true);
}
@@ -239,7 +217,7 @@ export default function ModelManagement() {
body: JSON.stringify({ providerId }),
});
setDiscoveredModels(payload.data);
if (!payload.data.length) message.info("未发现可用模型,可继续手工输入");
if (!payload.data.length) message.info("未发现可用模型,请检查供应商配置后重试");
} catch (error) {
message.error(error instanceof Error ? error.message : "获取模型列表失败");
} finally {
@@ -247,46 +225,30 @@ export default function ModelManagement() {
}
}
function selectDiscoveredModel(providerModel: string) {
const discovered = discoveredModels.find((item) => item.id === providerModel);
const values = modelForm.getFieldsValue(["modelId", "label"]);
modelForm.setFieldsValue({
providerModel,
...(!values.modelId ? {
modelId: providerModel.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "model",
} : {}),
...(!values.label ? { label: discovered?.label?.trim() || providerModel } : {}),
});
}
async function saveModel(values: ModelForm) {
setSaving(true);
try {
let settings: unknown;
try {
settings = JSON.parse(values.settingsJson);
} catch {
throw new Error("设置 JSON 格式不正确");
}
const discovered = discoveredModels.find((item) => item.id === values.providerModel);
const modelId = editingModel?.modelId ?? await createDiscoveredModelId(values.providerId, values.providerModel);
await adminRequestJson("/api/admin/models", {
method: "POST",
body: JSON.stringify({
action: "saveDraft",
modelId: values.modelId.trim(),
modelId,
versionId: editingModel?.id ?? null,
providerId: values.providerId,
label: values.label.trim(),
description: values.description?.trim() ?? "",
label: editingModel?.label ?? (discovered?.label?.trim() || values.providerModel).slice(0, 60),
providerModel: values.providerModel.trim(),
modelTier: values.modelTier,
creditCost: values.creditCost,
contextWindow: values.contextWindow ?? null,
inputCostMicrousdPerMillion: values.inputCostMicrousdPerMillion,
outputCostMicrousdPerMillion: values.outputCostMicrousdPerMillion,
enabled: values.enabled,
description: editingModel?.description ?? "",
modelTier: editingModel?.modelTier ?? "standard",
contextWindow: editingModel?.contextWindow ?? null,
inputCostMicrousdPerMillion: editingModel?.inputCostMicrousdPerMillion ?? 0,
outputCostMicrousdPerMillion: editingModel?.outputCostMicrousdPerMillion ?? 0,
isDefault: values.isDefault,
fallbackModelId: values.fallbackModelId?.trim() || null,
settings,
fallbackModelId: editingModel?.fallbackModelId ?? null,
settings: editingModel?.settings ?? {},
}),
});
message.success("模型草稿已保存");
@@ -415,39 +377,40 @@ export default function ModelManagement() {
</Form>
</Modal>
<Modal title={editingModel ? `编辑 ${editingModel.modelId}` : "新增模型草稿"} open={modelOpen} width={860} okText="保存草稿" cancelText="取消" confirmLoading={saving} onOk={() => modelForm.submit()} onCancel={() => setModelOpen(false)} destroyOnHidden>
<Modal title={editingModel ? `编辑 ${editingModel.modelId}` : "新增模型草稿"} open={modelOpen} width={720} okText="保存草稿" cancelText="取消" confirmLoading={saving} onOk={() => modelForm.submit()} onCancel={() => setModelOpen(false)} destroyOnHidden>
<Form<ModelForm> form={modelForm} layout="vertical" onFinish={saveModel}>
<Row gutter={16}>
<Col xs={24} md={8}><Form.Item name="modelId" label="模型 ID" rules={[{ required: true }, { pattern: /^[a-z0-9][a-z0-9._-]{0,63}$/ }]}><Input disabled={Boolean(editingModel)} /></Form.Item></Col>
<Col xs={24} md={8}><Form.Item name="label" label="显示名称" rules={[{ required: true }]}><Input /></Form.Item></Col>
<Col xs={24} md={8}><Form.Item label="供应商" required><Space.Compact block><Form.Item name="providerId" noStyle rules={[{ required: true, message: "请选择供应商" }]}><Select onChange={() => setDiscoveredModels([])} options={providers.map((item) => ({ value: item.id, label: `${item.name} (${item.code})` }))} /></Form.Item><Button loading={discovering} disabled={!selectedProviderId} onClick={() => void discoverModels(selectedProviderId)}></Button></Space.Compact></Form.Item></Col>
</Row>
<Form.Item label="发现的模型" extra="选择后会填入供应商模型名;模型 ID 和显示名称仅在为空时自动补全。">
<Form.Item label="供应商" required>
<Space.Compact block>
<Form.Item name="providerId" noStyle rules={[{ required: true, message: "请选择供应商" }]}>
<Select
onChange={() => {
setDiscoveredModels([]);
modelForm.setFieldValue("providerModel", "");
}}
options={providers.map((item) => ({ value: item.id, label: `${item.name} (${item.code})` }))}
/>
</Form.Item>
<Button loading={discovering} disabled={!selectedProviderId} onClick={() => void discoverModels(selectedProviderId)}></Button>
</Space.Compact>
</Form.Item>
<Form.Item name="providerModel" label="模型" rules={[{ required: true, message: "请从供应商模型列表中选择" }]} extra="获取列表后选择供应商提供的模型。">
<Select
showSearch
allowClear
optionFilterProp="label"
placeholder={selectedProviderId ? "获取模型列表,或在下方手工输入" : "请先选择供应商"}
placeholder={selectedProviderId ? "获取并选择模型" : "请先选择供应商"}
disabled={!selectedProviderId || !discoveredModels.length}
value={discoveredModels.some((item) => item.id === selectedProviderModel) ? selectedProviderModel : undefined}
options={discoveredModels.map((item) => ({ value: item.id, label: item.label ? `${item.label} (${item.id})` : item.id }))}
onSelect={selectDiscoveredModel}
/>
</Form.Item>
<Row gutter={16}>
<Col xs={24} md={12}><Form.Item name="providerModel" label="供应商模型名(可手工输入)" rules={[{ required: true }]}><Input /></Form.Item></Col>
<Col xs={24} md={6}><Form.Item name="modelTier" label="模型档位" rules={[{ required: true }]}><Select options={Object.entries(modelTierLabels).map(([value, label]) => ({ value, label }))} /></Form.Item></Col>
<Col xs={24} md={6}><Form.Item name="creditCost" label="单次点数" rules={[{ required: true }]}><InputNumber min={1} precision={0} style={{ width: "100%" }} /></Form.Item></Col>
<Col xs={24} md={12}><Form.Item name="creditCost" label="单次点数" rules={[{ required: true }]}><InputNumber min={1} precision={0} style={{ width: "100%" }} /></Form.Item></Col>
<Col xs={24} md={12}>
<Space size="large">
<Form.Item name="enabled" label="启用" valuePropName="checked"><Switch /></Form.Item>
<Form.Item name="isDefault" label="默认模型" valuePropName="checked" dependencies={["enabled"]} rules={[({ getFieldValue }) => ({ validator(_, value) { return value && !getFieldValue("enabled") ? Promise.reject(new Error("默认模型必须启用")) : Promise.resolve(); } })]}><Switch /></Form.Item>
</Space>
</Col>
</Row>
<Form.Item name="description" label="说明"><Input.TextArea rows={2} /></Form.Item>
<Row gutter={16}>
<Col xs={24} md={8}><Form.Item name="contextWindow" label="上下文窗口"><InputNumber min={1} precision={0} style={{ width: "100%" }} /></Form.Item></Col>
<Col xs={24} md={8}><Form.Item name="inputCostMicrousdPerMillion" label="输入成本(微美元/百万 Token" rules={[{ required: true }]}><InputNumber min={0} precision={0} style={{ width: "100%" }} /></Form.Item></Col>
<Col xs={24} md={8}><Form.Item name="outputCostMicrousdPerMillion" label="输出成本(微美元/百万 Token" rules={[{ required: true }]}><InputNumber min={0} precision={0} style={{ width: "100%" }} /></Form.Item></Col>
</Row>
<Form.Item name="fallbackModelId" label="回退模型 ID"><Input allowClear /></Form.Item>
<Form.Item name="settingsJson" label="设置 JSON" rules={[{ required: true }]}><Input.TextArea rows={5} spellCheck={false} /></Form.Item>
<Space size="large"><Form.Item name="enabled" label="启用" valuePropName="checked"><Switch /></Form.Item><Form.Item name="isDefault" label="默认模型" valuePropName="checked" dependencies={["enabled"]} rules={[({ getFieldValue }) => ({ validator(_, value) { return value && !getFieldValue("enabled") ? Promise.reject(new Error("默认模型必须启用")) : Promise.resolve(); } })]}><Switch /></Form.Item></Space>
</Form>
</Modal>
<Modal
+9 -2
View File
@@ -1,6 +1,6 @@
import { promises as dns } from "node:dns";
import https from "node:https";
import { isIP } from "node:net";
import { isIP, type LookupFunction } from "node:net";
const blockedHostnames = new Set([
"localhost",
@@ -17,6 +17,13 @@ type HostLookup = (
) => Promise<readonly ResolvedAddress[]>;
type ModelProviderEnvironment = Readonly<Record<string, string | undefined>>;
export function pinnedAddressLookup(pinned: ResolvedAddress): LookupFunction {
return (_hostname, options, callback) => {
if (options.all) callback(null, [pinned]);
else callback(null, pinned.address, pinned.family);
};
}
function blockedIpv4(address: string) {
const parts = address.split(".").map(Number);
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return true;
@@ -166,7 +173,7 @@ async function requestPinnedHttps(
agent: false,
headers,
servername: isIP(hostname) ? undefined : hostname,
lookup: (_hostname, _options, callback) => callback(null, pinned.address, pinned.family),
lookup: pinnedAddressLookup(pinned),
}, (response) => {
const status = response.statusCode ?? 0;
if (status >= 300 && status < 400) {
+27
View File
@@ -0,0 +1,27 @@
import assert from "node:assert/strict";
import test from "node:test";
import { createDiscoveredModelId } from "../src/components/admin/model-id.ts";
const providerA = "11111111-1111-4111-8111-111111111111";
const providerB = "22222222-2222-4222-8222-222222222222";
test("discovered model IDs are deterministic, distinct, and API-compatible", async () => {
const inputs = [
[providerA, "gpt-4o"],
[providerB, "gpt-4o"],
[providerA, "GPT-4o"],
[providerA, " gpt-4o"],
[providerA, "gpt-4o "],
[providerA, "模型-α"],
] as const;
const ids = await Promise.all(inputs.map(([providerId, providerModel]) => createDiscoveredModelId(providerId, providerModel)));
assert.equal(new Set(ids).size, inputs.length);
assert.equal(ids[0], "m-f1966f65ba46e2c192e274f77c120dbc90f32e6caa2bcd75bb52e4f985b8cb");
assert.equal(await createDiscoveredModelId(providerA, "gpt-4o"), ids[0]);
for (const id of ids) {
assert.equal(id.length, 64);
assert.match(id, /^m-[0-9a-f]{62}$/);
}
});
@@ -23,17 +23,47 @@ test("provider form keeps codes server-owned and API keys write-only", () => {
assert.match(component, /anthropic: "Anthropic 官方"/);
});
test("model discovery stays searchable with a manual provider model fallback", () => {
test("model discovery is the only model picker and generates system fields", () => {
assert.match(component, /onClick=\{\(\) => void discoverModels\(selectedProviderId\)\}>获取模型列表<\/Button>/);
assert.doesNotMatch(component, /title="获取供应商模型列表"[\s\S]*ReasonActionModal/);
assert.match(component, /adminRequestJson<DiscoveredModelsPayload>\("\/api\/admin\/models\/discover"/);
assert.match(component, /body: JSON\.stringify\(\{ providerId \}\)/);
assert.match(component, /获取模型列表/);
assert.match(component, /<Select[\s\S]*showSearch[\s\S]*optionFilterProp="label"[\s\S]*onSelect=\{selectDiscoveredModel\}/);
assert.match(component, /name="providerModel" label="供应商模型名(可手工输入)"/);
assert.match(component, /providerModel\.toLowerCase\(\)\.replace\(\/\[\^a-z0-9\]\+\/g, "-"\)/);
assert.match(component, /!values\.modelId/);
assert.match(component, /!values\.label/);
assert.match(component, /未发现可用模型,请检查供应商配置后重试/);
assert.doesNotMatch(component, /可继续手工输入/);
assert.match(component, /name="providerModel" label="模型"[\s\S]*<Select[\s\S]*showSearch[\s\S]*optionFilterProp="label"/);
assert.doesNotMatch(component, /供应商模型名(可手工输入)|name="modelId"|name="label" label="显示名称"/);
assert.match(component, /import \{ createDiscoveredModelId \} from "\.\/model-id"/);
assert.match(component, /const modelId = editingModel\?\.modelId \?\? await createDiscoveredModelId\(values\.providerId, values\.providerModel\)/);
assert.doesNotMatch(component, /providerModel\.toLowerCase\(\)\.replace/);
assert.match(component, /label: editingModel\?\.label \?\? \(discovered\?\.label\?\.trim\(\) \|\| values\.providerModel\)\.slice\(0, 60\)/);
});
test("model form exposes only essential switches and credit cost", () => {
assert.match(component, /name="creditCost" label="单次点数"/);
assert.match(component, /name="enabled" label="启用"/);
assert.match(component, /name="isDefault" label="默认模型"/);
for (const field of [
"description",
"modelTier",
"contextWindow",
"inputCostMicrousdPerMillion",
"outputCostMicrousdPerMillion",
"fallbackModelId",
"settingsJson",
]) {
assert.doesNotMatch(component, new RegExp(`name=["']${field}["']`));
}
assert.doesNotMatch(component, /模型档位|上下文窗口|输入成本(微美元\/百万 Token)|输出成本(微美元\/百万 Token)|回退模型 ID|设置 JSON/);
});
test("hidden model fields preserve draft values or use database-compatible defaults", () => {
assert.match(component, /description: editingModel\?\.description \?\? ""/);
assert.match(component, /modelTier: editingModel\?\.modelTier \?\? "standard"/);
assert.match(component, /contextWindow: editingModel\?\.contextWindow \?\? null/);
assert.match(component, /inputCostMicrousdPerMillion: editingModel\?\.inputCostMicrousdPerMillion \?\? 0/);
assert.match(component, /outputCostMicrousdPerMillion: editingModel\?\.outputCostMicrousdPerMillion \?\? 0/);
assert.match(component, /fallbackModelId: editingModel\?\.fallbackModelId \?\? null/);
assert.match(component, /settings: editingModel\?\.settings \?\? \{\}/);
});
test("model mutations omit client reasons while the server keeps fixed audit reasons", () => {
+1 -1
View File
@@ -215,7 +215,7 @@ test("网关探测固定已验证公网 IP、保留 TLS hostname 且限制重定
assert.match(gatewayPolicy, /const resolved = await withinTimeout\([\s\S]*resolvePublicUrl\(value, options\.lookup \?\? defaultLookup\)/);
assert.match(gatewayPolicy, /const pinned = resolved\.addresses\[0\]!/);
assert.match(gatewayPolicy, /servername: isIP\(hostname\) \? undefined : hostname/);
assert.match(gatewayPolicy, /lookup: \(_hostname, _options, callback\) => callback\(null, pinned\.address, pinned\.family\)/);
assert.match(gatewayPolicy, /lookup: pinnedAddressLookup\(pinned\)/);
assert.match(gatewayPolicy, /requestPinnedHttps\([\s\S]*resolved,[\s\S]*"HEAD"/);
assert.match(gatewayPolicy, /headStatus === 405 \|\| headStatus === 501[\s\S]*requestPinnedHttps\([\s\S]*resolved,[\s\S]*"GET"/);
assert.match(gatewayPolicy, /status >= 300 && status < 400/);
@@ -95,7 +95,7 @@ test("model provider URLs allow arbitrary public HTTPS origins but retain SSRF b
);
const gatewayPolicy = readFileSync(new URL("../src/lib/epay/gateway-policy.ts", import.meta.url), "utf8");
assert.match(gatewayPolicy, /lookup: \(_hostname, _options, callback\) => callback\(null, pinned\.address, pinned\.family\)/);
assert.match(gatewayPolicy, /lookup: pinnedAddressLookup\(pinned\)/);
assert.match(gatewayPolicy, /status >= 300 && status < 400/);
assert.match(gatewayPolicy, /不允许重定向/);
});
@@ -1,10 +1,13 @@
import assert from "node:assert/strict";
import test from "node:test";
import { readFileSync } from "node:fs";
import { connect, createServer, type AddressInfo } from "node:net";
import { pinnedAddressLookup } from "../src/lib/epay/gateway-policy.ts";
import { decryptModelProviderApiKey, encryptModelProviderApiKey, modelProviderModelsUrl, modelProviderRequestHeaders } from "../src/lib/model-provider-policy.ts";
const key=Buffer.alloc(32,7).toString("base64");
test("model provider credentials use the dedicated AES-GCM master key",()=>{const encrypted=encryptModelProviderApiKey("provider-secret",{MODEL_PROVIDER_CONFIG_ENCRYPTION_KEY:key});assert.notEqual(encrypted,"provider-secret");assert.equal(decryptModelProviderApiKey({encryptedApiKey:encrypted},{MODEL_PROVIDER_CONFIG_ENCRYPTION_KEY:key}),"provider-secret");assert.throws(()=>decryptModelProviderApiKey({encryptedApiKey:encrypted},{MODEL_PROVIDER_CONFIG_ENCRYPTION_KEY:Buffer.alloc(32,8).toString("base64")}));});
test("Anthropic discovery uses its official endpoint and headers",()=>{assert.equal(modelProviderModelsUrl({providerType:"anthropic",baseUrl:null}),"https://api.anthropic.com/v1/models");assert.deepEqual(modelProviderRequestHeaders("anthropic","secret"),{"x-api-key":"secret","anthropic-version":"2023-06-01"});assert.deepEqual(modelProviderRequestHeaders("openai","secret"),{authorization:"Bearer secret"});});
test("runtime and admin APIs do not parse legacy provider credential env vars",()=>{for(const file of ["../src/lib/model-catalog.ts","../src/mastra/model.ts","../src/app/api/admin/models/route.ts"]){const source=readFileSync(new URL(file,import.meta.url),"utf8");assert.doesNotMatch(source,/OPENAI_API_KEY|DEEPSEEK_API_KEY|LLM_API_KEY|secret_ref/);}});
test("discovery stays on the DNS-pinned bounded HTTPS helper",()=>{const route=readFileSync(new URL("../src/app/api/admin/models/discover/route.ts",import.meta.url),"utf8");const gateway=readFileSync(new URL("../src/lib/epay/gateway-policy.ts",import.meta.url),"utf8");assert.match(route,/requestAllowedModelProvider/);assert.doesNotMatch(route,/\bfetch\s*\(/);assert.match(gateway,/lookup: \(_hostname, _options, callback\)/);assert.match(gateway,/不允许重定向/);assert.match(route,/maxResponseBytes:256\*1024/);});
test("discovery stays on the DNS-pinned bounded HTTPS helper",()=>{const route=readFileSync(new URL("../src/app/api/admin/models/discover/route.ts",import.meta.url),"utf8");const gateway=readFileSync(new URL("../src/lib/epay/gateway-policy.ts",import.meta.url),"utf8");assert.match(route,/requestAllowedModelProvider/);assert.doesNotMatch(route,/\bfetch\s*\(/);assert.match(gateway,/lookup: pinnedAddressLookup\(pinned\)/);assert.match(gateway,/不允许重定向/);assert.match(route,/maxResponseBytes:256\*1024/);});
test("DNS pinning supports Node all-address and single-address lookups",async(t)=>{const server=createServer((socket)=>socket.end());await new Promise<void>((resolve,reject)=>{server.once("error",reject);server.listen(0,"127.0.0.1",resolve);});t.after(()=>new Promise<void>((resolve,reject)=>server.close((error)=>error?reject(error):resolve())));const {port}=server.address() as AddressInfo;for(const autoSelectFamily of [true,false])await new Promise<void>((resolve,reject)=>{const socket=connect({host:"provider.example",port,autoSelectFamily,lookup:pinnedAddressLookup({address:"127.0.0.1",family:4})});socket.once("connect",()=>{socket.end();resolve();});socket.once("error",reject);});});