Skip to content

Mobile visreg#517

Open
cb-ekuersch wants to merge 16 commits intomasterfrom
mobile-visreg
Open

Mobile visreg#517
cb-ekuersch wants to merge 16 commits intomasterfrom
mobile-visreg

Conversation

@cb-ekuersch
Copy link
Contributor

What changed? Why?

Root cause (required for bugfixes)

UI changes

iOS Old iOS New
old screenshot new screenshot
Android Old Android New
old screenshot new screenshot
Web Old Web New
old screenshot new screenshot

Testing

How has it been tested?

  • Unit tests
  • Interaction tests
  • Pseudo State tests
  • Manual - Web
  • Manual - Android (Emulator / Device)
  • Manual - iOS (Emulator / Device)

Testing instructions

Illustrations/Icons Checklist

Required if this PR changes files under packages/illustrations/** or packages/icons/**

  • verified visreg changes with Terran (include link to visreg run/approval)
  • all illustration/icons names have been reviewed by Dom and/or Terran

Change management

type=routine
risk=low
impact=sev5

automerge=false

@cb-heimdall
Copy link
Collaborator

cb-heimdall commented Mar 18, 2026

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 1
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1
CODEOWNERS 🟡 See below

🟡 CODEOWNERS

Code Owner Status Calculation
ui-systems-eng-team 🟡 0/1
Denominator calculation
Additional CODEOWNERS Requirement
Show calculation
Sum 0
0
From CODEOWNERS 1
Sum 1

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

This shell command depends on an uncontrolled
absolute path
.
This shell command depends on an uncontrolled
absolute path
.
This shell command depends on an uncontrolled
absolute path
.
This shell command depends on an uncontrolled
absolute path
.

Copilot Autofix

AI 8 days 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 execFileSync to the import from child_process.
  • Replace the string-building cmd line with an array of arguments: ['test', flowPath, ...envFlags.flatMap(e => ['--env', e])].
  • Replace the execSync(cmd, ...) call with execFileSync('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.

Suggested changeset 1
packages/mobile-visreg/src/run.mjs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/mobile-visreg/src/run.mjs b/packages/mobile-visreg/src/run.mjs
--- a/packages/mobile-visreg/src/run.mjs
+++ b/packages/mobile-visreg/src/run.mjs
@@ -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}`);
EOF
@@ -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}`);
Copilot is powered by AI and may make mistakes. Always verify output.
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

This shell command depends on an uncontrolled
absolute path
.

Copilot Autofix

AI 8 days 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 execFileSync instead of or in addition to execSync at 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.

Suggested changeset 1
packages/mobile-visreg/src/upload.mjs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/mobile-visreg/src/upload.mjs b/packages/mobile-visreg/src/upload.mjs
--- a/packages/mobile-visreg/src/upload.mjs
+++ b/packages/mobile-visreg/src/upload.mjs
@@ -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.');
EOF
@@ -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.');
Copilot is powered by AI and may make mistakes. Always verify output.
@github-actions
Copy link
Contributor

github-actions bot commented Mar 23, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

2 participants