Skip to content

Commit e62f645

Browse files
committed
Add unit tests
1 parent 764f104 commit e62f645

8 files changed

+489
-0
lines changed

tests/assert_test.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2021 Damien Nguyen
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import astroid
17+
import pytest
18+
19+
import pylint_secure_coding_standard as pylint_scs
20+
import pylint.testutils
21+
22+
23+
class TestSecureCodingStandardChecker(pylint.testutils.CheckerTestCase):
24+
CHECKER_CLASS = pylint_scs.SecureCodingStandardChecker
25+
26+
def test_assert_ok(self):
27+
call_node1, call_node2 = astroid.extract_node(
28+
"""
29+
int(0) #@
30+
foo() #@
31+
"""
32+
)
33+
34+
with self.assertNoMessages():
35+
self.checker.visit_call(call_node1)
36+
self.checker.visit_call(call_node2)
37+
38+
@pytest.mark.parametrize(
39+
's',
40+
(
41+
'assert len(s) > 0',
42+
'assert (my_set and my_list), "Some message"',
43+
),
44+
)
45+
def test_assert_not_ok(self, s):
46+
node = astroid.extract_node(s + ' #@')
47+
with self.assertAddsMessages(pylint.testutils.Message(msg_id='avoid-assert', node=node)):
48+
self.checker.visit_assert(node)

tests/eval_exec_test.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2021 Damien Nguyen
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import astroid
17+
import pytest
18+
19+
import pylint_secure_coding_standard as pylint_scs
20+
import pylint.testutils
21+
22+
23+
class TestSecureCodingStandardChecker(pylint.testutils.CheckerTestCase):
24+
CHECKER_CLASS = pylint_scs.SecureCodingStandardChecker
25+
26+
def test_eval_exec_ok(self):
27+
call_node1, call_node2 = astroid.extract_node(
28+
"""
29+
int(0) #@
30+
foo() #@
31+
sympy.sqrt(8).evalf()
32+
myvar.eval()
33+
"""
34+
)
35+
36+
with self.assertNoMessages():
37+
self.checker.visit_call(call_node1)
38+
self.checker.visit_call(call_node2)
39+
40+
@pytest.mark.parametrize(
41+
's',
42+
(
43+
'eval(input("Your input> "))',
44+
r'exec("a = 5\nb=10\nprint(\"Sum =\", a+b)")',
45+
),
46+
)
47+
def test_eval_exec_call(self, s):
48+
node = astroid.extract_node(s + ' #@')
49+
with self.assertAddsMessages(pylint.testutils.Message(msg_id='avoid-eval-exec', node=node)):
50+
self.checker.visit_call(node)

tests/jsonpickle_decode_test.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2021 Damien Nguyen
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import astroid
17+
import pytest
18+
19+
import pylint_secure_coding_standard as pylint_scs
20+
import pylint.testutils
21+
22+
23+
class TestSecureCodingStandardChecker(pylint.testutils.CheckerTestCase):
24+
CHECKER_CLASS = pylint_scs.SecureCodingStandardChecker
25+
26+
def test_yaml_ok(self):
27+
nodes = astroid.extract_node(
28+
"""
29+
int(0) #@
30+
foo() #@
31+
jsonpickle.encode(pvars) #@
32+
"""
33+
)
34+
35+
with self.assertNoMessages():
36+
for node in nodes:
37+
print(node)
38+
self.checker.visit_call(node)
39+
40+
@pytest.mark.parametrize(
41+
's',
42+
('jsonpickle.decode(payload)',),
43+
)
44+
def test_yaml_not_ok(self, s):
45+
node = astroid.extract_node(s + ' #@')
46+
with self.assertAddsMessages(pylint.testutils.Message(msg_id='avoid-jsonpickle-decode', node=node)):
47+
self.checker.visit_call(node)

tests/os_realpath_test.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2021 Damien Nguyen
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import astroid
17+
import pytest
18+
19+
import pylint_secure_coding_standard as pylint_scs
20+
import pylint.testutils
21+
22+
23+
class TestSecureCodingStandardChecker(pylint.testutils.CheckerTestCase):
24+
CHECKER_CLASS = pylint_scs.SecureCodingStandardChecker
25+
26+
def test_shell_true_ok(self):
27+
import_node1, call_node1, call_node2, call_node3 = astroid.extract_node(
28+
"""
29+
from os import realpath #@
30+
os.path.realpath(variable) #@
31+
os.path.realpath("/opt/file.txt") #@
32+
os.path.realpath("../file.txt") #@
33+
"""
34+
)
35+
36+
with self.assertNoMessages():
37+
self.checker.visit_importfrom(import_node1)
38+
self.checker.visit_call(call_node1)
39+
self.checker.visit_call(call_node2)
40+
self.checker.visit_call(call_node3)
41+
42+
@pytest.mark.parametrize(
43+
's',
44+
(
45+
'from os.path import abspath',
46+
'from os.path import relpath',
47+
'from os.path import join, relpath',
48+
),
49+
)
50+
def test_shell_true_importfrom(self, s):
51+
node = astroid.extract_node(s + ' #@')
52+
with self.assertAddsMessages(pylint.testutils.Message(msg_id='replace-os-relpath-abspath', node=node)):
53+
self.checker.visit_importfrom(node)
54+
55+
@pytest.mark.parametrize(
56+
's',
57+
(
58+
'os.path.abspath("/opt/file.txt")',
59+
'os.path.abspath("../file.txt")',
60+
'os.path.relpath("file.txt")',
61+
'os.path.relpath("file.txt", start="/")',
62+
'op.abspath("/opt/file.txt")',
63+
'op.abspath("../file.txt")',
64+
'op.relpath("file.txt")',
65+
'op.relpath("file.txt", start="/")',
66+
),
67+
)
68+
def test_shell_true_call(self, s):
69+
node = astroid.extract_node(s + ' #@')
70+
with self.assertAddsMessages(pylint.testutils.Message(msg_id='replace-os-relpath-abspath', node=node)):
71+
self.checker.visit_call(node)

tests/pdb_test.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2021 Damien Nguyen
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import astroid
17+
import pytest
18+
19+
import pylint_secure_coding_standard as pylint_scs
20+
import pylint.testutils
21+
22+
23+
class TestSecureCodingStandardChecker(pylint.testutils.CheckerTestCase):
24+
CHECKER_CLASS = pylint_scs.SecureCodingStandardChecker
25+
26+
def test_pdb_ok(self):
27+
call_node1, call_node2 = astroid.extract_node(
28+
"""
29+
int(0) #@
30+
foo() #@
31+
"""
32+
)
33+
34+
with self.assertNoMessages():
35+
self.checker.visit_call(call_node1)
36+
self.checker.visit_call(call_node2)
37+
38+
@pytest.mark.parametrize(
39+
's',
40+
(
41+
'import pdb',
42+
'import pdb, six',
43+
),
44+
)
45+
def test_pdb_import(self, s):
46+
node = astroid.extract_node(s + ' #@')
47+
with self.assertAddsMessages(pylint.testutils.Message(msg_id='avoid-debug-stmt', node=node)):
48+
self.checker.visit_import(node)
49+
50+
@pytest.mark.parametrize(
51+
's',
52+
('from pdb import set_trace',),
53+
)
54+
def test_pdb_importfrom(self, s):
55+
node = astroid.extract_node(s + ' #@')
56+
with self.assertAddsMessages(pylint.testutils.Message(msg_id='avoid-debug-stmt', node=node)):
57+
self.checker.visit_importfrom(node)
58+
59+
@pytest.mark.parametrize(
60+
's',
61+
(
62+
'pdb.set_trace()',
63+
'pdb.post_mortem(traceback=None)',
64+
'pdb.Pdb(skip=["django.*"])',
65+
'Pdb(skip=["django.*"])',
66+
),
67+
)
68+
def test_pdb_call(self, s):
69+
node = astroid.extract_node(s + ' #@')
70+
with self.assertAddsMessages(pylint.testutils.Message(msg_id='avoid-debug-stmt', node=node)):
71+
self.checker.visit_call(node)

