Skip to content

Commit 5f97a34

Browse files
authored
Merge pull request #7 from BrowserStackCE/npm-deploy
SDK path on NPM fix Other improvements on path sanitizations and utf8 encoding
2 parents b290ddc + fde3e27 commit 5f97a34

File tree

2 files changed

+70
-21
lines changed

2 files changed

+70
-21
lines changed

index.mjs

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import { globSync } from 'glob';
1212
import spawn from 'cross-spawn';
1313
import * as dotenv from 'dotenv';
1414
import { exit } from 'process';
15-
import sanitize from 'sanitize-filename';
16-
import { fileURLToPath } from 'url';
15+
import { fileURLToPath, pathToFileURL } from 'url';
1716

1817
dotenv.config();
1918
commander
@@ -48,14 +47,29 @@ rimrafSync(options.buildFolderPath)
4847
fs.mkdirSync(options.buildFolderPath);
4948

5049
function readFile(filename) {
51-
return JSON.parse(
52-
fs.readFileSync(
53-
path.join(
54-
'.',
55-
sanitize(filename)
56-
)
57-
)
58-
)
50+
if (typeof filename !== 'string' || filename.length === 0) {
51+
throw new Error('Invalid filename')
52+
}
53+
if (path.extname(filename) !== '.side') {
54+
throw new Error('Only .side files are allowed')
55+
}
56+
57+
// Resolve against cwd without using path.resolve/join for Semgrep compliance
58+
const cwdUrl = pathToFileURL(process.cwd() + '/')
59+
const fileUrl = new URL(filename, cwdUrl)
60+
const absolutePath = fileURLToPath(fileUrl)
61+
62+
// Containment check: ensure the resolved path is inside cwd
63+
const rel = path.relative(process.cwd(), absolutePath)
64+
if (rel.startsWith('..') || path.isAbsolute(rel)) {
65+
throw new Error('Access outside the working directory is not allowed')
66+
}
67+
68+
if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
69+
throw new Error('Target file does not exist or is not a regular file')
70+
}
71+
72+
return JSON.parse(fs.readFileSync(absolutePath, 'utf8'))
5973
}
6074

6175
function normalizeProject(project) {
@@ -92,19 +106,54 @@ if (options.outputFormat && options.outputFile)
92106
reporter = ['--reporter', options.outputFormat, '--reporter-options', 'output=' + options.outputFile]
93107

94108
const __dirname = path.dirname(fileURLToPath(import.meta.url));
95-
const browserstackSdkPath = path.join(__dirname, 'node_modules', '.bin', 'browserstack-node-sdk');
96109
const sideRunnerNodeModules = path.join(__dirname, 'node_modules');
97110

98-
const testSuiteProcess = spawn.sync(browserstackSdkPath, ['mocha', '_generated', '--timeouts', options.testTimeout, '-g', options.filter, '--browserstack.config', options.browserstackConfig, ...reporter], {
99-
stdio: 'inherit',
100-
env: {
101-
...process.env,
102-
testTimeout: options.testTimeout,
103-
NODE_PATH: `${sideRunnerNodeModules}${path.delimiter}${process.env.NODE_PATH || ''}`
104-
}
105-
});
111+
// Resolve BrowserStack SDK binary robustly with fallbacks
112+
const sdkCandidates = [
113+
path.join(__dirname, 'node_modules', '.bin', 'browserstack-node-sdk'),
114+
path.join(process.cwd(), 'node_modules', '.bin', 'browserstack-node-sdk'),
115+
// Fall back to letting the OS PATH resolve it (e.g., project-level node_modules/.bin)
116+
'browserstack-node-sdk'
117+
]
118+
const sdkBin = sdkCandidates.find(p => {
119+
try {
120+
// If candidate is a bare command (no path separators), let it pass
121+
if (!p.includes(path.sep)) return true
122+
return fs.existsSync(p)
123+
} catch {
124+
return false
125+
}
126+
}) || 'browserstack-node-sdk'
127+
128+
if (options.debug) {
129+
log.debug(`Using BrowserStack SDK binary: ${sdkBin}`)
130+
}
131+
132+
let testSuiteProcess
133+
try {
134+
testSuiteProcess = spawn.sync(
135+
sdkBin,
136+
['mocha', '_generated', '--timeouts', String(options.testTimeout), '-g', options.filter, '--browserstack.config', options.browserstackConfig, ...reporter],
137+
{
138+
stdio: 'inherit',
139+
env: {
140+
...process.env,
141+
testTimeout: options.testTimeout,
142+
NODE_PATH: `${sideRunnerNodeModules}${path.delimiter}${process.env.NODE_PATH || ''}`
143+
}
144+
}
145+
)
146+
} catch (err) {
147+
log.error(`Failed to start BrowserStack SDK at "${sdkBin}": ${err.message}`)
148+
exit(1)
149+
}
150+
151+
if (testSuiteProcess.error) {
152+
log.error(`Failed to start BrowserStack SDK at "${sdkBin}": ${testSuiteProcess.error.message}`)
153+
exit(1)
154+
}
106155

107156
if (!options.debug) {
108157
rimrafSync(options.buildFolderPath)
109158
}
110-
exit(testSuiteProcess.status)
159+
exit(testSuiteProcess.status ?? 1)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "git",
55
"url": "git+https://github.com/BrowserStackCE/browserstack-side-runner.git"
66
},
7-
"version": "2.3.0",
7+
"version": "2.4.0",
88
"main": "index.mjs",
99
"homepage": "https://github.com/BrowserStackCE/browserstack-side-runner#readme",
1010
"scripts": {

0 commit comments

Comments
 (0)