Skip to content
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

[sonic-config-engine] Replace os.system, replace yaml.load, remove subprocess with shell=True #12533

Merged
merged 5 commits into from
Oct 31, 2022

Conversation

maipbui
Copy link
Contributor

@maipbui maipbui commented Oct 27, 2022

Signed-off-by: maipbui maibui@microsoft.com

Why I did it

subprocess is used with shell=True, which is very dangerous for shell injection.
os - not secure against maliciously constructed input and dangerous if used to evaluate dynamic content
yaml.load can create arbitrary Python objects

How I did it

Replace os by subprocess, remove shell=True
Use yaml.safe_load()

How to verify it

Pass UT

Which release branch to backport (provide reason below if selected)

  • 201811
  • 201911
  • 202006
  • 202012
  • 202106
  • 202111
  • 202205

Description for the changelog

Ensure to add label/tag for the feature raised. example - PR#2174 under sonic-utilities repo. where, Generic Config and Update feature has been labelled as GCU.

Link to config_db schema for YANG module changes

A picture of a cute animal (not mandatory but encouraged)

Signed-off-by: maipbui <maibui@microsoft.com>
@maipbui maipbui requested a review from qiluo-msft October 27, 2022 20:09
@@ -47,7 +46,7 @@ def __init__(self, path=YANG_MODELS_DIR):
self.yang_parser = sonic_yang.SonicYang(path)
self.yang_parser.loadYangModel()
self.test_dir = os.path.dirname(os.path.realpath(__file__))
self.script_file = PYTHON_INTERPRETTER + ' ' + os.path.join(self.test_dir, '..', 'sonic-cfggen')
self.script_file = [PYTHON_INTERPRETTER] + [os.path.join(self.test_dir, '..', 'sonic-cfggen')]
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.script_file = [PYTHON_INTERPRETTER] + [os.path.join(self.test_dir, '..', 'sonic-cfggen')]
self.script_file = [PYTHON_INTERPRETTER, os.path.join(self.test_dir, '..', 'sonic-cfggen')]
``` #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All are fixed.

args, unknown = parser.parse_known_args(shlex.split(argument))
parser.add_argument("-o", "--output-file", help="Output file", nargs='?', const=None)
args, unknown = parser.parse_known_args(argument)
print(args)
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print

Remove debug code. #Closed

@@ -62,22 +61,29 @@ def validate(self, argument):
parser.add_argument("-p", "--port-config", help="port config file, used with -m or -k", nargs='?', const=None)
parser.add_argument("-S", "--hwsku-config", help="hwsku config file, used with -p and -m or -k", nargs='?', const=None)
parser.add_argument("-j", "--json", help="additional json file input, used with -p, -S and -m or -k", nargs='?', const=None)
args, unknown = parser.parse_known_args(shlex.split(argument))
parser.add_argument("-o", "--output-file", help="Output file", nargs='?', const=None)
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add_argument

Why change behavior? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because there're some commands that have output redirection.
Original command:

argument = '-m ' + self.t0_minigraph_nomgmt + ' -p ' + self.t0_port_config_tiny + ' -j ' + self.ztp + ' -j ' + self.port_data + ' -a \'{\"hwaddr\":\"e4:1d:2d:a5:f3:ad\"}\' -t ' + interfaces_template + '> ' + self.output_file

Changed command
argument = ['-m', self.t0_minigraph_nomgmt, '-p', self.t0_port_config_tiny, '-j', self.ztp, '-j', self.port_data, '-a', '{\"hwaddr\":\"e4:1d:2d:a5:f3:ad\"}', '-t', interfaces_template, '-o', self.output_file]

Implementation of new behavior:
https://github.com/sonic-net/sonic-buildimage/blob/ce3e0759543cd00a5a515b31ffb3e87de48c2e76/src/sonic-config-engine/tests/test_j2files.py#L46-#L59

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer following subprocess functions, adding stdout=None, stderr=None to this function parameter list.

@@ -16,7 +16,7 @@ class TestCfgGen(TestCase):
def setUp(self):
self.yang = utils.YangWrapper()
self.test_dir = os.path.dirname(os.path.realpath(__file__))
self.script_file = utils.PYTHON_INTERPRETTER + ' ' + os.path.join(self.test_dir, '..', 'sonic-cfggen')
self.script_file = [utils.PYTHON_INTERPRETTER] + [os.path.join(self.test_dir, '..', 'sonic-cfggen')]
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

] + [

The same. #Closed

@@ -52,13 +52,12 @@ def tearDown(self):
pass

def run_script(self, argument, check_stderr=False, verbose=False):
print('\n Running sonic-cfggen ' + argument)
print('\n Running sonic-cfggen ', argument)
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

,

Does semgrep alert on this line? #WontFix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but argument variable is a list, cannot be concatenated with string in the print statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to shlex.join

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice it is not available in python 3.7. Then + is good enough in test code.

@@ -21,17 +21,17 @@ class TestCfgGenPlatformJson(TestCase):

def setUp(self):
self.test_dir = os.path.dirname(os.path.realpath(__file__))
self.script_file = utils.PYTHON_INTERPRETTER + ' ' + os.path.join(self.test_dir, '..', 'sonic-cfggen')
self.script_file = [utils.PYTHON_INTERPRETTER] + [os.path.join(self.test_dir, '..', 'sonic-cfggen')]
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

] + [

The same. #Closed

@@ -10,18 +10,18 @@ class TestCfgGenT2ChassisFe(TestCase):

def setUp(self):
self.test_dir = os.path.dirname(os.path.realpath(__file__))
self.script_file = utils.PYTHON_INTERPRETTER + ' ' + os.path.join(self.test_dir, '..', 'sonic-cfggen')
self.script_file = [utils.PYTHON_INTERPRETTER] + [os.path.join(self.test_dir, '..', 'sonic-cfggen')]
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

] + [

The same #Closed

self.sample_graph_t2_chassis_fe = os.path.join(self.test_dir, 't2-chassis-fe-graph.xml')
self.sample_graph_t2_chassis_fe_vni = os.path.join(self.test_dir, 't2-chassis-fe-graph-vni.xml')
self.sample_graph_t2_chassis_fe_pc = os.path.join(self.test_dir, 't2-chassis-fe-graph-pc.xml')
self.t2_chassis_fe_port_config = os.path.join(self.test_dir, 't2-chassis-fe-port-config.ini')

def run_script(self, argument, check_stderr=False):
print('\n Running sonic-cfggen ' + argument)
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

semgrep alert on this line? #Closed

@@ -11,7 +11,7 @@
class TestJ2FilesT2ChassisFe(TestCase):
def setUp(self):
self.test_dir = os.path.dirname(os.path.realpath(__file__))
self.script_file = utils.PYTHON_INTERPRETTER + ' ' + os.path.join(self.test_dir, '..', 'sonic-cfggen')
self.script_file = [utils.PYTHON_INTERPRETTER] + [os.path.join(self.test_dir, '..', 'sonic-cfggen')]
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

] + [

The same. #Closed

@@ -25,23 +25,32 @@ def tearDown(self):
pass

def run_script(self, argument):
print('CMD: sonic-cfggen ' + argument)
output = subprocess.check_output(self.script_file + ' ' + argument, shell=True)
print('CMD: sonic-cfggen ', argument)
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

,

Semgrep alert on this line? #Closed

@@ -17,7 +17,7 @@ class TestCfgGenCaseInsensitive(TestCase):
def setUp(self):
self.yang = utils.YangWrapper()
self.test_dir = os.path.dirname(os.path.realpath(__file__))
self.script_file = utils.PYTHON_INTERPRETTER + ' ' + os.path.join(self.test_dir, '..', 'sonic-cfggen')
self.script_file = [utils.PYTHON_INTERPRETTER] + [os.path.join(self.test_dir, '..', 'sonic-cfggen')]
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

] + [

The same #Closed

@@ -27,13 +27,13 @@ def setUp(self):
self.port_config = os.path.join(self.test_dir, 't0-sample-port-config.ini')

def run_script(self, argument, check_stderr=False):
print('\n Running sonic-cfggen ' + argument)
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same. #Closed

@@ -23,7 +23,7 @@ def setUp(self):
self.yang = utils.YangWrapper()
self.test_dir = os.path.dirname(os.path.realpath(__file__))
self.test_data_dir = os.path.join(self.test_dir, 'multi_npu_data')
self.script_file = utils.PYTHON_INTERPRETTER + ' ' + os.path.join(self.test_dir, '..', 'sonic-cfggen')
self.script_file = [utils.PYTHON_INTERPRETTER] + [os.path.join(self.test_dir, '..', 'sonic-cfggen')]
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

] + [

The same #Closed

@@ -34,16 +34,24 @@ def setUp(self):
os.environ["CFGGEN_UNIT_TESTING"] = "2"

def run_script(self, argument, check_stderr=False):
print('\n Running sonic-cfggen ' + argument)
Copy link
Collaborator

@qiluo-msft qiluo-msft Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same #Closed

Signed-off-by: maipbui <maibui@microsoft.com>
Signed-off-by: maipbui <maibui@microsoft.com>
write_output = False
if '-o' in argument:
write_output = True
output_file = argument[-1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to guarantee '-o' is the last argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed to different solution.

os.system(echo_cmd)
subprocess.call(['sudo', 'mkdir', '/host'])
subprocess.call(['sudo', 'touch', '/host/machine.conf'])
getstatusoutput_noshell_pipe(echo_cmd1, echo_cmd2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain these commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check this PR #12065


return file_exist, dir_exist

def remove_machine_conf(self, file_exist, dir_exist):
if not file_exist:
os.system('sudo rm -f /host/machine.conf')
subprocess.call(['sudo', 'rm', '-f', '/host/machine.conf'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no user input in 'sudo rm -f /host/machine.conf', why is it a risk?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better for extra security when using subprocess and with array of strings. https://semgrep.dev/docs/cheat-sheets/python-command-injection/#1c-using-os-module-to-execute-commands

Signed-off-by: maipbui <maibui@microsoft.com>
Signed-off-by: maipbui <maibui@microsoft.com>
@maipbui maipbui marked this pull request as ready for review October 31, 2022 14:41
@maipbui maipbui changed the title [sonic-config-engine] Replace os.system and remove subprocess with shell=True [sonic-config-engine] Replace os.system, replace yaml.load, remove subprocess with shell=True Oct 31, 2022
@maipbui maipbui merged commit 934871c into sonic-net:master Oct 31, 2022
@maipbui maipbui deleted the bui/config-engine-pysec branch October 31, 2022 14:43
StormLiangMS added a commit that referenced this pull request Nov 7, 2022
…emove subprocess with shell=True (#12533)"

This reverts commit 934871c.
StormLiangMS added a commit that referenced this pull request Nov 7, 2022
…emove subprocess with shell=True (#12533)" (#12616)

This reverts commit 934871c. 

Unblocking sync from github to internal
maipbui added a commit to maipbui/sonic-buildimage that referenced this pull request Nov 7, 2022
….load, remove subprocess with shell=True (sonic-net#12533)" (sonic-net#12616)"

This reverts commit 661c467.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants