-
Notifications
You must be signed in to change notification settings - Fork 91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove subprocess with shell=True #24
Conversation
Signed-off-by: maipbui <maibui@microsoft.com>
This pull request introduces 1 alert when merging c1251e9 into bc8698d - view on LGTM.com new alerts:
|
Signed-off-by: maipbui <maibui@microsoft.com>
if raise_exception: | ||
raise | ||
|
||
def run_cmd_pipe(cmd0, cmd1, cmd2, log_err=True, raise_exception=False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which function are you referring to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You implemented twice run_commands_pipe
in this PR. I understand they are different a little bit. Could you try unify and reuse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unified duplicate code to one function.
scripts/hostcfgd
Outdated
cmd = "sed -e {0} {1} > {1}.new; mv -f {1} {1}.old; mv -f {1}.new {1}".format(' -e '.join(operations), filename) | ||
os.system(cmd) | ||
e_operations_str = ' -e '.join(operations) | ||
e_operations_list = split(e_operations_str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the list is joined by ' -e '
, not whitespace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got it. Better to implement the transformation on a list directly.
scripts/hostcfgd
Outdated
os.system(cmd) | ||
e_operations_str = ' -e '.join(operations) | ||
e_operations_list = split(e_operations_str) | ||
with open(filename+'new', 'w') as f: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scripts/hostcfgd
Outdated
@@ -971,9 +987,11 @@ class PasswHardening(object): | |||
|
|||
def modify_single_file_inplace(self, filename, operations=None): | |||
if operations: | |||
cmd = "sed -i {0} {1}".format(' -i '.join(operations), filename) | |||
i_operations_str = ' -i '.join(operations) | |||
i_operations_list = split(i_operations_str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scripts/procdockerstatsd
Outdated
cmd0 = ["ps", "-eo uid,pid,ppid,%mem,%cpu,stime,tty,time,cmd", "--sort", "-%cpu"] | ||
cmd1 = ["head", "-1024"] | ||
exitcode, data = getstatusoutput_noshell_pipe(cmd0, cmd1) | ||
if exitcode != [0, 0]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signed-off-by: maipbui <maibui@microsoft.com>
tests/caclmgrd/caclmgrd_bfd_test.py
Outdated
@@ -44,6 +44,8 @@ def test_caclmgrd_bfd(self, test_name, test_data, fs): | |||
call_rc = test_data["call_rc"] | |||
mocked_subprocess.call.return_value = call_rc | |||
|
|||
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ip = mock.MagicMock() | |||
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ipv6 = mock.MagicMock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you add mock? And why can original unit test work without mock?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without mock, it throws this error:
scripts/caclmgrd:202: in run_commands_pipe
exitcodes, stdout = getstatusoutput_noshell_pipe(cmd0, cmd1, cmd2, cmd3)
/usr/local/lib/python3.9/dist-packages/sonic_py_common/general.py:52: in getstatusoutput_noshell_pipe
popens = [Popen(cmd0, stdout=PIPE, universal_newlines=True)]
[OSError]: [Error 9] Bad file descriptor in the fake filesystem: '11'
I think it is due to
If close_fds is true, all file descriptors except 0, 1 and 2 will be closed before the child process is executed. Otherwise when close_fds is false, file descriptors obey their inheritable flag as described in Inheritance of File Descriptors.
in https://docs.python.org/3/library/subprocess.html#popen-constructor
Not sure how original UT works w/o mock
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have changed the behavior of this unit test, and we might have better solution.
https://github.com/sonic-net/sonic-host-services/blob/master/tests/caclmgrd/caclmgrd_bfd_test.py#L37
Above line doesn't work with your commit, and I think you should fix this mock and keep the original behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fixed the mock and revert to original behavior. Could you help review again this UT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other file is still using mock for get_namespace_mgmt_ip and get_namespace_mgmt_ipv6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. And thanks so much for your suggestion!
scripts/caclmgrd
Outdated
@@ -178,7 +182,7 @@ class ControlPlaneAclManager(daemon_base.DaemonBase): | |||
commands: List of strings, each string is a shell command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be List of List?
scripts/caclmgrd
Outdated
""" | ||
Given a list of shell commands, run them in order | ||
Args: | ||
commands: List of strings, each string is a shell command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still List of strings?
Signed-off-by: maipbui <maibui@microsoft.com>
subprocess.check_call(cmd) | ||
except Exception as err: | ||
if log_err: | ||
syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}" | ||
.format(err.cmd, err.returncode, err.output)) | ||
if raise_exception: | ||
raise | ||
|
||
def run_cmd_pipe(cmd0, cmd1, cmd2, log_err=True, raise_exception=False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use that code and not use the python 'sh' library?
It supports everything you need here: shell lexicography and pipe.
It is just much easier to use it.
Link to docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any updates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to have a more secure codebase and aim to fix potentially dangerous function calls that could introduce security vulnerabilities if used incorrectly. I fixed the functions based on Semgrep recommendations (a static analysis tool to find security issues, some refs: https://semgrep.dev/docs/cheat-sheets/python-command-injection/, https://github.com/sonic-net/sonic-buildimage/actions/runs/3412786426/jobs/5678731076). Not sure if we want to use third-party library and the 'sh' library docs does not mention anything about security.
tests/caclmgrd/caclmgrd_dhcp_test.py
Outdated
@@ -44,6 +44,8 @@ def test_caclmgrd_dhcp(self, test_name, test_data, fs): | |||
|
|||
mark = test_data["mark"] | |||
|
|||
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ip = mock.MagicMock() | |||
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ipv6 = mock.MagicMock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same issue
@@ -44,6 +44,8 @@ def test_feature_present(self, test_name, test_data, fs): | |||
call_rc = test_data["call_rc"] | |||
mocked_subprocess.call.return_value = call_rc | |||
|
|||
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ip = mock.MagicMock() | |||
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ipv6 = mock.MagicMock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same issue
Signed-off-by: maipbui <maibui@microsoft.com>
ipv6_address_cmd3 = ['head', '-1'] | ||
return self.run_commands_pipe(ipv6_address_cmd0, ipv6_address_cmd1, ipv6_address_cmd2, ipv6_address_cmd3) | ||
|
||
def log_output(self, cmd, exitcodes, stdout): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signed-off-by: maipbui <maibui@microsoft.com>
Signed-off-by: maipbui <maibui@microsoft.com>
scripts/procdockerstatsd
Outdated
@@ -135,7 +136,13 @@ class ProcDockerStats(daemon_base.DaemonBase): | |||
return True | |||
|
|||
def update_processstats_command(self): | |||
data = self.run_command("ps -eo uid,pid,ppid,%mem,%cpu,stime,tty,time,cmd --sort -%cpu | head -1024") | |||
cmd = "ps -eo uid,pid,ppid,%mem,%cpu,stime,tty,time,cmd --sort -%cpu | head -1024" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scripts/hostcfgd
Outdated
if ret == 0: | ||
syslog.syslog(syslog.LOG_INFO, "{} rule exists in {}".format(ip, chain)) | ||
else: | ||
# Modify command from Check to Append | ||
iptables_cmds.append(cmd.replace("check", "append")) | ||
iptables_cmds.append([word if word != "--check" else "--append" for word in cmd]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signed-off-by: maipbui <maibui@microsoft.com>
Signed-off-by: maipbui <maibui@microsoft.com>
@@ -22,3 +22,10 @@ def keys(self, db_id, pattern): | |||
|
|||
def get_all(self, db_id, key): | |||
return MockConnector.data[key] | |||
|
|||
def delete_all_by_pattern(self, db_id, pattern): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reverts commit 409ecc3.
…nic-net#33)" This reverts commit 28afb55.
@dgsudharsan included the #34, which is newer version, I think we don't need this PR. |
Signed-off-by: maipbui maibui@microsoft.com
Why I did it
subprocess
is used withshell=True
, which is very dangerous for shell injection.How I did it
remove
shell=True
, useshell=False
How to verify it
Pass UT
Manual test