1515import javax .annotation .Nonnull ;
1616import java .util .ArrayList ;
1717import java .util .Arrays ;
18+ import java .net .URI ;
1819import java .util .HashMap ;
1920import java .util .List ;
2021import java .util .Map ;
@@ -35,7 +36,8 @@ public final class AutomateClient extends BrowserStackClient implements Automate
3536 * @param accessKey Access Key for your BrowserStack Automate account.
3637 */
3738 public AutomateClient (String username , String accessKey ) {
38- super (System .getProperty ("browserstack.automate.api" , BASE_URL ), username , accessKey );
39+ super (validateApiBaseUrl (System .getProperty ("browserstack.automate.api" , BASE_URL )),
40+ username , accessKey );
3941 }
4042
4143 /**
@@ -458,6 +460,7 @@ public String getSessionLogs(final Session session) throws AutomateException {
458460 }
459461
460462 try {
463+ validateBrowserStackUrl (session .getLogUrl ());
461464 BrowserStackRequest request = newRequest (Method .GET , session .getLogUrl (), false );
462465 request .getHttpRequest ().getHeaders ().setAccept ("*/*" );
463466 return request .asString ();
@@ -529,4 +532,79 @@ public String recycleKey() throws AutomateException {
529532 setAccessKey (newAccessKey );
530533 return newAccessKey ;
531534 }
535+
536+ /**
537+ * Validates the API base URL read from the JVM system property
538+ * {@code browserstack.automate.api} (or the built-in default).
539+ * <p>
540+ * Only {@code https} URLs whose host (parsed via
541+ * {@link java.net.URI#getHost()}) ends in {@code .browserstack.com} are
542+ * accepted.
543+ *
544+ * @param url URL to validate. Must be a syntactically-valid absolute URL.
545+ * @return the same URL, unchanged, when validation passes.
546+ * @throws IllegalArgumentException if the URL is null, malformed, uses a
547+ * non-https scheme, or has a host outside
548+ * {@code *.browserstack.com}.
549+ */
550+ private static String validateApiBaseUrl (String url ) {
551+ if (url == null ) {
552+ throw new IllegalArgumentException ("API base URL is null" );
553+ }
554+
555+ final URI uri ;
556+ try {
557+ uri = URI .create (url );
558+ } catch (IllegalArgumentException e ) {
559+ throw new IllegalArgumentException ("Malformed API base URL" , e );
560+ }
561+
562+ final String scheme = uri .getScheme ();
563+ if (!"https" .equalsIgnoreCase (scheme )) {
564+ throw new IllegalArgumentException ("Insecure API base URL scheme: " + scheme );
565+ }
566+
567+ final String host = uri .getHost ();
568+ if (host == null || !host .toLowerCase ().endsWith (".browserstack.com" )) {
569+ throw new IllegalArgumentException ("Untrusted API base URL host: " + host );
570+ }
571+
572+ return url ;
573+ }
574+
575+ /**
576+ * Validates that a URL is a BrowserStack-issued logs URL before fetching.
577+ * <p>
578+ * Only {@code https} scheme is allowed, and the host (parsed via
579+ * {@link java.net.URI#getHost()}, not a string suffix on the raw URL) must
580+ * end with {@code .browserstack.com}. A URL whose host has
581+ * {@code .browserstack.com} only as an internal substring — e.g.
582+ * {@code https://automate.browserstack.com.example/} — is rejected.
583+ *
584+ * @param url URL to validate. Must be a syntactically-valid absolute URL.
585+ * @throws AutomateException if the URL is malformed, uses a non-https scheme,
586+ * or has a host outside {@code *.browserstack.com}.
587+ */
588+ private static void validateBrowserStackUrl (String url ) throws AutomateException {
589+ if (url == null ) {
590+ throw new AutomateException ("Logs URL is null" , 400 );
591+ }
592+
593+ final URI uri ;
594+ try {
595+ uri = URI .create (url );
596+ } catch (IllegalArgumentException e ) {
597+ throw new AutomateException ("Malformed logs URL" , 400 );
598+ }
599+
600+ final String scheme = uri .getScheme ();
601+ if (!"https" .equalsIgnoreCase (scheme )) {
602+ throw new AutomateException ("Insecure logs URL scheme: " + scheme , 400 );
603+ }
604+
605+ final String host = uri .getHost ();
606+ if (host == null || !host .toLowerCase ().endsWith (".browserstack.com" )) {
607+ throw new AutomateException ("Untrusted logs URL host: " + host , 400 );
608+ }
609+ }
532610}
0 commit comments