Skip to content

Commit 1e8502e

Browse files
committed
security: restrict webview executeCommand bridge to snyk.* namespace
Prevents XSS-to-arbitrary-command escalation by rejecting any command not prefixed with "snyk." before it reaches the Language Server.
1 parent af40ad6 commit 1e8502e

2 files changed

Lines changed: 33 additions & 0 deletions

File tree

Snyk.VisualStudio.Extension.2022/UI/Html/ExecuteCommandBridge.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ public static class ExecuteCommandBridge
3737
public static bool IsValidCallbackId(string callbackId) =>
3838
CallbackIdPattern.IsMatch(callbackId ?? string.Empty);
3939

40+
/// <summary>
41+
/// Returns true if <paramref name="command"/> is in the <c>snyk.*</c> namespace and may be
42+
/// dispatched from a webview.
43+
/// </summary>
44+
public static bool IsAllowedCommand(string command) =>
45+
!string.IsNullOrEmpty(command) && command.StartsWith("snyk.");
46+
4047
/// <summary>
4148
/// Returns the ES5-compatible JavaScript that defines <c>window.__ideExecuteCommand__</c>.
4249
/// Assumes <c>window.external.__ideExecuteCommand__(command, argsJson, callbackId)</c>
@@ -73,6 +80,12 @@ public static async Task DispatchAsync(
7380
{
7481
try
7582
{
83+
if (!IsAllowedCommand(command))
84+
{
85+
Logger.Warning("Webview attempted to execute disallowed command: {Command}", command);
86+
return;
87+
}
88+
7689
var args = string.IsNullOrEmpty(argsJson)
7790
? Array.Empty<object>()
7891
: JsonConvert.DeserializeObject<object[]>(argsJson);

Snyk.VisualStudio.Extension.Tests/UI/Html/ExecuteCommandBridgeTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,25 @@ public void IsValidCallbackId_RejectsInvalidFormats(string callbackId)
6666
{
6767
Assert.False(ExecuteCommandBridge.IsValidCallbackId(callbackId));
6868
}
69+
70+
[Theory]
71+
[InlineData("snyk.login", true)]
72+
[InlineData("snyk.logout", true)]
73+
[InlineData("snyk.navigateToRange", true)]
74+
[InlineData("snyk.anyCommand", true)]
75+
public void IsAllowedCommand_AllowsSnykPrefixedCommands(string command, bool expected)
76+
{
77+
Assert.Equal(expected, ExecuteCommandBridge.IsAllowedCommand(command));
78+
}
79+
80+
[Theory]
81+
[InlineData("workbench.action.terminal.new")]
82+
[InlineData("vscode.open")]
83+
[InlineData("")]
84+
[InlineData(null)]
85+
public void IsAllowedCommand_RejectsNonSnykCommands(string command)
86+
{
87+
Assert.False(ExecuteCommandBridge.IsAllowedCommand(command));
88+
}
6989
}
7090
}

0 commit comments

Comments
 (0)