Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pr-326.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wdio/browserstack-service": minor
---

- Secured the local connection between the SDK and the BrowserStack CLI with a per-run token.
13 changes: 12 additions & 1 deletion packages/browserstack-service/src/cli/grpcClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SDKClient,
grpcCredentials,
grpcChannel,
GrpcInterceptingCall,
StartBinSessionRequestConstructor,
StopBinSessionRequestConstructor,
ConnectBinSessionRequestConstructor,
Expand Down Expand Up @@ -40,8 +41,10 @@ import type {
TestOrchestrationRequest,
TestOrchestrationResponse
} from '../grpc/index.js'
import type { GrpcInterceptor } from '../grpc/index.js'

import PerformanceTester from '../instrumentation/performance/performance-tester.js'
import { CLI_AUTH_TOKEN_ENV, CLI_AUTH_TOKEN_HEADER } from '../constants.js'
import * as PERFORMANCE_SDK_EVENTS from '../instrumentation/performance/constants.js'
import { BStackLogger } from './cliLogger.js'

Expand Down Expand Up @@ -147,10 +150,18 @@ export class GrpcClient {
)

// Create a client using the channel
const authToken = process.env[CLI_AUTH_TOKEN_ENV]
const interceptors: GrpcInterceptor[] = authToken ? [(options, nextCall) => new GrpcInterceptingCall(nextCall(options), {
start(metadata, listener, next) {
metadata.set(CLI_AUTH_TOKEN_HEADER, authToken)
next(metadata, listener)
}
})] : []

this.client = new SDKClient(
listenAddress,
grpcCredentials.createInsecure(),
channelOptions
{ ...channelOptions, interceptors }
)

this.logger.info(`Connected to gRPC server at ${listenAddress}`)
Expand Down
6 changes: 5 additions & 1 deletion packages/browserstack-service/src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import util from 'node:util'
import { spawn } from 'node:child_process'
import { randomBytes } from 'node:crypto'

import { CLIUtils } from './cliUtils.js'
import PerformanceTester from '../instrumentation/performance/performance-tester.js'
Expand All @@ -12,7 +13,7 @@ import TestHubModule from './modules/testHubModule.js'
import type { ChildProcess } from 'node:child_process'
import type { StartBinSessionResponse } from '../grpc/index.js'
import type BaseModule from './modules/baseModule.js'
import { BROWSERSTACK_ACCESSIBILITY, BROWSERSTACK_OBSERVABILITY, BROWSERSTACK_TESTHUB_JWT, BROWSERSTACK_TESTHUB_UUID, CLI_STOP_TIMEOUT, TESTOPS_BUILD_COMPLETED_ENV, TESTOPS_SCREENSHOT_ENV, SERVER_SDK_CONFIG_KEY, SERVER_SDK_CONFIG_KEYS_KEY } from '../constants.js'
import { BROWSERSTACK_ACCESSIBILITY, BROWSERSTACK_OBSERVABILITY, BROWSERSTACK_TESTHUB_JWT, BROWSERSTACK_TESTHUB_UUID, CLI_STOP_TIMEOUT, TESTOPS_BUILD_COMPLETED_ENV, TESTOPS_SCREENSHOT_ENV, CLI_AUTH_TOKEN_ENV, SERVER_SDK_CONFIG_KEY, SERVER_SDK_CONFIG_KEYS_KEY } from '../constants.js'
import type { Options } from '@wdio/types'
import TestOpsConfig from '../testOps/testOpsConfig.js'
import WdioMochaTestFramework from './frameworks/wdioMochaTestFramework.js'
Expand Down Expand Up @@ -288,6 +289,9 @@ export class BrowserstackCLI {
}

const SDK_CLI_BIN_PATH = await this.getCliBinPath()

process.env[CLI_AUTH_TOKEN_ENV] = randomBytes(32).toString('hex')

const cmd:Array<string> = [SDK_CLI_BIN_PATH, 'sdk']
this.logger.debug(`spawning command='${cmd}'`)
// Create a child process
Expand Down
2 changes: 2 additions & 0 deletions packages/browserstack-service/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export const UPDATED_CLI_ENDPOINT = 'sdk/v1/update_cli'
export const CLI_STOP_TIMEOUT = 3000
export const SERVER_SDK_CONFIG_KEY = 'serverSdkConfig'
export const SERVER_SDK_CONFIG_KEYS_KEY = 'serverSdkConfigKeys'
export const CLI_AUTH_TOKEN_ENV = 'BROWSERSTACK_CLI_AUTH_TOKEN'
export const CLI_AUTH_TOKEN_HEADER = 'x-browserstack-cli-token'

export const NOT_ALLOWED_KEYS_IN_CAPS = ['includeTagsInTestingScope', 'excludeTagsInTestingScope', 'skipAppOverride', 'testManagementOptions', 'preferScenarioName', 'sessionNamePrependTopLevelSuiteTitle', 'sessionNameOmitTestTitle']
export const BROWSERSTACK_TEST_PLAN_ID = 'BROWSERSTACK_TEST_PLAN_ID'
Expand Down
2 changes: 1 addition & 1 deletion packages/browserstack-service/src/grpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export type {

// gRPC Service Client and credentials
export { SDKClient } from './generated/sdk.js';
export { credentials as grpcCredentials, Channel as grpcChannel, type ChannelCredentials } from '@grpc/grpc-js';
export { credentials as grpcCredentials, Channel as grpcChannel, InterceptingCall as GrpcInterceptingCall, type Interceptor as GrpcInterceptor, type ChannelCredentials } from '@grpc/grpc-js';

// Export message constructors
import * as SdkMessages from './generated/sdk-messages.js';
Expand Down
Loading