T09 · Insecure Skill Coding Practices
- Location
scripts/run_complete_qc.py:41- Finding
Shell Command Injection Through Unquoted Path Interpolation
- Content
View full analysis
Vulnerability Details
File Location:
scripts/run_complete_qc.py, lines 41–45, 119–126, 240–246, and 273–278
Vulnerability Type: OS command injection
Risk Level: MediumVulnerable Code
python def run_command(cmd, description): """运行命令并记录""" log(f"开始: {description}") log(f"命令: {cmd}") result = subprocess.run(cmd, shell=True, capture_output=True, text=True)python cmd = f""" cd-hit -i {input_file} \ -o {output_file} \ -c 0.90 \ -n 5 \ -M 0 \ -T 8 """ if not run_command(cmd, "CD-HIT 去冗余"): return Nonepython cmd = f""" mafft --localpair \ --maxiterate 1000 \ --thread 8 \ {input_file} 1> {output_file} 2> {log_file} """ if not run_command(cmd, "MAFFT 多序列比对"): return Nonepython cmd = f""" trimal -in {input_file} \ -out {output_file} \ -automated1 """ if not run_command(cmd, "trimAl 比对修剪"): return NoneTechnical Analysis
The pipeline constructs shell commands by directly interpolating
input_file,output_file, andlog_fileinto command strings. These strings are passed tosubprocess.runwithshell=True.Shell metacharacters contained in an interpolated path are consequently interpreted as shell syntax rather than as part of a filename. Relevant metacharacters include semicolons, command substitutions, pipes, redirection operators, and newline characters.
The bundled
main()currently obtains its initial input from a hardcoded path, which limits direct exploitation through the documented command line. However, the stage functions are ordinary module-level functions and can be imported and called with attacker-controlled path-like values. Future implementation of the documented input arguments would also make the flaw directly reachable unless command execution is corrected.Attack Path
- A ...[truncated 1228 chars]
- Remediation
View remediation
Remediation Suggestions
-
Remove
shell=Trueand pass every executable and argument as a separate list element:python def run_command(cmd, description, stdout=None, stderr=None): log(f"Starting: {description}") result = subprocess.run( cmd, shell=False, stdout=stdout, stderr=stderr, text=True, check=False, ) return result.returncode == 0 -
Construct CD-HIT and trimAl invocations as argument arrays:
python cmd = [ "cd-hit", "-i", str(input_file), "-o", str(output_file), "-c", "0.90", "-n", "5", "-M", "0", "-T", "8", ] -
Handle MAFFT output redirection through Python file handles rather than shell operators:
python cmd = [ "mafft", "--localpair", "--maxiterate", "1000", "--thread", "8", str(input_file), ] with output_file.open("w") as output_handle, log_file.open("w") as log_handle: result = subprocess.run( cmd, shell=False, stdout=output_handle, stderr=log_handle, text=True, check=False, ) -
Validate that input files exist and are regular files before execution.
-
Resolve and validate output paths against the selected output directory.
-
Avoid attempting to secure shell commands solely through manual quoting; argument arrays provide the appropriate command/argument boundary.
-
Run the pipeline under a dedicated, unprivileged account with access limited to the required input and output directories.
-
