fix: replace create_subprocess_shell #3265
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens the Vue dev-server startup in server.py by avoiding shell invocation, and adds a regression test to ensure the safer subprocess API is used.
Changes:
- Replace
asyncio.create_subprocess_shell("npm run dev", ...)withasyncio.create_subprocess_exec("npm", "run", "dev", ...)instart_vue_dev_server. - Add a security-focused test to assert
create_subprocess_shellis not used for starting the Vue dev server.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
server.py |
Switches Vue dev-server startup from shell-based to exec-based subprocess invocation. |
tests/security/test_subprocess_exec.py |
Adds a regression test intended to prevent reintroducing create_subprocess_shell in start_vue_dev_server. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
This PR hardens the Vue dev-server startup path by removing shell-based subprocess execution and adds a regression test to ensure the safer subprocess API remains in use.
Changes:
- Replace
asyncio.create_subprocess_shell("npm run dev", ...)withasyncio.create_subprocess_exec("npm", "run", "dev", ...)when starting the Vue dev server. - Improve logging to include the spawned process PID and schedule a background
wait()to avoid leaving the child process unreaped. - Add a security-focused test that parses
server.pyand assertsstart_vue_dev_serverdoes not usecreate_subprocess_shell.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
server.py |
Switches Vue dev-server startup to create_subprocess_exec, improving security by avoiding shell invocation. |
tests/security/test_subprocess_exec.py |
Adds an AST-based regression test guarding against reintroducing shell-based subprocess startup. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
fabf4b9 to
647c11a
Compare
There was a problem hiding this comment.
Pull request overview
Updates the Vue dev-server startup to avoid invoking a shell (reducing command-injection risk) and adds a regression test to ensure shell-based subprocess creation isn’t reintroduced.
Changes:
- Replace
asyncio.create_subprocess_shell("npm run dev", ...)withasyncio.create_subprocess_exec("npm", "run", "dev", ...)instart_vue_dev_server. - Log the spawned dev-server PID and schedule
proc.wait()in the background. - Add an AST-based security test asserting
start_vue_dev_serverdoes not containcreate_subprocess_shell.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
server.py |
Switches Vue dev-server startup to create_subprocess_exec and logs PID. |
tests/security/test_subprocess_exec.py |
Adds regression test to prevent reintroducing create_subprocess_shell in start_vue_dev_server. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func_content = ast.get_source_segment(content, func_node) | ||
| assert 'create_subprocess_shell' not in func_content | ||
| assert 'create_subprocess_exec' in func_content |
…server Avoid shell injection risk by using create_subprocess_exec instead.
- Capture proc from create_subprocess_exec, log PID, schedule proc.wait() to avoid zombie processes on Vue dev server exit - Rewrite test to use pytest style, ast-based function extraction, and Path-relative server.py resolution Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ast.get_source_segment() returns None when source offsets are unavailable; assert it is not None before using it in string checks.
647c11a to
5daf105
Compare
There was a problem hiding this comment.
Pull request overview
Updates the Caldera startup path to launch the Vue dev server without using a shell, and adds a regression test to prevent reintroducing shell-based subprocess execution.
Changes:
- Replace
asyncio.create_subprocess_shell("npm run dev", ...)withasyncio.create_subprocess_exec("npm", "run", "dev", ...)instart_vue_dev_server. - Add a security-focused test that asserts
start_vue_dev_serverdoes not usecreate_subprocess_shell.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
server.py |
Switches Vue dev server startup from shell-based to exec-based subprocess creation; logs PID and schedules a background wait. |
tests/security/test_subprocess_exec.py |
Adds an AST-based test to guard against reintroducing create_subprocess_shell in start_vue_dev_server. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| def test_no_create_subprocess_shell_in_start_vue(): | ||
| """Verify create_subprocess_shell is not used in start_vue_dev_server.""" | ||
| content = SERVER_PY.read_text() |
| func_content = ast.get_source_segment(content, func_node) | ||
| assert func_content is not None, "Could not extract source for start_vue_dev_server" | ||
| assert 'create_subprocess_shell' not in func_content | ||
| assert 'create_subprocess_exec' in func_content |
server.py
Outdated
| ) | ||
| logging.info("VueJS development server is live.") | ||
| logging.info("Started VueJS development server with PID %s.", proc.pid) | ||
| asyncio.create_task(proc.wait()) |
There was a problem hiding this comment.
Pull request overview
This PR hardens how the Vue dev server is launched by removing shell-based subprocess invocation and adds a regression test to prevent reintroducing shell execution in start_vue_dev_server.
Changes:
- Replace
asyncio.create_subprocess_shell("npm run dev", ...)withasyncio.create_subprocess_exec("npm", "run", "dev", ...)inserver.py. - Add an AST-based security test to ensure
start_vue_dev_serverdoes not containcreate_subprocess_shelland does containcreate_subprocess_exec.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/security/test_subprocess_exec.py | Adds a regression test intended to prevent shell-based subprocess usage in start_vue_dev_server. |
| server.py | Switches Vue dev server startup from shell invocation to exec-style invocation and logs PID. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func_content = ast.get_source_segment(content, func_node) | ||
| assert func_content is not None, "Could not extract source for start_vue_dev_server" | ||
| assert 'create_subprocess_shell' not in func_content | ||
| assert 'create_subprocess_exec' in func_content |
| tree = ast.parse(content, filename=str(SERVER_PY)) | ||
| func_node = None | ||
| for node in ast.walk(tree): | ||
| if isinstance(node, ast.AsyncFunctionDef) and node.name == 'start_vue_dev_server': |
There was a problem hiding this comment.
Pull request overview
Updates how the Caldera server starts the Vue dev server to avoid shell invocation, and adds a regression test to enforce the safer subprocess API usage.
Changes:
- Replace
asyncio.create_subprocess_shell("npm run dev", ...)withasyncio.create_subprocess_exec("npm", "run", "dev", ...)instart_vue_dev_server. - Improve the startup log message to include the spawned process PID.
- Add a security regression test that inspects
server.pyto ensurecreate_subprocess_shellis not used for starting the Vue dev server.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
server.py |
Switches Vue dev server startup to create_subprocess_exec and logs PID. |
tests/security/test_subprocess_exec.py |
Adds a test to prevent reintroducing create_subprocess_shell in start_vue_dev_server. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| proc = await asyncio.create_subprocess_exec( | ||
| "npm", "run", "dev", stdout=sys.stdout, stderr=sys.stderr, cwd=MAGMA_PATH | ||
| ) | ||
| logging.info("VueJS development server is live.") | ||
| logging.info("VueJS development server started (PID %s).", proc.pid) |
| assert 'create_subprocess_shell' not in func_content | ||
| assert 'create_subprocess_exec' in func_content |
|
|
||
| def test_no_create_subprocess_shell_in_start_vue(): | ||
| """Verify create_subprocess_shell is not used in start_vue_dev_server.""" | ||
| content = SERVER_PY.read_text() |
|
There was a problem hiding this comment.
Pull request overview
Updates the Vue dev-server startup path to avoid shell-based subprocess execution and adds a security regression test to prevent reintroducing create_subprocess_shell in start_vue_dev_server.
Changes:
- Replace
asyncio.create_subprocess_shell("npm run dev", ...)withasyncio.create_subprocess_exec("npm", "run", "dev", ...)inserver.py. - Add a security test that inspects
server.pyto ensurestart_vue_dev_serverdoes not usecreate_subprocess_shell.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/security/test_subprocess_exec.py | Adds an AST-based regression test intended to enforce non-shell subprocess usage in start_vue_dev_server. |
| server.py | Switches Vue dev server startup from shell invocation to exec-style subprocess creation and logs the spawned PID. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func_content = ast.get_source_segment(content, func_node) | ||
| assert func_content is not None, "Could not extract source for start_vue_dev_server" | ||
| assert 'create_subprocess_shell' not in func_content | ||
| assert 'create_subprocess_exec' in func_content |
* fix: replace create_subprocess_shell with safe exec in start_vue_dev_server Avoid shell injection risk by using create_subprocess_exec instead. * fix: address Copilot review feedback on subprocess PR - Capture proc from create_subprocess_exec, log PID, schedule proc.wait() to avoid zombie processes on Vue dev server exit - Rewrite test to use pytest style, ast-based function extraction, and Path-relative server.py resolution Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: guard against None return from ast.get_source_segment ast.get_source_segment() returns None when source offsets are unavailable; assert it is not None before using it in string checks. * fix: remove untracked create_task in start_vue_dev_server to avoid zombie subprocess * test: accept FunctionDef and AsyncFunctionDef in start_vue_dev_server check * test: use explicit utf-8 encoding in read_text() --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>



Avoid shell injection risk by using create_subprocess_exec instead.
Description
start_vue_dev_server() in server.py previously launched the Magma Vue development server using asyncio.create_subprocess_shell, which passes the command string to /bin/sh -c. This introduces a shell injection surface: if MAGMA_PATH or any surrounding logic were ever influenced by user-controlled input, shell metacharacters could be interpreted by the shell.
Replaced with asyncio.create_subprocess_exec, which bypasses the shell entirely and invokes npm directly with its arguments as a proper argv list. This is the safer default for any subprocess call where shell features (pipes, redirects, glob expansion) are not required — and they are not required here.
No functional change to runtime behavior.
Type of change
Bug fix (non-breaking change which fixes an issue)
How Has This Been Tested?
tests/security/test_subprocess_exec.py — static analysis test that parses server.py, locates the start_vue_dev_server function body, and asserts:
create_subprocess_shell is not present
create_subprocess_exec is present
Verified the development server still launches correctly via python3 server.py --insecure
pytest tests/security/test_subprocess_exec.py -v
1 passed
Checklist
My code follows the style guidelines of this project
I have performed a self-review of my own code
I have made corresponding changes to the documentation
I have added tests that prove my fix is effective or that my feature works