fix(onboard): seed Control UI origins for non-loopback binds (land #26157, thanks @stakeswky)
Co-authored-by: 不做了睡大觉 <stakeswky@users.noreply.github.com>
This commit is contained in:
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Security/Plugin channel HTTP auth: normalize protected `/api/channels` path checks against canonicalized request paths (case + percent-decoding + slash normalization), and fail closed on malformed `%`-encoded channel prefixes so alternate-path variants cannot bypass gateway auth.
|
||||
- Security/Exec approvals forwarding: prefer turn-source channel/account/thread metadata when resolving approval delivery targets so stale session routes do not misroute approval prompts.
|
||||
- Onboarding/Gateway: seed default Control UI `allowedOrigins` for non-loopback binds during onboarding (`localhost`/`127.0.0.1` plus custom bind host) so fresh non-loopback setups do not fail startup due to missing origin policy. (#26157) thanks @stakeswky.
|
||||
- Auto-reply/Streaming: suppress only exact `NO_REPLY` final replies while still filtering streaming partial sentinel fragments (`NO_`, `NO_RE`, `HEARTBEAT_...`) so substantive replies ending with `NO_REPLY` are delivered and partial silent tokens do not leak during streaming. (#19576) Thanks @aldoeliacim.
|
||||
- Doctor/State integrity: ignore metadata-only slash routing sessions when checking recent missing transcripts so `openclaw doctor` no longer reports false-positive transcript-missing warnings for `*:slash:*` keys. (#27375) thanks @gumadeiras.
|
||||
- Channels/Multi-account config: when adding a non-default channel account to a single-account top-level channel setup, move existing account-scoped top-level single-account values into `channels.<channel>.accounts.default` before writing the new account so the original account keeps working without duplicated account values at channel root; `openclaw doctor --fix` now repairs previously mixed channel account shapes the same way. (#27334) thanks @gumadeiras.
|
||||
|
||||
@@ -111,4 +111,29 @@ describe("configureGatewayForOnboarding", () => {
|
||||
expect(authConfig?.password).toBe("");
|
||||
expect(authConfig?.password).not.toBe("undefined");
|
||||
});
|
||||
|
||||
it("seeds control UI allowed origins for non-loopback binds", async () => {
|
||||
mocks.randomToken.mockReturnValue("generated-token");
|
||||
|
||||
const prompter = createPrompter({
|
||||
selectQueue: ["lan", "token", "off"],
|
||||
textQueue: ["18789", undefined],
|
||||
});
|
||||
const runtime = createRuntime();
|
||||
|
||||
const result = await configureGatewayForOnboarding({
|
||||
flow: "advanced",
|
||||
baseConfig: {},
|
||||
nextConfig: {},
|
||||
localPort: 18789,
|
||||
quickstartGateway: createQuickstartGateway("token"),
|
||||
prompter,
|
||||
runtime,
|
||||
});
|
||||
|
||||
expect(result.nextConfig.gateway?.controlUi?.allowedOrigins).toEqual([
|
||||
"http://localhost:18789",
|
||||
"http://127.0.0.1:18789",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,6 +49,21 @@ type ConfigureGatewayResult = {
|
||||
settings: GatewayWizardSettings;
|
||||
};
|
||||
|
||||
function buildDefaultControlUiAllowedOrigins(params: {
|
||||
port: number;
|
||||
bind: GatewayWizardSettings["bind"];
|
||||
customBindHost?: string;
|
||||
}): string[] {
|
||||
const origins = new Set<string>([
|
||||
`http://localhost:${params.port}`,
|
||||
`http://127.0.0.1:${params.port}`,
|
||||
]);
|
||||
if (params.bind === "custom" && params.customBindHost) {
|
||||
origins.add(`http://${params.customBindHost}:${params.port}`);
|
||||
}
|
||||
return [...origins];
|
||||
}
|
||||
|
||||
export async function configureGatewayForOnboarding(
|
||||
opts: ConfigureGatewayOptions,
|
||||
): Promise<ConfigureGatewayResult> {
|
||||
@@ -216,6 +231,28 @@ export async function configureGatewayForOnboarding(
|
||||
},
|
||||
};
|
||||
|
||||
const controlUiEnabled = nextConfig.gateway?.controlUi?.enabled ?? true;
|
||||
const hasExplicitControlUiAllowedOrigins =
|
||||
(nextConfig.gateway?.controlUi?.allowedOrigins ?? []).some(
|
||||
(origin) => origin.trim().length > 0,
|
||||
) || nextConfig.gateway?.controlUi?.dangerouslyAllowHostHeaderOriginFallback === true;
|
||||
if (controlUiEnabled && bind !== "loopback" && !hasExplicitControlUiAllowedOrigins) {
|
||||
nextConfig = {
|
||||
...nextConfig,
|
||||
gateway: {
|
||||
...nextConfig.gateway,
|
||||
controlUi: {
|
||||
...nextConfig.gateway?.controlUi,
|
||||
allowedOrigins: buildDefaultControlUiAllowedOrigins({
|
||||
port,
|
||||
bind,
|
||||
customBindHost,
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// If this is a new gateway setup (no existing gateway settings), start with a
|
||||
// denylist for high-risk node commands. Users can arm these temporarily via
|
||||
// /phone arm ... (phone-control plugin).
|
||||
|
||||
Reference in New Issue
Block a user