fix(gateway): land access/auth/config migration cluster

Land #28960 by @Glucksberg (Tailscale origin auto-allowlist).
Land #29394 by @synchronic1 (allowedOrigins upgrade migration).
Land #29198 by @Mariana-Codebase (plugin HTTP auth guard + route precedence).
Land #30910 by @liuxiaopai-ai (tailscale bind/config.patch guard).

Co-authored-by: Glucksberg <markuscontasul@gmail.com>
Co-authored-by: synchronic1 <synchronic1@users.noreply.github.com>
Co-authored-by: Mariana Sinisterra <mariana.data@outlook.com>
Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-03-02 00:05:48 +00:00
parent 8e6b3ade3e
commit 53d10f8688
18 changed files with 876 additions and 37 deletions

View File

@@ -104,3 +104,113 @@ describe("legacy migrate mention routing", () => {
).toBeUndefined();
});
});
describe("legacy migrate controlUi.allowedOrigins seed (issue #29385)", () => {
it("seeds allowedOrigins for bind=lan with no existing controlUi config", () => {
const res = migrateLegacyConfig({
gateway: {
bind: "lan",
auth: { mode: "token", token: "tok" },
},
});
expect(res.config?.gateway?.controlUi?.allowedOrigins).toEqual([
"http://localhost:18789",
"http://127.0.0.1:18789",
]);
expect(res.changes.some((c) => c.includes("gateway.controlUi.allowedOrigins"))).toBe(true);
expect(res.changes.some((c) => c.includes("bind=lan"))).toBe(true);
});
it("seeds allowedOrigins using configured port", () => {
const res = migrateLegacyConfig({
gateway: {
bind: "lan",
port: 9000,
auth: { mode: "token", token: "tok" },
},
});
expect(res.config?.gateway?.controlUi?.allowedOrigins).toEqual([
"http://localhost:9000",
"http://127.0.0.1:9000",
]);
});
it("seeds allowedOrigins including custom bind host for bind=custom", () => {
const res = migrateLegacyConfig({
gateway: {
bind: "custom",
customBindHost: "192.168.1.100",
auth: { mode: "token", token: "tok" },
},
});
expect(res.config?.gateway?.controlUi?.allowedOrigins).toContain("http://192.168.1.100:18789");
expect(res.config?.gateway?.controlUi?.allowedOrigins).toContain("http://localhost:18789");
});
it("does not overwrite existing allowedOrigins — returns null (no migration needed)", () => {
// When allowedOrigins already exists, the migration is a no-op.
// applyLegacyMigrations returns next=null when changes.length===0, so config is null.
const res = migrateLegacyConfig({
gateway: {
bind: "lan",
auth: { mode: "token", token: "tok" },
controlUi: { allowedOrigins: ["https://control.example.com"] },
},
});
expect(res.config).toBeNull();
expect(res.changes).toHaveLength(0);
});
it("does not migrate when dangerouslyAllowHostHeaderOriginFallback is set — returns null", () => {
const res = migrateLegacyConfig({
gateway: {
bind: "lan",
auth: { mode: "token", token: "tok" },
controlUi: { dangerouslyAllowHostHeaderOriginFallback: true },
},
});
expect(res.config).toBeNull();
expect(res.changes).toHaveLength(0);
});
it("seeds allowedOrigins when existing entries are blank strings", () => {
const res = migrateLegacyConfig({
gateway: {
bind: "lan",
auth: { mode: "token", token: "tok" },
controlUi: { allowedOrigins: ["", " "] },
},
});
expect(res.config?.gateway?.controlUi?.allowedOrigins).toEqual([
"http://localhost:18789",
"http://127.0.0.1:18789",
]);
expect(res.changes.some((c) => c.includes("gateway.controlUi.allowedOrigins"))).toBe(true);
});
it("does not migrate loopback bind — returns null", () => {
const res = migrateLegacyConfig({
gateway: {
bind: "loopback",
auth: { mode: "token", token: "tok" },
},
});
expect(res.config).toBeNull();
expect(res.changes).toHaveLength(0);
});
it("preserves existing controlUi fields when seeding allowedOrigins", () => {
const res = migrateLegacyConfig({
gateway: {
bind: "lan",
auth: { mode: "token", token: "tok" },
controlUi: { basePath: "/app" },
},
});
expect(res.config?.gateway?.controlUi?.basePath).toBe("/app");
expect(res.config?.gateway?.controlUi?.allowedOrigins).toEqual([
"http://localhost:18789",
"http://127.0.0.1:18789",
]);
});
});