chore: mobile visreg#517
Conversation
✅ Heimdall Review Status
✅
|
| Code Owner | Status | Calculation | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ui-systems-eng-team |
✅
1/1
|
Denominator calculation
|
| const cmd = ['maestro', 'test', flowPath, ...envFlags.map((e) => `--env ${e}`)].join(' '); | ||
|
|
||
| console.log(`\nRunning: ${cmd}\n`); | ||
| execSync(cmd, { stdio: 'inherit', cwd: outputDir }); |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 3 months ago
In general, the fix is to avoid constructing a single shell command string and instead call execFileSync (or spawnSync) with the command and its arguments as separate array elements. This way, Node passes arguments directly to the process without an intervening shell, so special characters in paths or environment values cannot change command structure. For this code, the best fix is to change the Maestro invocation so that maestro is the executable and the rest of the tokens (test, flowPath, --env, KEY=VALUE) are elements in an array, then use execFileSync instead of execSync. This preserves functionality but removes shell interpretation.
Concretely in packages/mobile-visreg/src/run.mjs:
- Add
execFileSyncto the import fromchild_process. - Replace the string-building
cmdline with an array of arguments:['test', flowPath, ...envFlags.flatMap(e => ['--env', e])]. - Replace the
execSync(cmd, ...)call withexecFileSync('maestro', args, { stdio: 'inherit', cwd: outputDir });. - Keep the existing console log of the human-readable command string (or reconstruct it from the args) for visibility if desired, but this log should not be executed by the shell.
The earlier execSync that runs node src/generate-flows.mjs ${platform} is technically similar, but it does not involve the tainted flowPath/absolute path and only injects platform (which is limited to 'ios' by default or whatever the caller supplies). Since CodeQL’s specific alerts here are about the Maestro command, we will leave that first execSync unchanged to avoid unrequested behavioral changes.
| @@ -1,4 +1,4 @@ | ||
| import { execSync } from 'child_process'; | ||
| import { execSync, execFileSync } from 'child_process'; | ||
| import { mkdirSync, readdirSync } from 'fs'; | ||
| import { resolve, dirname } from 'path'; | ||
| import { fileURLToPath } from 'url'; | ||
| @@ -50,10 +50,11 @@ | ||
| envFlags.push(`ROUTE_NAME=${route}`); | ||
| } | ||
|
|
||
| const cmd = ['maestro', 'test', flowPath, ...envFlags.map((e) => `--env ${e}`)].join(' '); | ||
| const maestroArgs = ['test', flowPath, ...envFlags.flatMap((e) => ['--env', e])]; | ||
| const cmd = ['maestro', ...maestroArgs].join(' '); | ||
|
|
||
| console.log(`\nRunning: ${cmd}\n`); | ||
| execSync(cmd, { stdio: 'inherit', cwd: outputDir }); | ||
| execFileSync('maestro', maestroArgs, { stdio: 'inherit', cwd: outputDir }); | ||
|
|
||
| const screenshots = readdirSync(outputDir).filter((f) => f.endsWith('.png')); | ||
| console.log(`\nCapture complete: ${screenshots.length} screenshots in ${outputDir}`); |
| const screenshotDir = resolve(dir); | ||
| console.log(`Uploading screenshots from ${screenshotDir} to Percy...`); | ||
|
|
||
| execSync(`npx percy upload ${screenshotDir}`, { stdio: 'inherit' }); |
Check warning
Code scanning / CodeQL
Shell command built from environment values Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 3 months ago
In general, to fix shell command injection or misinterpretation issues, avoid constructing a single shell command string that includes untrusted values. Instead, invoke the underlying program directly (bypassing the shell) and pass untrusted values as separate arguments in an array, using child_process.execFile / execFileSync or spawn / spawnSync. This prevents the shell from interpreting special characters in the arguments.
For this specific case in packages/mobile-visreg/src/upload.mjs, replace execSync with execFileSync from child_process, and change the invocation from a template string to a command plus arguments array:
- Import
execFileSyncinstead of or in addition toexecSyncat the top of the file. - Replace
execSync(\npx percy upload ${screenshotDir}`, { stdio: 'inherit' });withexecFileSync('npx', ['percy', 'upload', screenshotDir], { stdio: 'inherit' });`.
This keeps all existing behavior (still runs npx percy upload <dir> and inherits stdio) but avoids the shell entirely, so screenshotDir is treated as a literal argument. All changes are confined to packages/mobile-visreg/src/upload.mjs within the shown snippet, and no additional methods are needed beyond the new import.
| @@ -1,4 +1,4 @@ | ||
| import { execSync } from 'child_process'; | ||
| import { execFileSync } from 'child_process'; | ||
| import { resolve } from 'path'; | ||
|
|
||
| function parseArgs() { | ||
| @@ -24,6 +24,6 @@ | ||
| const screenshotDir = resolve(dir); | ||
| console.log(`Uploading screenshots from ${screenshotDir} to Percy...`); | ||
|
|
||
| execSync(`npx percy upload ${screenshotDir}`, { stdio: 'inherit' }); | ||
| execFileSync('npx', ['percy', 'upload', screenshotDir], { stdio: 'inherit' }); | ||
|
|
||
| console.log('\nUpload complete. Visit percy.io to review the build.'); |
94dd9a3 to
30d2737
Compare
1d6940e to
d0b8775
Compare
| runs-on: [small, default-config] | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| # Test the published action | ||
| # - name: New CDS Action | ||
| # uses: [fill this in on new branch] | ||
| # with: [fill this in on new branch] |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
In general, the fix is to explicitly declare a permissions: block that restricts the GITHUB_TOKEN to the minimal scopes required. For this stub workflow, the only visible requirement is to read repository contents (for actions/checkout), so contents: read is sufficient. Adding this at the workflow root applies to all jobs unless they override it.
The best fix without changing functionality is to add a root-level permissions: block right after the name: line, before the on: block, in .github/workflows/guard-debug-workflow.yml. This will document and enforce that the workflow’s GITHUB_TOKEN has only read access to repository contents, which is compatible with the existing steps. No imports, methods, or additional definitions are needed, as this is purely a YAML configuration change inside the workflow.
| @@ -16,6 +16,9 @@ | ||
|
|
||
| name: Debug Workflow (replace me in your branch) | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| on: | ||
| # Manual trigger for testing | ||
| workflow_dispatch: |
What changed? Why?
Replaces the legacy Detox-based packages/ui-mobile-visreg package with a new, simpler approach: Maestro for screenshot capture and BrowserStack App Percy for visual comparison.
The old Detox setup required specialized native builds with injected gray-box test code, making the build matrix complex (8 variations) and CI slow. Maestro navigates the app via deep-links and calls takeScreenshot — no special native build required. The standard release prebuild is used directly, and CI patches the JS bundle rather than doing a full native rebuild on every run.
Percy project: https://percy.io/d424b72f/web/cds-mobile-visreg-03c97426
Key Changes
New packages/mobile-visreg
Removed legacy packages/ui-mobile-visreg
Deep-link navigation fix (apps/mobile-app/src/App.tsx)
CI workflow (.github/workflows/visreg-mobile.yml)
Documentation
Screenshots
Testing
How has it been tested?
In addition to running the test suite locally with my simulator I performed and full end to end test run by uploading screenshots to percy from my machine from both master and v9 visreg branches.
Sample comparison build: https://percy.io/d424b72f/web/cds-mobile-visreg-03c97426/builds/47997661/changed/2548746021?browser=chrome&browser_ids=63%2C69%2C70%2C71&group_snapshots_by=similar_diff&subcategories=approved&test_case_ids=&viewLayout=side-by-side&viewMode=new&width=1206&widths=1206
Testing instructions
If you plan to test uploading screenshots from your machine, the the percy token from our project.
Illustrations/Icons Checklist
Required if this PR changes files under
packages/illustrations/**orpackages/icons/**Change management
type=routine
risk=low
impact=sev5
automerge=false