Skip to content

Commit 7b57e7b

Browse files
rounak610claude
andcommitted
LOC-6727: validate source URL host before binary download
Mirror the host allowlist added in the Java and Python bindings (browserstack-local-java#99, browserstack-local-python#62). Refuse download endpoints that aren't HTTPS or whose host isn't browserstack.com / *.browserstack.com. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5db2cbd commit 7b57e7b

1 file changed

Lines changed: 36 additions & 1 deletion

File tree

BrowserStackLocal/BrowserStackLocal/BrowserStackTunnel.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public enum LocalState { Idle, Connecting, Connected, Error, Disconnected };
2121

2222
public class BrowserStackTunnel : IDisposable
2323
{
24+
private static readonly string[] AllowedDownloadHosts = new string[] { "browserstack.com" };
25+
private static readonly string[] AllowedDownloadHostSuffixes = new string[] { ".browserstack.com" };
26+
2427
static readonly string uname = Util.GetUName();
2528
static readonly string binaryName = GetBinaryName();
2629

@@ -229,11 +232,43 @@ private string fetchSourceUrl(string accessKey)
229232
throw new Exception((string)jsonResponse["error"]);
230233
}
231234

232-
sourceUrl = jsonResponse["data"]?["endpoint"]?.ToString();
235+
sourceUrl = ValidateSourceUrl(jsonResponse["data"]?["endpoint"]?.ToString());
233236
return sourceUrl;
234237
}
235238
}
236239

240+
private static string ValidateSourceUrl(string url)
241+
{
242+
if (string.IsNullOrEmpty(url))
243+
{
244+
throw new Exception("Refusing binary download: empty source URL");
245+
}
246+
Uri parsed;
247+
if (!Uri.TryCreate(url, UriKind.Absolute, out parsed))
248+
{
249+
throw new Exception("Refusing binary download: malformed source URL");
250+
}
251+
if (!string.Equals(parsed.Scheme, "https", StringComparison.OrdinalIgnoreCase))
252+
{
253+
throw new Exception("Refusing binary download from non-HTTPS source URL");
254+
}
255+
string host = parsed.Host;
256+
if (string.IsNullOrEmpty(host))
257+
{
258+
throw new Exception("Refusing binary download: source URL has no host");
259+
}
260+
host = host.ToLowerInvariant();
261+
foreach (var allowed in AllowedDownloadHosts)
262+
{
263+
if (host.Equals(allowed)) return url;
264+
}
265+
foreach (var suffix in AllowedDownloadHostSuffixes)
266+
{
267+
if (host.EndsWith(suffix)) return url;
268+
}
269+
throw new Exception("Refusing binary download: host '" + host + "' is not in the allowed host list");
270+
}
271+
237272
public void downloadBinary()
238273
{
239274
string binaryDirectory = Path.Combine(this.binaryAbsolute, "..");

0 commit comments

Comments
 (0)