security: harden URL parsing against ReDoS and injection attacks

- Add strict prefix validation: require vscode-resource://vscode-webview/ prefix
- Add URI length limits (max 2048 chars) to prevent DoS
- Replace potentially vulnerable regex with bounded, anchored patterns
- Use ^ and $ anchors to prevent partial matches
- Limit character classes to prevent backtracking (e.g., [a-zA-Z0-9._-]{1,50})
- Add proper error handling for decode failures
- Addresses CodeQL warnings for polynomial regex and incomplete URL sanitization
This commit is contained in:
daniel-lxs 2025-10-27 14:26:28 -05:00
parent 7029f1d6f8
commit a1c402e77b
No known key found for this signature in database
GPG key ID: 21C74479048B3AA6

View file

@ -54,21 +54,34 @@ function webviewUriToFilePath(webviewUri: string): string {
}
// Handle VS Code webview URIs that contain encoded paths
if (webviewUri.includes("vscode-userdata") || webviewUri.includes("vscode-cdn.net")) {
// Try to decode the URI and extract the file path
const decoded = decodeURIComponent(webviewUri)
// Use strict prefix matching to prevent arbitrary host injection
if (
webviewUri.startsWith("vscode-resource://vscode-webview/") &&
(webviewUri.includes("vscode-userdata") || webviewUri.includes("vscode-cdn.net"))
) {
try {
// Decode safely with length limits
if (webviewUri.length > 2048) {
throw new Error("URI too long")
}
// Use safer, non-polynomial regex patterns
// Look for Unix-style paths first
let pathMatch = decoded.match(/\/Users\/[^?#]*\.(?:png|jpg|jpeg|gif|webp)/i)
if (pathMatch) {
return pathMatch[0]
}
const decoded = decodeURIComponent(webviewUri)
// Look for Windows-style paths with bounded length to prevent polynomial behavior
pathMatch = decoded.match(/C:\\[^?#]{0,500}\.(?:png|jpg|jpeg|gif|webp)/i)
if (pathMatch) {
return pathMatch[0]
// Use specific, bounded patterns to prevent ReDoS
// Match exact patterns without backtracking
const unixMatch = decoded.match(
/^[^?#]*\/Users\/[a-zA-Z0-9._-]{1,50}\/[^?#]{1,300}\.(png|jpg|jpeg|gif|webp)$/i,
)
if (unixMatch) {
return unixMatch[0]
}
const windowsMatch = decoded.match(/^[^?#]*C:\\[a-zA-Z0-9._\\-]{1,300}\.(png|jpg|jpeg|gif|webp)$/i)
if (windowsMatch) {
return windowsMatch[0]
}
} catch (error) {
console.error("Failed to decode webview URI:", error)
}
}