fix(config): normalize gateway bind host aliases during migration (#30855)

* fix(config): normalize gateway bind host aliases during migration [AI-assisted]

* config(legacy): detect gateway.bind host aliases as legacy

* config(legacy): sanitize bind alias migration log output

* test(config): cover bind alias legacy detection and log escaping

* config(legacy): add source-literal gate to legacy rules

* config(legacy): make issue detection source-aware

* config(legacy): require source-literal gateway.bind alias detection

* config(io): pass parsed source to legacy issue detection

* test(config): cover resolved-only gateway.bind alias legacy detection

* changelog: format after #30855 rebase conflict resolution

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Mark L
2026-03-02 11:53:00 +08:00
committed by GitHub
parent c9f0d6ac8e
commit 5b06c8c6e3
8 changed files with 202 additions and 10 deletions

View File

@@ -377,6 +377,50 @@ describe("legacy config detection", () => {
expect(validated.config.gateway?.bind).toBe("tailnet");
}
});
it("normalizes gateway.bind host aliases to supported bind modes", async () => {
const cases = [
{ input: "0.0.0.0", expected: "lan" },
{ input: "::", expected: "lan" },
{ input: "127.0.0.1", expected: "loopback" },
{ input: "localhost", expected: "loopback" },
{ input: "::1", expected: "loopback" },
] as const;
for (const testCase of cases) {
const res = migrateLegacyConfig({
gateway: { bind: testCase.input },
});
expect(res.changes).toContain(
`Normalized gateway.bind "${testCase.input}" → "${testCase.expected}".`,
);
expect(res.config?.gateway?.bind).toBe(testCase.expected);
const validated = validateConfigObject(res.config);
expect(validated.ok).toBe(true);
if (validated.ok) {
expect(validated.config.gateway?.bind).toBe(testCase.expected);
}
}
});
it("flags gateway.bind host aliases as legacy to trigger auto-migration paths", async () => {
const cases = ["0.0.0.0", "::", "127.0.0.1", "localhost", "::1"] as const;
for (const bind of cases) {
const validated = validateConfigObject({ gateway: { bind } });
expect(validated.ok, bind).toBe(false);
if (!validated.ok) {
expect(
validated.issues.some((issue) => issue.path === "gateway.bind"),
bind,
).toBe(true);
}
}
});
it("escapes control characters in gateway.bind migration change text", async () => {
const res = migrateLegacyConfig({
gateway: { bind: "\r\n0.0.0.0\r\n" },
});
expect(res.changes).toContain('Normalized gateway.bind "\\r\\n0.0.0.0\\r\\n" → "lan".');
});
it('enforces dmPolicy="open" allowFrom wildcard for supported providers', async () => {
const cases = [
{