start-command 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # start-command
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8ea5659: Add user isolation support with --isolated-user and --keep-user options
8
+
9
+ Implements user isolation that creates a new isolated user to run commands:
10
+
11
+ ## --isolated-user option (create isolated user with same permissions)
12
+ - Add --isolated-user, -u option to create a new isolated user automatically
13
+ - New user inherits group memberships from current user (sudo, docker, wheel, etc.)
14
+ - User is automatically deleted after command completes (unless --keep-user)
15
+ - Works with screen and tmux isolation backends (not docker)
16
+ - Optional custom username via --isolated-user=myname or -u myname
17
+ - For screen/tmux: Wraps commands with sudo -n -u <user>
18
+ - Requires sudo NOPASSWD configuration for useradd/userdel/sudo
19
+
20
+ ## --keep-user option
21
+ - Add --keep-user option to prevent user deletion after command completes
22
+ - Useful when you need to inspect files created during execution
23
+ - User must be manually deleted with: sudo userdel -r <username>
24
+
25
+ ## Other improvements
26
+ - Add comprehensive tests for user isolation
27
+ - Update documentation with user isolation examples
28
+ - Integrate --keep-alive and --auto-remove-docker-container from main branch
29
+
30
+ Usage:
31
+ - $ --isolated-user -- npm test # Auto-generated username, auto-deleted
32
+ - $ --isolated-user myrunner -- npm start # Custom username
33
+ - $ -u myrunner -- npm start # Short form
34
+ - $ --isolated screen --isolated-user -- npm test # Combine with process isolation
35
+ - $ --isolated-user --keep-user -- npm test # Keep user after completion
36
+
37
+ Note: User isolation requires sudo NOPASSWD configuration.
38
+
3
39
  ## 0.9.0
4
40
 
5
41
  ### Minor Changes
package/README.md CHANGED
@@ -152,6 +152,44 @@ $ --isolated docker --image oven/bun:latest -- bun install
152
152
  $ -i tmux -s my-session -d bun start
