|
10 | 10 | except: |
11 | 11 | import pkg_resources |
12 | 12 |
|
13 | | -# LOC-6719 / INJ-002 (CWE-88): kwargs are forwarded to the BrowserStackLocal |
14 | | -# binary as flags. Without this allowlist an attacker-influenced kwarg can |
15 | | -# inject arbitrary flags — notably proxyHost / proxyPort / forceproxy to |
16 | | -# redirect tunnel traffic through an attacker-controlled proxy. |
17 | | -# |
18 | | -# Cross-referenced against the binary's COMMAND_CONFIGURATION table |
19 | | -# (browserStackTunnel/extensions/node/config/constants.js) and the public docs |
20 | | -# at https://www.browserstack.com/docs/local-testing/binary-params. Both the |
21 | | -# camelCase form ('name') and the kebab-case form ('alias') are accepted |
22 | | -# because user code passes both shapes. Internal-only flags (visible:false: |
23 | | -# region, bsHost, customRepeater, enterprise, public-interface-services, |
24 | | -# identifier, trusted-hosts), help/version flags, and undocumented internal |
25 | | -# flags (-r, -skipCheck, -daemonInstance, -tunnelIdentifier, -uniqueIdentifier) |
26 | | -# are intentionally excluded. |
27 | | -# |
28 | | -# Wrapper-managed keys (key, binarypath, logfile, source) are stripped from |
29 | | -# self.options in start() before this check runs and are not listed here. |
30 | | -# 'daemon' / 'logFile' are also omitted because they are emitted unconditionally |
31 | | -# by _generate_cmd and a user-supplied duplicate would conflict. |
32 | | -ALLOWED_OPTIONS = frozenset({ |
33 | | - # Verbose / logging |
34 | | - 'v', 'vv', 'vvv', 'verbose', |
35 | | - 'enableLoggingForAPI', 'enable-logging-for-api', |
36 | | - 'enableUTCLogging', 'enable-utc-logging', |
37 | | - # Folder testing |
38 | | - 'f', 'folder', |
39 | | - # Force / start behaviour |
40 | | - 'force', 'F', |
41 | | - 'forcelocal', 'force-local', 'forceLocal', |
42 | | - 'forceproxy', 'force-proxy', 'forceProxy', |
43 | | - 'onlyAutomate', 'only-automate', |
44 | | - # Host targeting / restriction |
45 | | - 'only', |
46 | | - 'include-hosts', 'exclude-hosts', |
47 | | - 'localIdentifier', 'local-identifier', |
48 | | - 'parallelRuns', 'parallel-runs', |
49 | | - # Corporate proxy |
50 | | - 'proxyHost', 'proxy-host', |
51 | | - 'proxyPort', 'proxy-port', |
52 | | - 'proxyUser', 'proxy-user', |
53 | | - 'proxyPass', 'proxy-pass', |
54 | | - 'disableProxyDiscovery', 'disable-proxy-discovery', |
55 | | - # Local proxy |
56 | | - 'localProxyHost', 'local-proxy-host', |
57 | | - 'localProxyPort', 'local-proxy-port', |
58 | | - 'localProxyUser', 'local-proxy-user', |
59 | | - 'localProxyPass', 'local-proxy-pass', |
60 | | - # PAC / HTTPS / protocol |
61 | | - 'pacFile', 'pac-file', |
62 | | - 'https-ports', |
63 | | - 'client-protocol', |
64 | | - # CA certificates |
65 | | - 'useCaCertificate', 'use-ca-certificate', |
66 | | - 'useSystemInstalledCa', 'use-system-installed-ca', |
67 | | - # NTLM proxy |
68 | | - 'ntlm-username', 'ntlm-password', 'ntlm-domain', 'ntlm-workstation', |
69 | | - # Dashboard / misc |
70 | | - 'disableDashboard', 'disable-dashboard', |
71 | | - 'config-file', |
72 | | - 'no-container', |
73 | | - 'connect-timeout', |
74 | | - 'timeout', |
75 | | - 'debug-utility', 'debug-url', |
76 | | -}) |
77 | | - |
78 | 13 | class Local: |
79 | 14 | def __init__(self, key=None, binary_path=None, **kwargs): |
80 | 15 | self.key = os.environ['BROWSERSTACK_ACCESS_KEY'] if 'BROWSERSTACK_ACCESS_KEY' in os.environ else key |
@@ -117,8 +52,6 @@ def get_package_version(self): |
117 | 52 | def _generate_cmd(self): |
118 | 53 | cmd = [self.binary_path, '-d', 'start', '-logFile', self.local_logfile_path, "-k", self.key, '--source', 'python:' + self.get_package_version()] |
119 | 54 | for o in self.options.keys(): |
120 | | - if o not in ALLOWED_OPTIONS: |
121 | | - raise BrowserStackLocalError('Unknown option: {}'.format(o)) |
122 | 55 | if self.options.get(o) is not None: |
123 | 56 | cmd = cmd + self.__xstr(o, self.options.get(o)) |
124 | 57 | return cmd |
|
0 commit comments