Skip to content

Commit 75e2116

Browse files
authored
Auth: Add input validation for instance URL (#3156)
This commit adds validation for sourcegraph tokens in the AuthMenus and AuthProvider files. The showInstanceURLInputBox function in AuthMenus now checks if the user is entering a token as a URL and displays an error message if so. Similarly, the formatURL function in AuthProvider now throws an error if the URI is a sourcegraph token, and return `null`. Additionally, the LocalStorageProvider file now ignores and clears the last used endpoint if the provided endpoint is a sourcegraph token. ![image](https://github.com/sourcegraph/cody/assets/68532117/c77005be-edd9-4018-9c42-4655baff2782) Update placeholder value for URL to always starts with `https://` to make it clear that the field is for URL input: ![image](https://github.com/sourcegraph/cody/assets/68532117/cea4f78a-d39e-4819-bc97-a59f6af4c369) ## Test plan <!-- Required. See https://sourcegraph.com/docs/dev/background-information/testing_principles. --> https://github.com/sourcegraph/cody/assets/68532117/9d44427a-3b8b-4c35-812d-aa5b22120d6d
1 parent 4756dc7 commit 75e2116

File tree

4 files changed

+72
-14
lines changed

4 files changed

+72
-14
lines changed

vscode/src/chat/protocol.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,18 @@ export function isLoggedIn(authStatus: AuthStatus): boolean {
315315
}
316316

317317
export type AuthMethod = 'dotcom' | 'github' | 'gitlab' | 'google'
318+
319+
// Provide backward compatibility for the old token regex
320+
// Details: https://docs.sourcegraph.com/dev/security/secret_formats
321+
const sourcegraphTokenRegex =
322+
/(sgp_(?:[a-fA-F0-9]{16}|local)|sgp_)?[a-fA-F0-9]{40}|(sgd|slk|sgs)_[a-fA-F0-9]{64}/
323+
324+
/**
325+
* Checks if the given text matches the regex for a Sourcegraph access token.
326+
*
327+
* @param text - The text to check against the regex.
328+
* @returns Whether the text matches the Sourcegraph token regex.
329+
*/
330+
export function isSourcegraphToken(text: string): boolean {
331+
return sourcegraphTokenRegex.test(text)
332+
}

vscode/src/services/AuthMenus.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from 'vscode'
22

33
import { isDotCom, LOCAL_APP_URL } from '@sourcegraph/cody-shared'
4+
import { isSourcegraphToken } from '../chat/protocol'
45

56
interface LoginMenuItem {
67
id: string
@@ -56,10 +57,28 @@ export const AuthMenu = async (
5657
export async function showInstanceURLInputBox(title: string): Promise<string | undefined> {
5758
const result = await vscode.window.showInputBox({
5859
title,
59-
prompt: 'Enter the URL of the Sourcegraph instance',
60+
prompt: 'Enter the URL of the Sourcegraph instance. For example, https://sourcegraph.example.com.',
6061
placeHolder: 'https://sourcegraph.example.com',
62+
value: 'https://',
6163
password: false,
6264
ignoreFocusOut: true,
65+
// valide input to ensure the user is not entering a token as URL
66+
validateInput: (value: string) => {
67+
// ignore empty value
68+
if (!value) {
69+
return null
70+
}
71+
if (isSourcegraphToken(value)) {
72+
return 'Please enter a valid URL'
73+
}
74+
if (value.length > 4 && !value.startsWith('http')) {
75+
return 'URL must start with http or https'
76+
}
77+
if (!/([.]|^https?:\/\/)/.test(value)) {
78+
return 'Please enter a valid URL'
79+
}
80+
return null
81+
},
6382
})
6483

6584
if (typeof result === 'string') {

vscode/src/services/AuthProvider.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
networkErrorAuthStatus,
1818
unauthenticatedStatus,
1919
type AuthStatus,
20+
isSourcegraphToken,
2021
} from '../chat/protocol'
2122
import { newAuthStatus } from '../chat/utils'
2223
import { getFullConfig } from '../configuration'
@@ -447,21 +448,28 @@ export function isNetworkError(error: Error): boolean {
447448
}
448449

449450
function formatURL(uri: string): string | null {
450-
if (!uri) {
451-
return null
452-
}
453-
// Check if the URI is in the correct URL format
454-
// Add missing https:// if needed
455-
if (!uri.startsWith('http')) {
456-
uri = `https://${uri}`
457-
}
458451
try {
452+
if (!uri) {
453+
return null
454+
}
455+
456+
// Check if the URI is a sourcegraph token
457+
if (isSourcegraphToken(uri)) {
458+
throw new Error('Access Token is not a valid URL')
459+
}
460+
461+
// Check if the URI is in the correct URL format
462+
// Add missing https:// if needed
463+
if (!uri.startsWith('http')) {
464+
uri = `https://${uri}`
465+
}
466+
459467
const endpointUri = new URL(uri)
460468
return endpointUri.href
461-
} catch {
462-
console.error('Invalid URL')
469+
} catch (error) {
470+
console.error('Invalid URL: ', error)
471+
return null
463472
}
464-
return null
465473
}
466474

467475
async function showAuthResultMessage(

vscode/src/services/LocalStorageProvider.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Memento } from 'vscode'
33

44
import type { ChatHistory, ChatInputHistory, UserLocalHistory } from '@sourcegraph/cody-shared'
55

6-
import type { AuthStatus } from '../chat/protocol'
6+
import { isSourcegraphToken, type AuthStatus } from '../chat/protocol'
77

88
type ChatHistoryKey = `${string}-${string}`
99
type AccountKeyedChatHistory = {
@@ -55,14 +55,25 @@ class LocalStorage {
5555
}
5656

5757
public getEndpoint(): string | null {
58-
return this.storage.get<string | null>(this.LAST_USED_ENDPOINT, null)
58+
const endpoint = this.storage.get<string | null>(this.LAST_USED_ENDPOINT, null)
59+
// Clear last used endpoint if it is a Sourcegraph token
60+
if (endpoint && isSourcegraphToken(endpoint)) {
61+
this.deleteEndpoint()
62+
return null
63+
}
64+
return endpoint
5965
}
6066

6167
public async saveEndpoint(endpoint: string): Promise<void> {
6268
if (!endpoint) {
6369
return
6470
}
6571
try {
72+
// Do not save sourcegraph tokens as the last used endpoint
73+
if (isSourcegraphToken(endpoint)) {
74+
return
75+
}
76+
6677
const uri = new URL(endpoint).href
6778
await this.storage.update(this.LAST_USED_ENDPOINT, uri)
6879
await this.addEndpointHistory(uri)
@@ -84,6 +95,11 @@ class LocalStorage {
8495
}
8596

8697
private async addEndpointHistory(endpoint: string): Promise<void> {
98+
// Do not save sourcegraph tokens as endpoint
99+
if (isSourcegraphToken(endpoint)) {
100+
return
101+
}
102+
87103
const history = this.storage.get<string[] | null>(this.CODY_ENDPOINT_HISTORY, null)
88104
const historySet = new Set(history)
89105
historySet.delete(endpoint)

0 commit comments

Comments
 (0)