Skip to content

Commit 9e44f18

Browse files
refactor: add safe list for external app protocols (#17742)
To prevent malicious apps and vendors to use the Coder session token we are adding safe protocols/schemas we want to support. - vscode: - vscode-insiders: - windsurf: - cursor: - jetbrains-gateway: - jetbrains: Fix coder/security#77
1 parent 5c53277 commit 9e44f18

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

site/src/modules/apps/apps.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,22 @@ describe("getAppHref", () => {
5353
expect(href).toBe(externalApp.url);
5454
});
5555

56+
it("doesn't return the URL with the session token replaced when using unauthorized protocol", () => {
57+
const externalApp = {
58+
...MockWorkspaceApp,
59+
external: true,
60+
url: `ftp://example.com?token=${SESSION_TOKEN_PLACEHOLDER}`,
61+
};
62+
const href = getAppHref(externalApp, {
63+
host: "*.apps-host.tld",
64+
agent: MockWorkspaceAgent,
65+
workspace: MockWorkspace,
66+
path: "/path-base",
67+
token: "user-session-token",
68+
});
69+
expect(href).toBe(externalApp.url);
70+
});
71+
5672
it("returns a path when app doesn't use a subdomain", () => {
5773
const app = {
5874
...MockWorkspaceApp,

site/src/modules/apps/apps.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ import type {
1010
// be used internally, and is highly subject to break.
1111
export const SESSION_TOKEN_PLACEHOLDER = "$SESSION_TOKEN";
1212

13+
// This is a list of external app protocols that we
14+
// allow to be opened in a new window. This is
15+
// used to prevent phishing attacks where a user
16+
// is tricked into clicking a link that opens
17+
// a malicious app using the Coder session token.
18+
const ALLOWED_EXTERNAL_APP_PROTOCOLS = [
19+
"vscode:",
20+
"vscode-insiders:",
21+
"windsurf:",
22+
"cursor:",
23+
"jetbrains-gateway:",
24+
"jetbrains:",
25+
];
26+
1327
type GetVSCodeHrefParams = {
1428
owner: string;
1529
workspace: string;
@@ -78,7 +92,11 @@ export const getAppHref = (
7892
{ path, token, workspace, agent, host }: GetAppHrefParams,
7993
): string => {
8094
if (isExternalApp(app)) {
81-
return needsSessionToken(app)
95+
const appProtocol = new URL(app.url).protocol;
96+
const isAllowedProtocol =
97+
ALLOWED_EXTERNAL_APP_PROTOCOLS.includes(appProtocol);
98+
99+
return needsSessionToken(app) && isAllowedProtocol
82100
? app.url.replaceAll(SESSION_TOKEN_PLACEHOLDER, token ?? "")
83101
: app.url;
84102
}

site/src/modules/resources/AppLink/AppLink.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const ExternalApp: Story = {
8080
workspace: MockWorkspace,
8181
app: {
8282
...MockWorkspaceApp,
83+
url: "vscode://open",
8384
external: true,
8485
},
8586
agent: MockWorkspaceAgent,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy