From 5b8493d8de43595cb3453ce15086f1ce76492da2 Mon Sep 17 00:00:00 2001 From: Kevin Adler Date: Thu, 20 Feb 2020 18:46:04 -0600 Subject: [PATCH 1/4] fix: Quote CL cmd in iCmd5250 for shell escaping iCmd5250 is just a fancy wrapper around iSh, using the PASE system command. It did not do any shell escaping/quoting, so it basically only allowed calling CL commands without parameters or with only positional parameters because the parentheses would cause the shell to gack. eg. iCmd5250('wrkactjob', 'WRKACTJOB SBS(QBATCH)') basically became iSh('wrkactjob', '/QOpenSys/usr/bin/system WRKACTJOB SBS(QBATCH)') When executed, an error would be given from the shell: sh: syntax error at line 1 : `(' unexpected The proper fix is to quote the string, but that can be tricky to get right. Luckily, Python has shlex.quote to do the hard work for us. https://docs.python.org/3/library/shlex.html#shlex.quote Fixes #49 --- src/itoolkit/itoolkit.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/itoolkit/itoolkit.py b/src/itoolkit/itoolkit.py index 522c681..cd2e092 100644 --- a/src/itoolkit/itoolkit.py +++ b/src/itoolkit/itoolkit.py @@ -113,6 +113,13 @@ class iXml(iBase): IBM i XMLSERVICE raw xml input import re import time +try: + from shlex import quote +except ImportError: + # python2 has shlex, but not shlex.quote + # Implement a crude equivalent. We don't care about Python 2 that much + def quote(s): + return '"{}"'.format(s) class iBase(object): # noqa N801 """ @@ -329,7 +336,7 @@ class iCmd5250(iSh): # noqa N801 """ def __init__(self, ikey, icmd, iopt={}): - cmd = "/QOpenSys/usr/bin/system " + icmd + cmd = "/QOpenSys/usr/bin/system " + quote(icmd) super(iCmd5250, self).__init__(ikey, cmd, iopt) From c8d52f1f0bfcb883207e9a50537e2ce17b12f094 Mon Sep 17 00:00:00 2001 From: Kevin Adler Date: Wed, 26 Feb 2020 15:46:01 -0600 Subject: [PATCH 2/4] Improve handling for Python 2 quoting --- src/itoolkit/itoolkit.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/itoolkit/itoolkit.py b/src/itoolkit/itoolkit.py index cd2e092..863cf72 100644 --- a/src/itoolkit/itoolkit.py +++ b/src/itoolkit/itoolkit.py @@ -119,7 +119,12 @@ class iXml(iBase): IBM i XMLSERVICE raw xml input # python2 has shlex, but not shlex.quote # Implement a crude equivalent. We don't care about Python 2 that much def quote(s): - return '"{}"'.format(s) + if ' ' not in s: + return s + + # remove first and last space to be less confusing + quote_replacement = """ '"'"' """[1:-1] + return "'" + s.replace("'", quote_replacement) + "'" class iBase(object): # noqa N801 """ From 4676272a239eeec8edddd06d652b0a3b1f816f4b Mon Sep 17 00:00:00 2001 From: Kevin Adler Date: Wed, 26 Feb 2020 15:46:26 -0600 Subject: [PATCH 3/4] test: Add quoting tests for iCmd5250 --- tests/test_unit_cmd5250.py | 47 ++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/tests/test_unit_cmd5250.py b/tests/test_unit_cmd5250.py index 6c8c0f0..e12dc71 100644 --- a/tests/test_unit_cmd5250.py +++ b/tests/test_unit_cmd5250.py @@ -3,10 +3,6 @@ from itoolkit import iCmd5250 -def to5250(cmd): - return "/QOpenSys/usr/bin/system {}".format(cmd) - - def test_5250(): cmd = 'WRKACTJOB' key = 'lljqezl' @@ -20,8 +16,7 @@ def test_5250(): assert('var' in element.attrib) assert(element.attrib['var'] == key) - assert(element.text == to5250(cmd)) - + assert(element.text == "/QOpenSys/usr/bin/system WRKACTJOB") def test_5250_error_on(): cmd = 'WRKACTJOB' @@ -37,7 +32,7 @@ def test_5250_error_on(): assert('var' in element.attrib) assert(element.attrib['var'] == key) - assert(element.text == to5250(cmd)) + assert(element.text == "/QOpenSys/usr/bin/system WRKACTJOB") def test_5250_error_off(): @@ -54,7 +49,7 @@ def test_5250_error_off(): assert('var' in element.attrib) assert(element.attrib['var'] == key) - assert(element.text == to5250(cmd)) + assert(element.text == "/QOpenSys/usr/bin/system WRKACTJOB") def test_5250_row_on(): @@ -71,7 +66,7 @@ def test_5250_row_on(): assert('var' in element.attrib) assert(element.attrib['var'] == key) - assert(element.text == to5250(cmd)) + assert(element.text == "/QOpenSys/usr/bin/system WRKACTJOB") def test_5250_row_off(): @@ -88,4 +83,36 @@ def test_5250_row_off(): assert('var' in element.attrib) assert(element.attrib['var'] == key) - assert(element.text == to5250(cmd)) + assert(element.text == "/QOpenSys/usr/bin/system WRKACTJOB") + +def test_5250_space(): + cmd = 'WRKACTJOB SBS(*QINTER)' + key = 'lknwqekrn' + + element = ET.fromstring(iCmd5250(key, cmd).xml_in()) + assert(element.tag == 'sh') + + assert('error' in element.attrib) + assert(element.attrib['error'] == 'fast') + + assert('var' in element.attrib) + assert(element.attrib['var'] == key) + + assert(element.text == "/QOpenSys/usr/bin/system 'WRKACTJOB SBS(*QINTER)'") + + +def test_5250_inner_string(): + cmd = "wrklnk '/test/file'" + key = 'znxvlkja' + + element = ET.fromstring(iCmd5250(key, cmd).xml_in()) + assert(element.tag == 'sh') + + assert('error' in element.attrib) + assert(element.attrib['error'] == 'fast') + + assert('var' in element.attrib) + assert(element.attrib['var'] == key) + + assert(element.text == """/QOpenSys/usr/bin/system 'wrklnk '"'"'/test/file'"'"''""") + From c6a5f8574546a84b7a6984a65b44f54b737b0e31 Mon Sep 17 00:00:00 2001 From: Kevin Adler Date: Thu, 27 Feb 2020 10:53:48 -0600 Subject: [PATCH 4/4] fixup! test: Add quoting tests for iCmd5250 --- tests/test_unit_cmd5250.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unit_cmd5250.py b/tests/test_unit_cmd5250.py index e12dc71..bd78399 100644 --- a/tests/test_unit_cmd5250.py +++ b/tests/test_unit_cmd5250.py @@ -18,6 +18,7 @@ def test_5250(): assert(element.text == "/QOpenSys/usr/bin/system WRKACTJOB") + def test_5250_error_on(): cmd = 'WRKACTJOB' key = 'rtoiu1nqew' @@ -115,4 +116,3 @@ def test_5250_inner_string(): assert(element.attrib['var'] == key) assert(element.text == """/QOpenSys/usr/bin/system 'wrklnk '"'"'/test/file'"'"''""") -