tests/shell_exec_test.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2021 Damien Nguyen
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import astroid
17+
import pytest
18+
19+
import pylint_secure_coding_standard as pylint_scs
20+
import pylint.testutils
21+
22+
23+
class TestSecureCodingStandardChecker(pylint.testutils.CheckerTestCase):
24+
CHECKER_CLASS = pylint_scs.SecureCodingStandardChecker
25+
26+
def test_shell_true_ok(self):
27+
nodes = astroid.extract_node(
28+
"""
29+
subprocess.Popen(["cat", "/etc/passwd"], b, e, i, o, e, pre, c) #@
30+
subprocess.Popen(["cat", "/etc/passwd"], b, e, i, o, e, pre, c, False) #@
31+
subprocess.Popen(["cat", "/etc/passwd"], b, e, i, o, e, pre, c, False, cwd) #@
32+
subprocess.Popen(["cat", "/etc/passwd"], shell=False) #@
33+
sp.Popen(["cat", "/etc/passwd"], shell=False) #@
34+
subprocess.run(["cat", "/etc/passwd"], shell=False) #@
35+
sp.run(["cat", "/etc/passwd"], shell=False) #@
36+
subprocess.call(["cat", "/etc/passwd"], shell=False) #@
37+
sp.call(["cat", "/etc/passwd"], shell=False) #@
38+
subprocess.check_call(["cat", "/etc/passwd"], shell=False) #@
39+
sp.check_call(["cat", "/etc/passwd"], shell=False) #@
40+
subprocess.check_output(["cat", "/etc/passwd"], shell=False) #@
41+
sp.check_output(["cat", "/etc/passwd"], shell=False) #@
42+
"""
43+
)
44+
45+
with self.assertNoMessages():
46+
for node in nodes:
47+
self.checker.visit_call(node)
48+
49+
@pytest.mark.parametrize(
50+
's, msg_id',
51+
(
52+
('from os import system', 'avoid-os-system'),
53+
('from os import system as os_system', 'avoid-os-system'),
54+
),
55+
)
56+
def test_shell_true_importfrom(self, s, msg_id):
57+
node = astroid.extract_node(s + ' #@')
58+
with self.assertAddsMessages(pylint.testutils.Message(msg_id=msg_id, node=node)):
59+
self.checker.visit_importfrom(node)
60+
61+
@pytest.mark.parametrize(
62+
's, msg_id',
63+
(
64+
('os.system("ls -l")', 'avoid-os-system'),
65+
('subprocess.Popen(["cat", "/etc/passwd"], b, e, i, o, e, pre, c, True)', 'avoid-shell-true'),
66+
('subprocess.Popen(["cat", "/etc/passwd"], b, e, i, o, e, pre, c, True, cwd)', 'avoid-shell-true'),
67+
('subprocess.run(["cat", "/etc/passwd"], shell=True)', 'avoid-shell-true'),
68+
('sp.run(["cat", "/etc/passwd"], shell=True)', 'avoid-shell-true'),
69+
('subprocess.call(["cat", "/etc/passwd"], shell=True)', 'avoid-shell-true'),
70+
('sp.call(["cat", "/etc/passwd"], shell=True)', 'avoid-shell-true'),
71+
('subprocess.check_call(["cat", "/etc/passwd"], shell=True)', 'avoid-shell-true'),
72+
('sp.check_call(["cat", "/etc/passwd"], shell=True)', 'avoid-shell-true'),
73+
('subprocess.check_output(["cat", "/etc/passwd"], shell=True)', 'avoid-shell-true'),
74+
('sp.check_output(["cat", "/etc/passwd"], shell=True)', 'avoid-shell-true'),
75+
),
76+
)
77+
def test_shell_true_call(self, s, msg_id):
78+
node = astroid.extract_node(s + ' #@')
79+
with self.assertAddsMessages(pylint.testutils.Message(msg_id=msg_id, node=node)):
80+
self.checker.visit_call(node)

0 commit comments

Comments
 (0)