153
153
  ```
154
154
 
155
+ ### User Isolation
156
+
157
+ Create a new isolated user with the same group permissions as your current user to run commands in complete isolation:
158
+
159
+ ```bash
160
+ # Create an isolated user with same permissions and run command
161
+ $ --isolated-user -- npm test
162
+
163
+ # Specify custom username for the isolated user
164
+ $ --isolated-user myrunner -- npm start
165
+ $ -u myrunner -- npm start
166
+
167
+ # Combine with process isolation (screen or tmux)
168
+ $ --isolated screen --isolated-user -- npm test
169
+
170
+ # Keep the user after command completes (don't delete)
171
+ $ --isolated-user --keep-user -- npm start
172
+
173
+ # The isolated user inherits your group memberships:
174
+ # - sudo group (if you have it)
175
+ # - docker group (if you have it)
176
+ # - wheel, admin, and other privileged groups
177
+ ```
178
+
179
+ The `--isolated-user` option:
180
+
181
+ - Creates a new system user with the same group memberships as your current user
182
+ - Runs the command as that user
183
+ - Automatically deletes the user after the command completes (unless `--keep-user` is specified)
184
+ - Requires sudo access without password (NOPASSWD configuration)
185
+ - Works with screen and tmux isolation backends (not docker)
186
+
187
+ This is useful for:
188
+
189
+ - Running untrusted code in isolation
190
+ - Testing with a clean user environment
191
+ - Ensuring commands don't affect your user's files
192
+
155
193
  #### Supported Backends
156
194
 
157
195
  | Backend | Description | Installation |
@@ -162,15 +200,17 @@ $ -i tmux -s my-session -d bun start
162
200
 
163
201
  #### Isolation Options
164
202
 
165
- | Option | Description |
166
- | -------------------------------- | ----------------------------------------------------- |
167
- | `--isolated, -i` | Isolation backend (screen, tmux, docker) |
168
- | `--attached, -a` | Run in attached/foreground mode (default) |
169
- | `--detached, -d` | Run in detached/background mode |
170
- | `--session, -s` | Custom session/container name |
171
- | `--image` | Docker image (required for docker isolation) |
172
- | `--keep-alive, -k` | Keep session alive after command completes |
173
- | `--auto-remove-docker-container` | Auto-remove docker container after exit (docker only) |
203
+ | Option | Description |
204
+ | -------------------------------- | --------------------------------------------------------- |
205
+ | `--isolated, -i` | Isolation backend (screen, tmux, docker) |
206
+ | `--attached, -a` | Run in attached/foreground mode (default) |
207
+ | `--detached, -d` | Run in detached/background mode |
208
+ | `--session, -s` | Custom session/container name |
209
+ | `--image` | Docker image (required for docker isolation) |
210
+ | `--isolated-user, -u [name]` | Create isolated user with same permissions (screen/tmux) |
211
+ | `--keep-user` | Keep isolated user after command completes (don't delete) |
212
+ | `--keep-alive, -k` | Keep session alive after command completes |
213
+ | `--auto-remove-docker-container` | Auto-remove docker container after exit (docker only) |
174
214
 
175
215
  **Note:** Using both `--attached` and `--detached` together will result in an error - you must choose one mode.
176
216
 
package/REQUIREMENTS.md CHANGED
@@ -142,6 +142,8 @@ Support two patterns for passing wrapper options:
142
142
  - `--detached, -d`: Run in detached/background mode
143
143
  - `--session, -s <name>`: Custom session name
144
144
  - `--image <image>`: Docker image (required for docker backend)
145
+ - `--isolated-user, -u [username]`: Create new isolated user with same group permissions as current user
146
+ - `--keep-user`: Keep isolated user after command completes (don't delete)
145
147
  - `--keep-alive, -k`: Keep isolation environment alive after command exits (disabled by default)
146
148
  - `--auto-remove-docker-container`: Automatically remove docker container after exit (disabled by default, only valid with --isolated docker)
147
149
 
@@ -157,7 +159,50 @@ Support two patterns for passing wrapper options:
157
159
  - **Detached mode**: Command runs in background, session info displayed for reattachment
158
160
  - **Conflict handling**: If both --attached and --detached are specified, show error asking user to choose one
159
161
 
160
- #### 6.5 Auto-Exit Behavior
162
+ #### 6.5 User Isolation
163
+
164
+ - `--isolated-user, -u [username]`: Create a new isolated user with same permissions
165
+ - Creates user with same group memberships as current user (sudo, docker, wheel, etc.)
166
+ - Automatically generates username if not specified
167
+ - User is automatically deleted after command completes (unless `--keep-user` is specified)
168
+ - For screen/tmux: Wraps command with `sudo -n -u <username>`
169
+ - Requires sudo NOPASSWD for useradd/userdel commands
170
+ - Works with screen and tmux isolation (not docker)
171
+
172
+ #### 6.6 Keep User Option
173
+
174
+ - `--keep-user`: Keep the isolated user after command completes
175
+ - Only valid with `--isolated-user` option
176
+ - User must be manually deleted later with `sudo userdel -r <username>`
177
+
178
+ Example usage:
179
+
180
+ ```bash
181
+ # Create isolated user and run command (user auto-deleted after)
182
+ $ --isolated-user -- npm test
183
+
184
+ # Custom username for isolated user
185
+ $ --isolated-user myrunner -- npm start
186
+ $ -u myrunner -- npm start
187
+
188
+ # Combine with screen isolation
189
+ $ --isolated screen --isolated-user -- npm test
190
+
191
+ # Combine with tmux detached mode
192
+ $ -i tmux -d --isolated-user testuser -- npm run build
193
+
194
+ # Keep user after command completes
195
+ $ --isolated-user --keep-user -- npm test
196
+ ```
197
+
198
+ Benefits:
199
+
200
+ - Clean user environment for each run
201
+ - Inherits sudo/docker access from current user
202
+ - Files created during execution belong to isolated user
203
+ - Automatic cleanup after execution (unless --keep-user)
204
+
205
+ #### 6.7 Auto-Exit Behavior
161
206
 
162
207
  By default, all isolation environments (screen, tmux, docker) automatically exit after the target command completes execution. This ensures:
163
208
 
@@ -180,10 +225,11 @@ The `--auto-remove-docker-container` flag enables automatic removal of the conta
180
225
 
181
226
  Note: `--auto-remove-docker-container` is only valid with `--isolated docker` and is independent of the `--keep-alive` flag.
182
227
 
183
- #### 6.6 Graceful Degradation
228
+ #### 6.8 Graceful Degradation
184
229
 
185
230
  - If isolation backend is not installed, show informative error with installation instructions
186
231
  - If session creation fails, report error with details
232
+ - If sudo fails due to password requirement, command will fail with sudo error
187
233
 
188
234
  ## Configuration Options
189
235
 
@@ -0,0 +1,83 @@
1
+ # User Isolation Research
2
+
3
+ ## Issue #30: Support user isolation
4
+
5
+ ### Understanding the Requirement
6
+
7
+ Based on the issue description:
8
+
9
+ > We need to find a way to support not only isolation in screen, but also isolation by user at the same time.
10
+
11
+ And the clarification from the user:
12
+
13
+ > No, there is no way to use existing user to run the command, user isolation should mean we create user - run command using this user, after command have finished we can delete user, unless we have `--keep-user` option.
14
+
15
+ This means:
16
+
17
+ 1. Running commands in isolated environments (screen, tmux, docker) - **ALREADY IMPLEMENTED**
18
+ 2. Creating new isolated users with same permissions as current user - **IMPLEMENTED**
19
+ 3. Automatic cleanup of isolated users after command completes - **IMPLEMENTED**
20
+ 4. Option to keep the user (`--keep-user`) - **IMPLEMENTED**
21
+
22
+ ### Related Issues
23
+
24
+ - Issue #31: Support ssh isolation (execute commands on remote ssh servers)
25
+ - Issue #9: Isolation support (closed - implemented screen/tmux/docker)
26
+
27
+ ### Final Implementation
28
+
29
+ The `--isolated-user` option creates a new isolated user with the same group memberships as the current user:
30
+
31
+ ```bash
32
+ # Create isolated user and run command (user auto-deleted after)
33
+ $ --isolated-user -- npm test
34
+
35
+ # Custom username for isolated user
36
+ $ --isolated-user myrunner -- npm start
37
+ $ -u myrunner -- npm start
38
+
39
+ # Combine with screen isolation
40
+ $ --isolated screen --isolated-user -- npm test
41
+
42
+ # Combine with tmux detached mode
43
+ $ -i tmux -d --isolated-user testuser -- npm run build
44
+
45
+ # Keep user after command completes
46
+ $ --isolated-user --keep-user -- npm test
47
+ ```
48
+
49
+ ### How It Works
50
+
51
+ 1. **User Creation**
52
+ - Creates new system user with same group memberships as current user
53
+ - Inherits sudo, docker, wheel, admin, and other groups
54
+ - Uses `sudo useradd` with `-G` flag for groups
55
+
56
+ 2. **Command Execution**
57
+ - For screen/tmux: Wraps command with `sudo -n -u <user>`
58
+ - For standalone (no isolation backend): Uses `sudo -n -u <user> sh -c '<command>'`
59
+
60
+ 3. **Cleanup**
61
+ - After command completes, user is deleted with `sudo userdel -r <user>`
62
+ - Unless `--keep-user` flag is specified
63
+
64
+ ### Requirements
65
+
66
+ - `sudo` access with NOPASSWD configuration for:
67
+ - `useradd` - to create the isolated user
68
+ - `userdel` - to delete the isolated user
69
+ - `sudo -u` - to run commands as the isolated user
70
+
71
+ ### Benefits
72
+
73
+ - Clean user environment for each run
74
+ - Inherits sudo/docker access from current user
75
+ - Files created during execution belong to isolated user
76
+ - Automatic cleanup after execution (unless --keep-user)
77
+ - Prevents untrusted code from affecting your user's files
78
+
79
+ ### Limitations
80
+
81
+ - Not supported with Docker isolation (Docker has its own user isolation mechanism)
82
+ - Requires sudo NOPASSWD configuration
83
+ - Only works on Unix-like systems (Linux, macOS)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-command",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Gamification of coding, execute any command with ability to auto-report issues on GitHub",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/bin/cli.js CHANGED
@@ -15,12 +15,19 @@ const {
15
15
  } = require('../lib/args-parser');
16
16
  const {
17
17
  runIsolated,
18
+ runAsIsolatedUser,
18
19
  getTimestamp,
19
20
  createLogHeader,
20
21
  createLogFooter,
21
22
  writeLogFile,
22
23
  createLogPath,
23
24
  } = require('../lib/isolation');
25
+ const {
26
+ createIsolatedUser,
27
+ deleteUser,
28
+ hasSudoAccess,
29
+ getCurrentUserGroups,
30
+ } = require('../lib/user-manager');
24
31
 
25
32
  // Configuration from environment variables
26
33
  const config = {
@@ -211,37 +218,33 @@ function getToolVersion(toolName, versionFlag, verbose = false) {
211
218
  return firstLine || null;
212
219
  }
213
220
 
214
- /**
215
- * Print usage information
216
- */
221
+ /** Print usage information */
217
222
  function printUsage() {
218
- console.log('Usage: $ [options] [--] <command> [args...]');
219
- console.log(' $ <command> [args...]');
220
- console.log('');
221
- console.log('Options:');
222
- console.log(
223
- ' --isolated, -i <environment> Run in isolated environment (screen, tmux, docker)'
224
- );
225
- console.log(' --attached, -a Run in attached mode (foreground)');
226
- console.log(' --detached, -d Run in detached mode (background)');
227
- console.log(' --session, -s <name> Session name for isolation');
228
- console.log(
229
- ' --image <image> Docker image (required for docker isolation)'
230
- );
231
- console.log(
232
- ' --keep-alive, -k Keep isolation environment alive after command exits'
233
- );
234
- console.log(
235
- ' --auto-remove-docker-container Automatically remove docker container after exit (disabled by default)'
236
- );
237
- console.log(' --version, -v Show version information');
238
- console.log('');
239
- console.log('Examples:');
240
- console.log(' $ echo "Hello World"');
241
- console.log(' $ bun test');
242
- console.log(' $ --isolated tmux -- bun start');
243
- console.log(' $ -i screen -d bun start');
244
- console.log(' $ --isolated docker --image oven/bun:latest -- bun install');
223
+ console.log(`Usage: $ [options] [--] <command> [args...]
224
+ $ <command> [args...]
225
+
226
+ Options:
227
+ --isolated, -i <env> Run in isolated environment (screen, tmux, docker)
228
+ --attached, -a Run in attached mode (foreground)
229
+ --detached, -d Run in detached mode (background)
230
+ --session, -s <name> Session name for isolation
231
+ --image <image> Docker image (required for docker isolation)
232
+ --isolated-user, -u [name] Create isolated user with same permissions
233
+ --keep-user Keep isolated user after command completes
234
+ --keep-alive, -k Keep isolation environment alive after command exits
235
+ --auto-remove-docker-container Auto-remove docker container after exit
236
+ --version, -v Show version information
237
+
238
+ Examples:
239
+ $ echo "Hello World"
240
+ $ bun test
241
+ $ --isolated tmux -- bun start
242
+ $ -i screen -d bun start
243
+ $ --isolated docker --image oven/bun:latest -- bun install
244
+ $ --isolated-user -- npm test # Create isolated user
245
+ $ -u myuser -- npm start # Custom username
246
+ $ -i screen --isolated-user -- npm test # Combine with process isolation
247
+ $ --isolated-user --keep-user -- npm start`);
245
248
  console.log('');
246
249
  console.log('Piping with $:');
247
250
  console.log(' echo "hi" | $ agent # Preferred - pipe TO $ command');
@@ -311,8 +314,8 @@ if (!config.disableSubstitutions) {
311
314
 
312
315
  // Main execution
313
316
  (async () => {
314
- // Check if running in isolation mode
315
- if (hasIsolation(wrapperOptions)) {
317
+ // Check if running in isolation mode or with user isolation
318
+ if (hasIsolation(wrapperOptions) || wrapperOptions.user) {
316
319
  await runWithIsolation(wrapperOptions, command);
317
320
  } else {
318
321
  await runDirect(command);
@@ -330,45 +333,112 @@ async function runWithIsolation(options, cmd) {
330
333
  const startTime = getTimestamp();
331
334
 
332
335
  // Create log file path
333
- const logFilePath = createLogPath(environment);
336
+ const logFilePath = createLogPath(environment || 'direct');
334
337
 
335
338
  // Get session name (will be generated by runIsolated if not provided)
336
339
  const sessionName =
337
340
  options.session ||
338
- `${environment}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
341
+ `${environment || 'start'}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
342
+
343
+ // Handle --isolated-user option: create a new user with same permissions
344
+ let createdUser = null;
345
+
346
+ if (options.user) {
347
+ // Check for sudo access
348
+ if (!hasSudoAccess()) {
349
+ console.error(
350
+ 'Error: --isolated-user requires sudo access without password.'
351
+ );
352
+ console.error(
353
+ 'Configure NOPASSWD in sudoers or run with appropriate permissions.'
354
+ );
355
+ process.exit(1);
356
+ }
357
+
358
+ // Get current user groups to show what will be inherited
359
+ const currentGroups = getCurrentUserGroups();
360
+ const importantGroups = ['sudo', 'docker', 'wheel', 'admin'].filter((g) =>
361
+ currentGroups.includes(g)
362
+ );
363
+
364
+ console.log(`[User Isolation] Creating new user with same permissions...`);
365
+ if (importantGroups.length > 0) {
366
+ console.log(
367
+ `[User Isolation] Inheriting groups: ${importantGroups.join(', ')}`
368
+ );
369
+ }
370
+
371
+ // Create the isolated user
372
+ const userResult = createIsolatedUser(options.userName);
373
+ if (!userResult.success) {
374
+ console.error(
375
+ `Error: Failed to create isolated user: ${userResult.message}`
376
+ );
377
+ process.exit(1);
378
+ }
379
+
380
+ createdUser = userResult.username;
381
+ console.log(`[User Isolation] Created user: ${createdUser}`);
382
+ if (userResult.groups && userResult.groups.length > 0) {
383
+ console.log(
384
+ `[User Isolation] User groups: ${userResult.groups.join(', ')}`
385
+ );
386
+ }
387
+ if (options.keepUser) {
388
+ console.log(`[User Isolation] User will be kept after command completes`);
389
+ }
390
+ console.log('');
391
+ }
339
392
 
340
393
  // Print start message (unified format)
341
394
  console.log(`[${startTime}] Starting: ${cmd}`);
342
395
  console.log('');
343
396
 
344
397
  // Log isolation info
345
- console.log(`[Isolation] Environment: ${environment}, Mode: ${mode}`);
398
+ if (environment) {
399
+ console.log(`[Isolation] Environment: ${environment}, Mode: ${mode}`);
400
+ }
346
401
  if (options.session) {
347
402
  console.log(`[Isolation] Session: ${options.session}`);
348
403
  }
349
404
  if (options.image) {
350
405
  console.log(`[Isolation] Image: ${options.image}`);
351
406
  }
407
+ if (createdUser) {
408
+ console.log(`[Isolation] User: ${createdUser} (isolated)`);
409
+ }
352
410
  console.log('');
353
411
 
354
412
  // Create log content
355
413
  let logContent = createLogHeader({
356
414
  command: cmd,
357
- environment,
415
+ environment: environment || 'direct',
358
416
  mode,
359
417
  sessionName,
360
418
  image: options.image,
419
+ user: createdUser,
361
420
  startTime,
362
421
  });
363
422
 
364
- // Run in isolation
365
- const result = await runIsolated(environment, cmd, {
366
- session: options.session,
367
- image: options.image,
368
- detached: mode === 'detached',
369
- keepAlive: options.keepAlive,
370
- autoRemoveDockerContainer: options.autoRemoveDockerContainer,
371
- });
423
+ let result;
424
+
425
+ if (environment) {
426
+ // Run in isolation backend (screen, tmux, docker)
427
+ result = await runIsolated(environment, cmd, {
428
+ session: options.session,
429
+ image: options.image,
430
+ detached: mode === 'detached',
431
+ user: createdUser,
432
+ keepAlive: options.keepAlive,
433
+ autoRemoveDockerContainer: options.autoRemoveDockerContainer,
434
+ });
435
+ } else if (createdUser) {
436
+ // Run directly as the created user (no isolation backend)
437
+ result = await runAsIsolatedUser(cmd, createdUser);
438
+ } else {
439
+ // This shouldn't happen in isolation mode, but handle gracefully
440
+ result = { success: false, message: 'No isolation configuration provided' };
441
+ }
372
442
 
373
443
  // Get exit code
374
444
  const exitCode =
@@ -390,6 +460,23 @@ async function runWithIsolation(options, cmd) {
390
460
  console.log(`Exit code: ${exitCode}`);
391
461
  console.log(`Log saved: ${logFilePath}`);
392
462
 
463
+ // Cleanup: delete the created user if we created one (unless --keep-user)
464
+ if (createdUser && !options.keepUser) {
465
+ console.log('');
466
+ console.log(`[User Isolation] Cleaning up user: ${createdUser}`);
467
+ const deleteResult = deleteUser(createdUser, { removeHome: true });
468
+ if (deleteResult.success) {
469
+ console.log(`[User Isolation] User deleted successfully`);
470
+ } else {
471
+ console.log(`[User Isolation] Warning: ${deleteResult.message}`);
472
+ }
473
+ } else if (createdUser && options.keepUser) {
474
+ console.log('');
475
+ console.log(
476
+ `[User Isolation] Keeping user: ${createdUser} (use 'sudo userdel -r ${createdUser}' to delete)`
477
+ );
478
+ }
479
+
393
480
  process.exit(exitCode);
394
481
  }
395
482
 
@@ -11,6 +11,8 @@
11
11
  * --detached, -d Run in detached mode (background)
12
12
  * --session, -s <name> Session name for isolation
13
13
  * --image <image> Docker image (required for docker isolation)
14
+ * --isolated-user, -u [username] Create isolated user with same permissions (auto-generated name if not specified)
15
+ * --keep-user Keep isolated user after command completes (don't delete)
14
16
  * --keep-alive, -k Keep isolation environment alive after command exits
15
17
  * --auto-remove-docker-container Automatically remove docker container after exit (disabled by default)
16
18
  */
@@ -36,6 +38,9 @@ function parseArgs(args) {
36
38
  detached: false, // Run in detached mode
37
39
  session: null, // Session name
38
40
  image: null, // Docker image
41
+ user: false, // Create isolated user
42
+ userName: null, // Optional custom username for isolated user
43
+ keepUser: false, // Keep isolated user after command completes (don't delete)
39
44
  keepAlive: false, // Keep environment alive after command exits
40
45
  autoRemoveDockerContainer: false, // Auto-remove docker container after exit
41
46
  };
@@ -175,6 +180,35 @@ function parseOption(args, index, options) {
175
180
  return 1;
176
181
  }
177
182
 
183
+ // --isolated-user or -u [optional-username] - creates isolated user with same permissions
184
+ if (arg === '--isolated-user' || arg === '-u') {
185
+ options.user = true;
186
+ // Check if next arg is an optional username (not starting with -)
187
+ if (index + 1 < args.length && !args[index + 1].startsWith('-')) {
188
+ // Check if next arg looks like a username (not a command)
189
+ const nextArg = args[index + 1];
190
+ // If next arg matches username format, consume it
191
+ if (/^[a-zA-Z0-9_-]+$/.test(nextArg) && nextArg.length <= 32) {
192
+ options.userName = nextArg;
193
+ return 2;
194
+ }
195
+ }
196
+ return 1;
197
+ }
198
+
199
+ // --isolated-user=<value>
200
+ if (arg.startsWith('--isolated-user=')) {
201
+ options.user = true;
202
+ options.userName = arg.split('=')[1];
203
+ return 1;
204
+ }
205
+
206
+ // --keep-user - keep isolated user after command completes
207
+ if (arg === '--keep-user') {
208
+ options.keepUser = true;
209
+ return 1;
210
+ }
211
+
178
212
  // --keep-alive or -k
179
213
  if (arg === '--keep-alive' || arg === '-k') {
180
214
  options.keepAlive = true;
@@ -241,6 +275,34 @@ function validateOptions(options) {
241
275
  '--auto-remove-docker-container option is only valid with --isolated docker'
242
276
  );
243
277
  }
278
+
279
+ // User isolation validation
280
+ if (options.user) {
281
+ // User isolation is not supported with Docker (Docker has its own user mechanism)
282
+ if (options.isolated === 'docker') {
283
+ throw new Error(
284
+ '--isolated-user is not supported with Docker isolation. Docker uses its own user namespace for isolation.'
285
+ );
286
+ }
287
+ // Validate custom username if provided
288
+ if (options.userName) {
289
+ if (!/^[a-zA-Z0-9_-]+$/.test(options.userName)) {
290
+ throw new Error(
291
+ `Invalid username format for --isolated-user: "${options.userName}". Username should contain only letters, numbers, hyphens, and underscores.`
292
+ );
293
+ }
294
+ if (options.userName.length > 32) {
295
+ throw new Error(
296
+ `Username too long for --isolated-user: "${options.userName}". Maximum length is 32 characters.`
297
+ );
298
+ }
299
+ }
300
+ }
301
+
302
+ // Keep-user validation
303
+ if (options.keepUser && !options.user) {
304
+ throw new Error('--keep-user option is only valid with --isolated-user');
305
+ }
244
306
  }
245
307
 
246
308
  /**