Skip to content

Commit 06d995e

Browse files
caohy1988claude
andcommitted
fix: Address Gemini Code Assist review — shell injection, shlex, check=True
1. Shell injection (HIGH): Use shlex.split() and pass args as separate list elements to subprocess.run instead of concatenating into bash -c command string. Args now arrive as positional params ($1, $2, ...). 2. Fragile arg parsing (MEDIUM): Replace .split() with shlex.split() for Python script sys.argv injection — correctly handles quoted arguments like --name "John Doe". 3. Status detection (MEDIUM): Add check=True to subprocess.run in shell wrapper so non-zero exit codes raise CalledProcessError, which the code executor captures as stderr. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d019e0b commit 06d995e

File tree

3 files changed

+21
-14
lines changed

3 files changed

+21
-14
lines changed

contributing/samples/skill_script_demo/test_skill_compat.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ async def test_execute_with_input_args(toolset_with_executor, mock_ctx):
241241
call_args = toolset_with_executor._code_executor.execute_code.call_args
242242
code_input = call_args[0][1]
243243
assert "sys.argv" in code_input.code
244+
assert "shlex.split" in code_input.code
244245
assert "-t stdio -c python eval.xml" in code_input.code
245246

246247

src/google/adk/tools/skill_toolset.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -392,25 +392,28 @@ def _prepare_code(
392392
# Python script: execute directly, inject sys.argv if args
393393
if input_args:
394394
return (
395-
"import sys\n"
396-
f"sys.argv = [{script_name!r}] + {input_args!r}.split()\n"
395+
"import sys, shlex\n"
396+
f"sys.argv = [{script_name!r}]"
397+
f" + shlex.split({input_args!r})\n"
397398
+ script_src
398399
)
399400
return script_src
400401
elif ext in ("sh", "bash"):
401-
# Shell script: wrap in subprocess.run
402+
# Shell script: wrap in subprocess.run.
403+
# Args are passed as separate list elements after the script
404+
# name to avoid shell injection — bash -c receives the script
405+
# source, and $0/$1/... get the positional parameters.
402406
return (
403-
"import subprocess\n"
407+
"import subprocess, shlex\n"
404408
"_result = subprocess.run(\n"
405-
f" ['bash', '-c', {script_src!r}"
406-
+ (f" + ' ' + {input_args!r}" if input_args else "")
407-
+ f"],\n"
408-
f" capture_output=True, text=True,\n"
409-
f")\n"
410-
f"print(_result.stdout, end='')\n"
411-
f"if _result.stderr:\n"
412-
f" import sys\n"
413-
f" print(_result.stderr, end='', file=sys.stderr)\n"
409+
f" ['bash', '-c', {script_src!r},"
410+
f" {script_name!r}]"
411+
+ (f" + shlex.split({input_args!r})" if input_args else "")
412+
+ ",\n"
413+
" capture_output=True, text=True,\n"
414+
" check=True,\n"
415+
")\n"
416+
"print(_result.stdout, end='')\n"
414417
)
415418
return None
416419

tests/unittests/tools/test_skill_toolset.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,11 +473,12 @@ async def test_execute_script_shell_success(mock_skill1):
473473
assert result["status"] == "success"
474474
assert result["stdout"] == "setup\n"
475475

476-
# Verify the code wraps in subprocess.run
476+
# Verify the code wraps in subprocess.run with check=True
477477
call_args = executor.execute_code.call_args
478478
code_input = call_args[0][1]
479479
assert "subprocess.run" in code_input.code
480480
assert "bash" in code_input.code
481+
assert "check=True" in code_input.code
481482

482483

483484
@pytest.mark.asyncio
@@ -499,6 +500,7 @@ async def test_execute_script_with_input_args_python(mock_skill1):
499500
call_args = executor.execute_code.call_args
500501
code_input = call_args[0][1]
501502
assert "sys.argv" in code_input.code
503+
assert "shlex.split" in code_input.code
502504
assert "--verbose --count 3" in code_input.code
503505

504506

@@ -520,6 +522,7 @@ async def test_execute_script_with_input_args_shell(mock_skill1):
520522

521523
call_args = executor.execute_code.call_args
522524
code_input = call_args[0][1]
525+
assert "shlex.split" in code_input.code
523526
assert "--force" in code_input.code
524527

525528

0 commit comments

Comments
 (0)