mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-29 18:31:33 +00:00
Fix private AI custom endpoints (#1299)
This commit is contained in:
@@ -51,8 +51,7 @@ function normalizeHost(hostname: string): string {
|
||||
/**
|
||||
* True when the URL names a destination the SSRF guard would refuse. A bare
|
||||
* hostname that is not an IP literal (e.g. "ollama.internal") is treated as
|
||||
* private only if it is "localhost" -- anything else resolves through DNS and
|
||||
* is caught at connect time by the guard instead.
|
||||
* private only if it is "localhost" -- anything else needs DNS resolution.
|
||||
*/
|
||||
export function isPrivateDestination(rawUrl: string): boolean {
|
||||
let url: URL;
|
||||
@@ -98,12 +97,18 @@ export function evaluateEgress(
|
||||
|
||||
const host = normalizeHost(url.hostname);
|
||||
const isPrivate = isPrivateDestination(rawUrl);
|
||||
const normalized = allowlist.map((entry) => entry.trim().toLowerCase());
|
||||
|
||||
// An explicitly allowlisted hostname may resolve to a private address. It
|
||||
// must use the private fetch path; sending it through safeOutboundFetch
|
||||
// would reject it after DNS resolution and make hostname allowlist entries
|
||||
// ineffective. Only administrators can write this list.
|
||||
if (normalized.includes(host)) {
|
||||
return { allowed: true, isPrivate: true };
|
||||
}
|
||||
|
||||
if (!isPrivate) return { allowed: true, isPrivate: false };
|
||||
|
||||
const normalized = allowlist.map((entry) => entry.trim().toLowerCase());
|
||||
if (normalized.includes(host)) return { allowed: true, isPrivate: true };
|
||||
|
||||
return {
|
||||
allowed: false,
|
||||
isPrivate: true,
|
||||
|
||||
@@ -61,6 +61,16 @@ describe("evaluateEgress", () => {
|
||||
expect(decision.isPrivate).toBe(true);
|
||||
});
|
||||
|
||||
it.each(["llm.internal", "host.docker.internal"])(
|
||||
"routes allowlisted private DNS name %s through the private path",
|
||||
(host) => {
|
||||
expect(evaluateEgress(`http://${host}:8000/v1`, [host])).toEqual({
|
||||
allowed: true,
|
||||
isPrivate: true,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("matches the allowlist case-insensitively", () => {
|
||||
expect(
|
||||
evaluateEgress("http://LOCALHOST:11434", ["localhost"]).allowed,
|
||||
|
||||
Reference in New Issue
Block a user