Skip to content

Commit 2ffe6e3

Browse files
authored
[show][mlnx] replace shell=True, replace xml (#2700)
Signed-off-by: maipbui <[email protected]> #### What I did `subprocess()` - when using with `shell=True` is dangerous. Using subprocess function without a static string can lead to command injection. #### How I did it `subprocess()` - use `shell=False` instead, use list of strings Ref: [https://semgrep.dev/docs/cheat-sheets/python-command-injection/#mitigation](https://semgrep.dev/docs/cheat-sheets/python-command-injection/#mitigation) #### How to verify it Add UT Manual test ``` admin@***:~$ show platform mlnx issu ISSU is enabled admin@***:~$ show platform mlnx sniffer sdk sniffer is disabled ```
1 parent a5091bb commit 2ffe6e3

File tree

2 files changed

+74
-12
lines changed

2 files changed

+74
-12
lines changed

show/plugins/mlnx.py

+11-12
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
import sys
2727
import subprocess
2828
import click
29-
import xml.etree.ElementTree as ET
29+
from shlex import join
30+
from lxml import etree as ET
3031
from sonic_py_common import device_info
3132
except ImportError as e:
3233
raise ImportError("%s - required module not found" % str(e))
@@ -46,9 +47,9 @@ def run_command(command, display_cmd=False, ignore_error=False, print_to_console
4647
"""Run bash command and print output to stdout
4748
"""
4849
if display_cmd == True:
49-
click.echo(click.style("Running command: ", fg='cyan') + click.style(command, fg='green'))
50+
click.echo(click.style("Running command: ", fg='cyan') + click.style(join(command), fg='green'))
5051

51-
proc = subprocess.Popen(command, shell=True, text=True, stdout=subprocess.PIPE)
52+
proc = subprocess.Popen(command, text=True, stdout=subprocess.PIPE)
5253
(out, err) = proc.communicate()
5354

5455
if len(out) > 0 and print_to_console:
@@ -70,17 +71,17 @@ def mlnx():
7071
# get current status of sniffer from conf file
7172
def sniffer_status_get(env_variable_name):
7273
enabled = False
73-
command = "docker exec {} bash -c 'touch {}'".format(CONTAINER_NAME, SNIFFER_CONF_FILE)
74+
command = ["docker", "exec", CONTAINER_NAME, "bash", "-c", 'touch {}'.format(SNIFFER_CONF_FILE)]
7475
run_command(command)
75-
command = 'docker cp {} {}'.format(SNIFFER_CONF_FILE_IN_CONTAINER, TMP_SNIFFER_CONF_FILE)
76+
command = ['docker', 'cp', SNIFFER_CONF_FILE_IN_CONTAINER, TMP_SNIFFER_CONF_FILE]
7677
run_command(command)
7778
conf_file = open(TMP_SNIFFER_CONF_FILE, 'r')
7879
for env_variable_string in conf_file:
7980
if env_variable_string.find(env_variable_name) >= 0:
8081
enabled = True
8182
break
8283
conf_file.close()
83-
command = 'rm -rf {}'.format(TMP_SNIFFER_CONF_FILE)
84+
command = ['rm', '-rf', TMP_SNIFFER_CONF_FILE]
8485
run_command(command)
8586
return enabled
8687

@@ -97,10 +98,8 @@ def is_issu_status_enabled():
9798
# Get the SAI XML path from sai.profile
9899
sai_profile_path = '/{}/sai.profile'.format(HWSKU_PATH)
99100

100-
DOCKER_CAT_COMMAND = 'docker exec {container_name} cat {path}'
101-
102-
command = DOCKER_CAT_COMMAND.format(container_name=CONTAINER_NAME, path=sai_profile_path)
103-
sai_profile_content, _ = run_command(command, print_to_console=False)
101+
DOCKER_CAT_COMMAND = ['docker', 'exec', CONTAINER_NAME, 'cat', sai_profile_path]
102+
sai_profile_content, _ = run_command(DOCKER_CAT_COMMAND, print_to_console=False)
104103

105104
sai_profile_kvs = {}
106105

@@ -117,8 +116,8 @@ def is_issu_status_enabled():
117116
sys.exit(1)
118117

119118
# Get ISSU from SAI XML
120-
command = DOCKER_CAT_COMMAND.format(container_name=CONTAINER_NAME, path=sai_xml_path)
121-
sai_xml_content, _ = run_command(command, print_to_console=False)
119+
DOCKER_CAT_COMMAND = ['docker', 'exec', CONTAINER_NAME, 'cat', sai_xml_path]
120+
sai_xml_content, _ = run_command(DOCKER_CAT_COMMAND, print_to_console=False)
122121

123122
try:
124123
root = ET.fromstring(sai_xml_content)

tests/show_mlnx_test.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import sys
2+
import click
3+
import pytest
4+
import show.plugins.mlnx as show
5+
from unittest.mock import call, patch, mock_open, MagicMock
6+
7+
8+
class TestShowMlnx(object):
9+
def setup(self):
10+
print('SETUP')
11+
12+
@patch('click.style')
13+
def test_run_command(self, mock_click):
14+
cmd0 = ['echo', 'test']
15+
out, err = show.run_command(cmd0, display_cmd=True)
16+
17+
assert mock_click.call_args_list == [call('Running command: ', fg='cyan'), call(' '.join(cmd0), fg='green')]
18+
assert out == 'test\n'
19+
20+
cmd1 = [sys.executable, "-c", "import sys; sys.exit(6)"]
21+
with pytest.raises(SystemExit) as e:
22+
show.run_command(cmd1)
23+
assert e.value.code == 6
24+
25+
@patch('builtins.open', mock_open(read_data=show.ENV_VARIABLE_SX_SNIFFER))
26+
@patch('show.plugins.mlnx.run_command')
27+
def test_sniffer_status_get_enable(self, mock_runcmd):
28+
expected_calls = [
29+
call(["docker", "exec", show.CONTAINER_NAME, "bash", "-c", 'touch {}'.format(show.SNIFFER_CONF_FILE)]),
30+
call(['docker', 'cp', show.SNIFFER_CONF_FILE_IN_CONTAINER, show.TMP_SNIFFER_CONF_FILE]),
31+
call(['rm', '-rf', show.TMP_SNIFFER_CONF_FILE])
32+
]
33+
34+
output = show.sniffer_status_get(show.ENV_VARIABLE_SX_SNIFFER)
35+
assert mock_runcmd.call_args_list == expected_calls
36+
assert output
37+
38+
@patch('builtins.open', mock_open(read_data='not_enable'))
39+
@patch('show.plugins.mlnx.run_command')
40+
def test_sniffer_status_get_disable(self, mock_runcmd):
41+
expected_calls = [
42+
call(["docker", "exec", show.CONTAINER_NAME, "bash", "-c", 'touch {}'.format(show.SNIFFER_CONF_FILE)]),
43+
call(['docker', 'cp', show.SNIFFER_CONF_FILE_IN_CONTAINER, show.TMP_SNIFFER_CONF_FILE]),
44+
call(['rm', '-rf', show.TMP_SNIFFER_CONF_FILE])
45+
]
46+
47+
output = show.sniffer_status_get(show.ENV_VARIABLE_SX_SNIFFER)
48+
assert mock_runcmd.call_args_list == expected_calls
49+
assert not output
50+
51+
@patch('show.plugins.mlnx.run_command')
52+
def test_is_issu_status_enabled_systemexit(self, mock_runcmd):
53+
mock_runcmd.return_value = ('key0=value0\n', '')
54+
expected_calls = ['docker', 'exec', show.CONTAINER_NAME, 'cat', r'/{}/sai.profile'.format(show.HWSKU_PATH)]
55+
56+
with pytest.raises(SystemExit) as e:
57+
show.is_issu_status_enabled()
58+
assert e.value.code == 1
59+
mock_runcmd.assert_called_with(expected_calls, print_to_console=False)
60+
61+
def teardown(self):
62+
print('TEARDOWN')
63+

0 commit comments

Comments
 (0)