fix(test): replace shell=True with shell=False in cli_coverage_steps.py subprocess call #734

Closed
opened 2026-03-12 13:19:31 +00:00 by CoreRasurae · 1 comment
Member

Metadata

  • Commit Message: fix(test): replace shell=True with shell=False in cli_coverage_steps.py subprocess call
  • Branch: fix/m3-shell-true-subprocess

Summary

features/steps/cli_coverage_steps.py:44 uses subprocess.run(command, ..., shell=True) where command is a string built from BDD step definitions. While the command strings originate from .feature files authored by the development team (not runtime user input), shell=True with string input is a well-known command injection vector and violates defense-in-depth principles.

Background and Context

Identified during the security review of PR #727 (Security Finding #3 by @hurui200320). This is a pre-existing issue not introduced by that PR. The reviewer noted that the same pattern has already been correctly implemented with shell=False in cli_plan_context_commands_steps.py:351, making this an inconsistency.

Current Behavior

# features/steps/cli_coverage_steps.py:44
subprocess.run(command, ..., shell=True)

The command is a string passed from BDD step patterns like @when('I run shell command "{command}"'). With shell=True, the string is passed to the system shell for parsing, which could interpret shell metacharacters.

Expected Behavior

import shlex
subprocess.run(shlex.split(command), ..., shell=False)

This is consistent with the pattern already used in cli_plan_context_commands_steps.py:351.

Acceptance Criteria

  • shell=True is replaced with shell=False in cli_coverage_steps.py
  • shlex.split() is used to properly tokenize the command string
  • All scenarios using the I run shell command step still pass
  • No other shell=True usages remain in step files (audit)

Supporting Information

  • Identified in: PR #727 Security Review, Finding #3
  • Review URL: #727
  • Reviewer: @hurui200320
  • Reference implementation: cli_plan_context_commands_steps.py:351 (already uses shell=False)

Subtasks

  • Replace shell=True with shell=False + shlex.split() in cli_coverage_steps.py:44
  • Audit all step files for other shell=True usages
  • Fix any additional shell=True instances found
  • Run scenarios that use I run shell command step to verify correctness
  • Run nox -s unit_tests to verify no regressions

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.
## Metadata - **Commit Message**: `fix(test): replace shell=True with shell=False in cli_coverage_steps.py subprocess call` - **Branch**: `fix/m3-shell-true-subprocess` ## Summary `features/steps/cli_coverage_steps.py:44` uses `subprocess.run(command, ..., shell=True)` where `command` is a string built from BDD step definitions. While the command strings originate from `.feature` files authored by the development team (not runtime user input), `shell=True` with string input is a well-known command injection vector and violates defense-in-depth principles. ## Background and Context Identified during the security review of PR #727 (Security Finding #3 by @hurui200320). This is a pre-existing issue not introduced by that PR. The reviewer noted that the same pattern has already been correctly implemented with `shell=False` in `cli_plan_context_commands_steps.py:351`, making this an inconsistency. ## Current Behavior ```python # features/steps/cli_coverage_steps.py:44 subprocess.run(command, ..., shell=True) ``` The `command` is a string passed from BDD step patterns like `@when('I run shell command "{command}"')`. With `shell=True`, the string is passed to the system shell for parsing, which could interpret shell metacharacters. ## Expected Behavior ```python import shlex subprocess.run(shlex.split(command), ..., shell=False) ``` This is consistent with the pattern already used in `cli_plan_context_commands_steps.py:351`. ## Acceptance Criteria - [x] `shell=True` is replaced with `shell=False` in `cli_coverage_steps.py` - [x] `shlex.split()` is used to properly tokenize the command string - [x] All scenarios using the `I run shell command` step still pass - [x] No other `shell=True` usages remain in step files (audit) ## Supporting Information - Identified in: PR #727 Security Review, Finding #3 - Review URL: https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/727 - Reviewer: @hurui200320 - Reference implementation: `cli_plan_context_commands_steps.py:351` (already uses `shell=False`) ## Subtasks - [x] Replace `shell=True` with `shell=False` + `shlex.split()` in `cli_coverage_steps.py:44` - [x] Audit all step files for other `shell=True` usages - [x] Fix any additional `shell=True` instances found - [x] Run scenarios that use `I run shell command` step to verify correctness - [x] Run `nox -s unit_tests` to verify no regressions ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done.
freemo added this to the v3.2.0 milestone 2026-03-12 20:22:20 +00:00
Author
Member

Implementation Notes

Changes Made (commit 6077e921)

Replaced shell=True with shell=False + shlex.split() in features/steps/cli_coverage_steps.py line 44, consistent with the pattern already established in cli_plan_context_commands_steps.py:351.

Design Decisions

  1. shlex.split() handles quoted paths: The command strings constructed in the step function use f'"{sys.executable}" -m cleveragents.cli.main' which includes quoted paths. shlex.split() correctly tokenizes these into a proper argument list.
  2. No explicit shell=False: Since False is the default for subprocess.run's shell parameter, it's omitted for cleanliness.

Audit Results

  • features/steps/: Only cli_coverage_steps.py:44 had shell=True — now fixed
  • robot/: Found Robot Framework Run Process keyword using shell=True in several .robot files — these are different from Python subprocess and do not have the same injection risk
  • No other Python shell=True usages found in step files or helper scripts

Quality Gates

Session Result
lint PASS
typecheck PASS
unit_tests PASS (11,455 scenarios)
integration_tests PASS (1,600 tests)
coverage_report PASS (97.0%)

PR

PR #1071Closes #734

## Implementation Notes ### Changes Made (commit `6077e921`) Replaced `shell=True` with `shell=False` + `shlex.split()` in `features/steps/cli_coverage_steps.py` line 44, consistent with the pattern already established in `cli_plan_context_commands_steps.py:351`. ### Design Decisions 1. **`shlex.split()` handles quoted paths**: The command strings constructed in the step function use `f'"{sys.executable}" -m cleveragents.cli.main'` which includes quoted paths. `shlex.split()` correctly tokenizes these into a proper argument list. 2. **No explicit `shell=False`**: Since `False` is the default for `subprocess.run`'s `shell` parameter, it's omitted for cleanliness. ### Audit Results - `features/steps/`: Only `cli_coverage_steps.py:44` had `shell=True` — now fixed - `robot/`: Found Robot Framework `Run Process` keyword using `shell=True` in several `.robot` files — these are different from Python `subprocess` and do not have the same injection risk - No other Python `shell=True` usages found in step files or helper scripts ### Quality Gates | Session | Result | |---|---| | lint | PASS | | typecheck | PASS | | unit_tests | PASS (11,455 scenarios) | | integration_tests | PASS (1,600 tests) | | coverage_report | PASS (97.0%) | ### PR PR #1071 — `Closes #734`
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#734
No description provided.