-
Notifications
You must be signed in to change notification settings - Fork 964
fix(host-service): add CORS lockdown + PSK auth for local host-service #2927
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c2e797e
b6c744d
06737f6
4741910
8eef468
a083c9f
913c50a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| const secrets = new Map<string, string>(); | ||
|
|
||
| export function setHostServiceSecret(hostUrl: string, secret: string): void { | ||
| secrets.set(hostUrl, secret); | ||
| } | ||
|
|
||
| export function removeHostServiceSecret(hostUrl: string): void { | ||
| secrets.delete(hostUrl); | ||
| } | ||
|
|
||
| export function getHostServiceHeaders(hostUrl: string): Record<string, string> { | ||
| const secret = secrets.get(hostUrl); | ||
| return secret ? { Authorization: `Bearer ${secret}` } : {}; | ||
| } | ||
|
|
||
| export function getHostServiceWsToken(hostUrl: string): string | null { | ||
| return secrets.get(hostUrl) ?? null; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| export { | ||
| useWorkspaceHostUrl, | ||
| useWorkspaceWsUrl, | ||
| WorkspaceClientProvider as WorkspaceTrpcProvider, | ||
| } from "@superset/workspace-client"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { | |
| import { env } from "renderer/env.renderer"; | ||
| import { authClient } from "renderer/lib/auth-client"; | ||
| import { electronTrpc } from "renderer/lib/electron-trpc"; | ||
| import { setHostServiceSecret } from "renderer/lib/host-service-auth"; | ||
| import { | ||
| getHostServiceClient, | ||
| type HostServiceClient, | ||
|
|
@@ -73,10 +74,14 @@ export function HostServiceProvider({ children }: { children: ReactNode }) { | |
| const services = useMemo(() => { | ||
| const map = new Map<string, OrgService>(); | ||
|
|
||
| const addOrg = (orgId: string, port: number) => { | ||
| const addOrg = (orgId: string, port: number, secret: string | null) => { | ||
| const url = `http://127.0.0.1:${port}`; | ||
| if (secret) { | ||
| setHostServiceSecret(url, secret); | ||
| } | ||
|
Comment on lines
+77
to
+81
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify render-phase mutation inside useMemo in HostServiceProvider.
rg -n -C3 'useMemo\(|setHostServiceSecret\(' apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsxRepository: superset-sh/superset Length of output: 809 🏁 Script executed: # Locate and examine setHostServiceSecret implementation
fd -e ts -e tsx "host-service-auth" apps/desktop/src/renderer/Repository: superset-sh/superset Length of output: 114 🏁 Script executed: # Also check the full HostServiceProvider file to understand context
wc -l apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsxRepository: superset-sh/superset Length of output: 169 🏁 Script executed: # Read the setHostServiceSecret implementation
cat -n apps/desktop/src/renderer/lib/host-service-auth.tsRepository: superset-sh/superset Length of output: 752 🏁 Script executed: # Read the full HostServiceProvider to understand context and dependencies
cat -n apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsxRepository: superset-sh/superset Length of output: 4364 Move secret synchronization out of At line 80, ♻️ Suggested refactor-import { setHostServiceSecret } from "renderer/lib/host-service-auth";
+import {
+ removeHostServiceSecret,
+ setHostServiceSecret,
+} from "renderer/lib/host-service-auth";
export interface OrgService {
port: number;
url: string;
client: HostServiceClient;
+ secret: string | null;
}
const services = useMemo(() => {
const map = new Map<string, OrgService>();
const addOrg = (orgId: string, port: number, secret: string | null) => {
const url = `http://127.0.0.1:${port}`;
- if (secret) {
- setHostServiceSecret(url, secret);
- }
map.set(orgId, {
port,
url,
client: getHostServiceClient(port),
+ secret,
});
};
...
}, [orgIds, utils, activeOrganizationId, activePortData]);
+useEffect(() => {
+ const currentUrls = new Set<string>();
+ for (const service of services.values()) {
+ currentUrls.add(service.url);
+ if (service.secret) {
+ setHostServiceSecret(service.url, service.secret);
+ } else {
+ removeHostServiceSecret(service.url);
+ }
+ }
+ return () => {
+ for (const url of currentUrls) {
+ removeHostServiceSecret(url);
+ }
+ };
+}, [services]);🤖 Prompt for AI Agents |
||
| map.set(orgId, { | ||
| port, | ||
| url: `http://127.0.0.1:${port}`, | ||
| url, | ||
| client: getHostServiceClient(port), | ||
| }); | ||
|
Comment on lines
+77
to
86
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check if there's any subscription or event mechanism for host-service restarts
rg -n "subscription|onRestart|secretChanged" --type=ts apps/desktop/src/lib/trpc/routers/host-service-manager/
rg -n "subscription|onRestart|secretChanged" --type=ts apps/desktop/src/main/lib/host-service-manager.tsRepository: superset-sh/superset Length of output: 46 🏁 Script executed: # First, check the full HostServiceProvider implementation
cat -n apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/HostServiceProvider.tsxRepository: superset-sh/superset Length of output: 4364 🏁 Script executed: # Check the host-service-manager implementation to see how secrets are generated and managed
cat -n apps/desktop/src/main/lib/host-service-manager.tsRepository: superset-sh/superset Length of output: 11494 🏁 Script executed: # Check if removeHostServiceSecret is ever called/used
rg -n "removeHostServiceSecret" --type=ts apps/desktop/Repository: superset-sh/superset Length of output: 182 🏁 Script executed: # Check how getHostServiceClient is implemented and if it has 401 retry logic
fd -e ts -e tsx ".*" apps/desktop/src/lib/ | xargs rg -l "getHostServiceClient"Repository: superset-sh/superset Length of output: 48 🏁 Script executed: # Find the getLocalPort router implementation
fd -type f "host-service-manager" apps/desktop/src/lib/trpc/routers/ | head -5Repository: superset-sh/superset Length of output: 234 🏁 Script executed: # Search for getLocalPort query definition
rg -n "getLocalPort" --type=ts apps/desktop/src/lib/trpc/routers/Repository: superset-sh/superset Length of output: 161 🏁 Script executed: # Check removeHostServiceSecret usage throughout the codebase
rg -n "removeHostServiceSecret" --type=ts apps/desktop/Repository: superset-sh/superset Length of output: 182 🏁 Script executed: # Read the full host-service-manager router implementation
cat -n apps/desktop/src/lib/trpc/routers/host-service-manager/index.tsRepository: superset-sh/superset Length of output: 1313 🏁 Script executed: # Double-check removeHostServiceSecret usage with more context
rg -B2 -A2 "removeHostServiceSecret" --type ts apps/desktop/Repository: superset-sh/superset Length of output: 414 🏁 Script executed: # Verify if there's any 401 retry logic in the host-service client
cat -n apps/desktop/src/renderer/lib/host-service-client.tsRepository: superset-sh/superset Length of output: 1228 🏁 Script executed: # Check if there's any mechanism to invalidate or refetch on app focus/resume
rg -n "refetch|invalidate|onFocus" --type ts apps/desktop/src/renderer/routes/_authenticated/providers/HostServiceProvider/Repository: superset-sh/superset Length of output: 46 Stale secret after host-service restart causes authentication failures. When the host-service crashes and respawns, The Consider either:
🤖 Prompt for AI Agents |
||
| }; | ||
|
|
@@ -86,7 +91,7 @@ export function HostServiceProvider({ children }: { children: ReactNode }) { | |
| organizationId: orgId, | ||
| }); | ||
| if (cached?.port) { | ||
| addOrg(orgId, cached.port); | ||
| addOrg(orgId, cached.port, cached.secret ?? null); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -96,7 +101,11 @@ export function HostServiceProvider({ children }: { children: ReactNode }) { | |
| activePortData?.port && | ||
| !map.has(activeOrganizationId) | ||
| ) { | ||
| addOrg(activeOrganizationId, activePortData.port); | ||
| addOrg( | ||
| activeOrganizationId, | ||
| activePortData.port, | ||
| activePortData.secret ?? null, | ||
| ); | ||
| } | ||
|
|
||
| return map; | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: Fail closed when HOST_SERVICE_SECRET is missing. The current fallback to
undefineddisables host auth entirely, leaving the WebSocket routes unauthenticated if the env var isn’t set.Prompt for AI agents