mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
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 https://github.com/coder/security/issues/77
This commit is contained in:
@ -53,6 +53,22 @@ describe("getAppHref", () => {
|
||||
expect(href).toBe(externalApp.url);
|
||||
});
|
||||
|
||||
it("doesn't return the URL with the session token replaced when using unauthorized protocol", () => {
|
||||
const externalApp = {
|
||||
...MockWorkspaceApp,
|
||||
external: true,
|
||||
url: `ftp://example.com?token=${SESSION_TOKEN_PLACEHOLDER}`,
|
||||
};
|
||||
const href = getAppHref(externalApp, {
|
||||
host: "*.apps-host.tld",
|
||||
agent: MockWorkspaceAgent,
|
||||
workspace: MockWorkspace,
|
||||
path: "/path-base",
|
||||
token: "user-session-token",
|
||||
});
|
||||
expect(href).toBe(externalApp.url);
|
||||
});
|
||||
|
||||
it("returns a path when app doesn't use a subdomain", () => {
|
||||
const app = {
|
||||
...MockWorkspaceApp,
|
||||
|
@ -10,6 +10,20 @@ import type {
|
||||
// be used internally, and is highly subject to break.
|
||||
export const SESSION_TOKEN_PLACEHOLDER = "$SESSION_TOKEN";
|
||||
|
||||
// This is a list of external app protocols that we
|
||||
// allow to be opened in a new window. This is
|
||||
// used to prevent phishing attacks where a user
|
||||
// is tricked into clicking a link that opens
|
||||
// a malicious app using the Coder session token.
|
||||
const ALLOWED_EXTERNAL_APP_PROTOCOLS = [
|
||||
"vscode:",
|
||||
"vscode-insiders:",
|
||||
"windsurf:",
|
||||
"cursor:",
|
||||
"jetbrains-gateway:",
|
||||
"jetbrains:",
|
||||
];
|
||||
|
||||
type GetVSCodeHrefParams = {
|
||||
owner: string;
|
||||
workspace: string;
|
||||
@ -78,7 +92,11 @@ export const getAppHref = (
|
||||
{ path, token, workspace, agent, host }: GetAppHrefParams,
|
||||
): string => {
|
||||
if (isExternalApp(app)) {
|
||||
return needsSessionToken(app)
|
||||
const appProtocol = new URL(app.url).protocol;
|
||||
const isAllowedProtocol =
|
||||
ALLOWED_EXTERNAL_APP_PROTOCOLS.includes(appProtocol);
|
||||
|
||||
return needsSessionToken(app) && isAllowedProtocol
|
||||
? app.url.replaceAll(SESSION_TOKEN_PLACEHOLDER, token ?? "")
|
||||
: app.url;
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ export const ExternalApp: Story = {
|
||||
workspace: MockWorkspace,
|
||||
app: {
|
||||
...MockWorkspaceApp,
|
||||
url: "vscode://open",
|
||||
external: true,
|
||||
},
|
||||
agent: MockWorkspaceAgent,
|
||||
|
Reference in New Issue
Block a user