From 4742481070e07c436295ed16207d9f9b8290ad69 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 22 Jan 2024 13:50:33 +0000 Subject: [PATCH 1/5] Python: Consolidate "mutable default" tests Moves the existing tests into the `ModificationOfParameterWithDefault` subdirectory which already contained a bunch more tests. In the process, I also removed some duplicated test cases. --- ...odificationOfParameterWithDefault.expected | 32 +++++++++ .../test.py | 56 +++++++++++++++ ...odificationOfParameterWithDefault.expected | 44 ------------ .../ModificationOfParameterWithDefault.qlref | 1 - .../Functions/general/functions_test.py | 68 ++----------------- 5 files changed, 93 insertions(+), 108 deletions(-) delete mode 100644 python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.expected delete mode 100644 python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.qlref diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected index 527a30c9b3f5..b33afdac80db 100644 --- a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected @@ -28,6 +28,18 @@ edges | test.py:131:23:131:23 | ControlFlowNode for l | test.py:135:9:135:9 | ControlFlowNode for l | | test.py:138:15:138:15 | ControlFlowNode for l | test.py:140:9:140:9 | ControlFlowNode for l | | test.py:145:23:145:23 | ControlFlowNode for l | test.py:147:9:147:9 | ControlFlowNode for l | +| test.py:153:25:153:25 | ControlFlowNode for x | test.py:154:5:154:5 | ControlFlowNode for x | +| test.py:156:21:156:21 | ControlFlowNode for x | test.py:157:5:157:5 | ControlFlowNode for x | +| test.py:159:27:159:27 | ControlFlowNode for y | test.py:160:25:160:25 | ControlFlowNode for y | +| test.py:159:27:159:27 | ControlFlowNode for y | test.py:161:21:161:21 | ControlFlowNode for y | +| test.py:160:25:160:25 | ControlFlowNode for y | test.py:153:25:153:25 | ControlFlowNode for x | +| test.py:161:21:161:21 | ControlFlowNode for y | test.py:156:21:156:21 | ControlFlowNode for x | +| test.py:181:28:181:28 | ControlFlowNode for x | test.py:185:9:185:9 | ControlFlowNode for x | +| test.py:181:28:181:28 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | +| test.py:194:18:194:18 | ControlFlowNode for x | test.py:195:28:195:28 | ControlFlowNode for x | +| test.py:195:28:195:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x | +| test.py:197:18:197:18 | ControlFlowNode for x | test.py:198:28:198:28 | ControlFlowNode for x | +| test.py:198:28:198:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x | nodes | test.py:2:12:2:12 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | | test.py:3:5:3:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | @@ -81,6 +93,20 @@ nodes | test.py:140:9:140:9 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | | test.py:145:23:145:23 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | | test.py:147:9:147:9 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | +| test.py:153:25:153:25 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:154:5:154:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:156:21:156:21 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:157:5:157:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:159:27:159:27 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:160:25:160:25 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:161:21:161:21 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:181:28:181:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:185:9:185:9 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:187:9:187:9 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:194:18:194:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:195:28:195:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:197:18:197:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:198:28:198:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | subpaths #select | test.py:3:5:3:5 | ControlFlowNode for l | test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l | This expression mutates a $@. | test.py:2:12:2:12 | ControlFlowNode for l | default value | @@ -106,3 +132,9 @@ subpaths | test.py:135:9:135:9 | ControlFlowNode for l | test.py:131:23:131:23 | ControlFlowNode for l | test.py:135:9:135:9 | ControlFlowNode for l | This expression mutates a $@. | test.py:131:23:131:23 | ControlFlowNode for l | default value | | test.py:140:9:140:9 | ControlFlowNode for l | test.py:138:15:138:15 | ControlFlowNode for l | test.py:140:9:140:9 | ControlFlowNode for l | This expression mutates a $@. | test.py:138:15:138:15 | ControlFlowNode for l | default value | | test.py:147:9:147:9 | ControlFlowNode for l | test.py:145:23:145:23 | ControlFlowNode for l | test.py:147:9:147:9 | ControlFlowNode for l | This expression mutates a $@. | test.py:145:23:145:23 | ControlFlowNode for l | default value | +| test.py:154:5:154:5 | ControlFlowNode for x | test.py:159:27:159:27 | ControlFlowNode for y | test.py:154:5:154:5 | ControlFlowNode for x | This expression mutates a $@. | test.py:159:27:159:27 | ControlFlowNode for y | default value | +| test.py:157:5:157:5 | ControlFlowNode for x | test.py:159:27:159:27 | ControlFlowNode for y | test.py:157:5:157:5 | ControlFlowNode for x | This expression mutates a $@. | test.py:159:27:159:27 | ControlFlowNode for y | default value | +| test.py:185:9:185:9 | ControlFlowNode for x | test.py:194:18:194:18 | ControlFlowNode for x | test.py:185:9:185:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:194:18:194:18 | ControlFlowNode for x | default value | +| test.py:185:9:185:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:185:9:185:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value | +| test.py:187:9:187:9 | ControlFlowNode for x | test.py:194:18:194:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:194:18:194:18 | ControlFlowNode for x | default value | +| test.py:187:9:187:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value | diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py index f1377f36e56e..2791acf235c0 100644 --- a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py @@ -148,3 +148,59 @@ def sanitizer_negated(l = [1]): else: l.append(1) return l + +# indirect modification of parameter with default +def aug_assign_argument(x): + x += ['x'] #$ modification=x + +def mutate_argument(x): + x.append('x') #$ modification=x + +def indirect_modification(y = []): + aug_assign_argument(y) + mutate_argument(y) + +def guarded_modification(z=[]): + if z: + z.append(0) + return z + +# This function causes a discrepancy between the +# Python 2 and 3 versions of the analysis. +# We comment it out until we have resoved the issue. +# +# def issue1143(expr, param=[]): +# if not param: +# return result +# for i in param: +# param.remove(i) # Mutation here + + +# Type guarding of modification of parameter with default: + +def do_stuff_based_on_type(x): + if isinstance(x, str): + x = x.split() + elif isinstance(x, dict): + x.setdefault('foo', 'bar') #$ modification=x + elif isinstance(x, list): + x.append(5) #$ modification=x + elif isinstance(x, tuple): + x = x.unknown_method() + +def str_default(x="hello world"): + do_stuff_based_on_type(x) + +def dict_default(x={'baz':'quux'}): + do_stuff_based_on_type(x) + +def list_default(x=[1,2,3,4]): + do_stuff_based_on_type(x) + +def tuple_default(x=(1,2)): + do_stuff_based_on_type(x) + +# Modification of parameter with default (safe method) + +def safe_method(x=[]): + return x.count(42) diff --git a/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.expected b/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.expected deleted file mode 100644 index 02111ef0e5e6..000000000000 --- a/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.expected +++ /dev/null @@ -1,44 +0,0 @@ -edges -| functions_test.py:39:9:39:9 | ControlFlowNode for x | functions_test.py:40:5:40:5 | ControlFlowNode for x | -| functions_test.py:133:15:133:15 | ControlFlowNode for x | functions_test.py:134:5:134:5 | ControlFlowNode for x | -| functions_test.py:151:25:151:25 | ControlFlowNode for x | functions_test.py:152:5:152:5 | ControlFlowNode for x | -| functions_test.py:154:21:154:21 | ControlFlowNode for x | functions_test.py:155:5:155:5 | ControlFlowNode for x | -| functions_test.py:157:27:157:27 | ControlFlowNode for y | functions_test.py:158:25:158:25 | ControlFlowNode for y | -| functions_test.py:157:27:157:27 | ControlFlowNode for y | functions_test.py:159:21:159:21 | ControlFlowNode for y | -| functions_test.py:158:25:158:25 | ControlFlowNode for y | functions_test.py:151:25:151:25 | ControlFlowNode for x | -| functions_test.py:159:21:159:21 | ControlFlowNode for y | functions_test.py:154:21:154:21 | ControlFlowNode for x | -| functions_test.py:179:28:179:28 | ControlFlowNode for x | functions_test.py:183:9:183:9 | ControlFlowNode for x | -| functions_test.py:179:28:179:28 | ControlFlowNode for x | functions_test.py:185:9:185:9 | ControlFlowNode for x | -| functions_test.py:192:18:192:18 | ControlFlowNode for x | functions_test.py:193:28:193:28 | ControlFlowNode for x | -| functions_test.py:193:28:193:28 | ControlFlowNode for x | functions_test.py:179:28:179:28 | ControlFlowNode for x | -| functions_test.py:195:18:195:18 | ControlFlowNode for x | functions_test.py:196:28:196:28 | ControlFlowNode for x | -| functions_test.py:196:28:196:28 | ControlFlowNode for x | functions_test.py:179:28:179:28 | ControlFlowNode for x | -nodes -| functions_test.py:39:9:39:9 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:40:5:40:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:133:15:133:15 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:134:5:134:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:151:25:151:25 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:152:5:152:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:154:21:154:21 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:155:5:155:5 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:157:27:157:27 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| functions_test.py:158:25:158:25 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| functions_test.py:159:21:159:21 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| functions_test.py:179:28:179:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:183:9:183:9 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:185:9:185:9 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:192:18:192:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:193:28:193:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:195:18:195:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| functions_test.py:196:28:196:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -subpaths -#select -| functions_test.py:40:5:40:5 | ControlFlowNode for x | functions_test.py:39:9:39:9 | ControlFlowNode for x | functions_test.py:40:5:40:5 | ControlFlowNode for x | This expression mutates a $@. | functions_test.py:39:9:39:9 | ControlFlowNode for x | default value | -| functions_test.py:134:5:134:5 | ControlFlowNode for x | functions_test.py:133:15:133:15 | ControlFlowNode for x | functions_test.py:134:5:134:5 | ControlFlowNode for x | This expression mutates a $@. | functions_test.py:133:15:133:15 | ControlFlowNode for x | default value | -| functions_test.py:152:5:152:5 | ControlFlowNode for x | functions_test.py:157:27:157:27 | ControlFlowNode for y | functions_test.py:152:5:152:5 | ControlFlowNode for x | This expression mutates a $@. | functions_test.py:157:27:157:27 | ControlFlowNode for y | default value | -| functions_test.py:155:5:155:5 | ControlFlowNode for x | functions_test.py:157:27:157:27 | ControlFlowNode for y | functions_test.py:155:5:155:5 | ControlFlowNode for x | This expression mutates a $@. | functions_test.py:157:27:157:27 | ControlFlowNode for y | default value | -| functions_test.py:183:9:183:9 | ControlFlowNode for x | functions_test.py:192:18:192:18 | ControlFlowNode for x | functions_test.py:183:9:183:9 | ControlFlowNode for x | This expression mutates a $@. | functions_test.py:192:18:192:18 | ControlFlowNode for x | default value | -| functions_test.py:183:9:183:9 | ControlFlowNode for x | functions_test.py:195:18:195:18 | ControlFlowNode for x | functions_test.py:183:9:183:9 | ControlFlowNode for x | This expression mutates a $@. | functions_test.py:195:18:195:18 | ControlFlowNode for x | default value | -| functions_test.py:185:9:185:9 | ControlFlowNode for x | functions_test.py:192:18:192:18 | ControlFlowNode for x | functions_test.py:185:9:185:9 | ControlFlowNode for x | This expression mutates a $@. | functions_test.py:192:18:192:18 | ControlFlowNode for x | default value | -| functions_test.py:185:9:185:9 | ControlFlowNode for x | functions_test.py:195:18:195:18 | ControlFlowNode for x | functions_test.py:185:9:185:9 | ControlFlowNode for x | This expression mutates a $@. | functions_test.py:195:18:195:18 | ControlFlowNode for x | default value | diff --git a/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.qlref b/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.qlref deleted file mode 100644 index 8c4044e8feeb..000000000000 --- a/python/ql/test/query-tests/Functions/general/ModificationOfParameterWithDefault.qlref +++ /dev/null @@ -1 +0,0 @@ -Functions/ModificationOfParameterWithDefault.ql diff --git a/python/ql/test/query-tests/Functions/general/functions_test.py b/python/ql/test/query-tests/Functions/general/functions_test.py index c47794fef28f..23419d738b17 100644 --- a/python/ql/test/query-tests/Functions/general/functions_test.py +++ b/python/ql/test/query-tests/Functions/general/functions_test.py @@ -36,8 +36,8 @@ def ok3(x): def ok4(x = []): return len(x) -def mpd(x = []): - x.append("x") + + def use_implicit_return_value(arg): @@ -128,10 +128,10 @@ def mutli_return(arg): else: return do_nothing() -#Modification of parameter with default -def augassign(x = []): - x += ["x"] + + + #ODASA 3658 @@ -144,61 +144,3 @@ def ok5(): except ValueError as e: print(e) exit(EXIT_ERROR) - - - -# indirect modification of parameter with default -def aug_assign_argument(x): - x += ['x'] - -def mutate_argument(x): - x.append('x') - -def indirect_modification(y = []): - aug_assign_argument(y) - mutate_argument(y) - -def guarded_modification(z=[]): - if z: - z.append(0) - return z - -# This function causes a discrepancy between the -# Python 2 and 3 versions of the analysis. -# We comment it out until we have resoved the issue. -# -# def issue1143(expr, param=[]): -# if not param: -# return result -# for i in param: -# param.remove(i) # Mutation here - - -# Type guarding of modification of parameter with default: - -def do_stuff_based_on_type(x): - if isinstance(x, str): - x = x.split() - elif isinstance(x, dict): - x.setdefault('foo', 'bar') - elif isinstance(x, list): - x.append(5) - elif isinstance(x, tuple): - x = x.unknown_method() - -def str_default(x="hello world"): - do_stuff_based_on_type(x) - -def dict_default(x={'baz':'quux'}): - do_stuff_based_on_type(x) - -def list_default(x=[1,2,3,4]): - do_stuff_based_on_type(x) - -def tuple_default(x=(1,2)): - do_stuff_based_on_type(x) - -# Modification of parameter with default (safe method) - -def safe_method(x=[]): - return x.count(42) From 411c1076608abf44218b5cac7cf61f079cdcecac Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 22 Jan 2024 15:21:57 +0000 Subject: [PATCH 2/5] Python: Add tests for `deepcopy` FPs There are two issues with `deepcopy` here. Firstly, the `deepcopy` function itself has a mutable default value in its parameter `_nil` (set to the empty list by default). Now, this value is never actually returned from `deepcopy`, as it is only used as a sentinel, but our analysis is not clever enough to see this. Thus, it thinks that this mutable default is returned, and hence the result of any call to `deepcopy` is a potential source. To remedy this, I opted to simply exclude all sources that originate from within the standard library. It is very unlikely for any of the sources in the standard library to be legit. Secondly, `deepcopy` -- by virtue of being a function that we model as preserving values -- admits data-flow through its calls, but this is not correct for the mutable default query, as it is here the _identity_ of the default value in question that is important. Thus, we get spurious flow through `deepcopy` for this specific query. --- ...odificationOfParameterWithDefault.expected | 47 +++++++++++++++++++ .../options | 1 + .../test.py | 12 +++++ .../query-tests/Functions/general/options | 2 +- 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/options diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected index b33afdac80db..760532619b20 100644 --- a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected @@ -1,4 +1,18 @@ edges +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | test.py:213:9:213:20 | ControlFlowNode for deepcopy() | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x | | test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l | | test.py:7:11:7:11 | ControlFlowNode for l | test.py:8:5:8:5 | ControlFlowNode for l | | test.py:12:14:12:14 | ControlFlowNode for l | test.py:13:9:13:9 | ControlFlowNode for l | @@ -40,7 +54,27 @@ edges | test.py:195:28:195:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x | | test.py:197:18:197:18 | ControlFlowNode for x | test.py:198:28:198:28 | ControlFlowNode for x | | test.py:198:28:198:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x | +| test.py:213:5:213:5 | ControlFlowNode for y | test.py:214:5:214:5 | ControlFlowNode for y | +| test.py:213:9:213:20 | ControlFlowNode for deepcopy() | test.py:213:5:213:5 | ControlFlowNode for y | +| test.py:216:30:216:30 | ControlFlowNode for x | test.py:217:18:217:18 | ControlFlowNode for x | +| test.py:217:5:217:5 | ControlFlowNode for y | test.py:218:5:218:5 | ControlFlowNode for y | +| test.py:217:9:217:19 | ControlFlowNode for deepcopy() | test.py:217:5:217:5 | ControlFlowNode for y | +| test.py:217:18:217:18 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | +| test.py:217:18:217:18 | ControlFlowNode for x | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | nodes +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | semmle.label | ControlFlowNode for _nil | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil | semmle.label | ControlFlowNode for _nil | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | semmle.label | ControlFlowNode for _deepcopy_atomic() | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:2:12:2:12 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | | test.py:3:5:3:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | | test.py:7:11:7:11 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | @@ -107,7 +141,17 @@ nodes | test.py:195:28:195:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:197:18:197:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:198:28:198:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:213:5:213:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:213:9:213:20 | ControlFlowNode for deepcopy() | semmle.label | ControlFlowNode for deepcopy() | +| test.py:214:5:214:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:216:30:216:30 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:217:5:217:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:217:9:217:19 | ControlFlowNode for deepcopy() | semmle.label | ControlFlowNode for deepcopy() | +| test.py:217:18:217:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:218:5:218:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | subpaths +| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | +| test.py:217:18:217:18 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | #select | test.py:3:5:3:5 | ControlFlowNode for l | test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l | This expression mutates a $@. | test.py:2:12:2:12 | ControlFlowNode for l | default value | | test.py:8:5:8:5 | ControlFlowNode for l | test.py:7:11:7:11 | ControlFlowNode for l | test.py:8:5:8:5 | ControlFlowNode for l | This expression mutates a $@. | test.py:7:11:7:11 | ControlFlowNode for l | default value | @@ -138,3 +182,6 @@ subpaths | test.py:185:9:185:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:185:9:185:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value | | test.py:187:9:187:9 | ControlFlowNode for x | test.py:194:18:194:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:194:18:194:18 | ControlFlowNode for x | default value | | test.py:187:9:187:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value | +| test.py:214:5:214:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | test.py:214:5:214:5 | ControlFlowNode for y | This expression mutates a $@. | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | default value | +| test.py:218:5:218:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | test.py:218:5:218:5 | ControlFlowNode for y | This expression mutates a $@. | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | default value | +| test.py:218:5:218:5 | ControlFlowNode for y | test.py:216:30:216:30 | ControlFlowNode for x | test.py:218:5:218:5 | ControlFlowNode for y | This expression mutates a $@. | test.py:216:30:216:30 | ControlFlowNode for x | default value | diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/options b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/options new file mode 100644 index 000000000000..d0cb6f47f9b1 --- /dev/null +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=2 --dont-split-graph diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py index 2791acf235c0..c1983a643d2e 100644 --- a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py @@ -204,3 +204,15 @@ def tuple_default(x=(1,2)): def safe_method(x=[]): return x.count(42) + +# Use of deepcopy: + +from copy import deepcopy + +def flow_from_within_deepcopy_fp(): + y = deepcopy([]) + y.append(1) #$ SPURIOUS: modification=y + +def flow_through_deepcopy_fp(x=[]): + y = deepcopy(x) + y.append(1) #$ SPURIOUS: modification=y diff --git a/python/ql/test/query-tests/Functions/general/options b/python/ql/test/query-tests/Functions/general/options index 2032dd9e54c5..d0cb6f47f9b1 100644 --- a/python/ql/test/query-tests/Functions/general/options +++ b/python/ql/test/query-tests/Functions/general/options @@ -1 +1 @@ -semmle-extractor-options: --max-import-depth=1 --dont-split-graph +semmle-extractor-options: --max-import-depth=2 --dont-split-graph From 14c958ac4d8ab5c7bb4bd22d545a96229dd490f4 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 22 Jan 2024 15:23:52 +0000 Subject: [PATCH 3/5] Python: Remove mutable default sources from inside stdlib --- ...ionOfParameterWithDefaultCustomizations.qll | 6 +++++- ...ModificationOfParameterWithDefault.expected | 18 ------------------ .../ModificationOfParameterWithDefault/test.py | 2 +- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/python/ql/src/semmle/python/functions/ModificationOfParameterWithDefaultCustomizations.qll b/python/ql/src/semmle/python/functions/ModificationOfParameterWithDefaultCustomizations.qll index 0aa2b96a634f..c7aef20c09dd 100644 --- a/python/ql/src/semmle/python/functions/ModificationOfParameterWithDefaultCustomizations.qll +++ b/python/ql/src/semmle/python/functions/ModificationOfParameterWithDefaultCustomizations.qll @@ -75,7 +75,11 @@ module ModificationOfParameterWithDefault { class MutableDefaultValue extends Source { boolean nonEmpty; - MutableDefaultValue() { nonEmpty = mutableDefaultValue(this.asCfgNode().(NameNode).getNode()) } + MutableDefaultValue() { + nonEmpty = mutableDefaultValue(this.asCfgNode().(NameNode).getNode()) and + // Ignore sources inside the standard library. These are unlikely to be true positives. + exists(this.getLocation().getFile().getRelativePath()) + } override boolean isNonEmpty() { result = nonEmpty } } diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected index 760532619b20..afcfc006c137 100644 --- a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected @@ -1,12 +1,6 @@ edges | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | test.py:213:9:213:20 | ControlFlowNode for deepcopy() | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | @@ -54,8 +48,6 @@ edges | test.py:195:28:195:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x | | test.py:197:18:197:18 | ControlFlowNode for x | test.py:198:28:198:28 | ControlFlowNode for x | | test.py:198:28:198:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x | -| test.py:213:5:213:5 | ControlFlowNode for y | test.py:214:5:214:5 | ControlFlowNode for y | -| test.py:213:9:213:20 | ControlFlowNode for deepcopy() | test.py:213:5:213:5 | ControlFlowNode for y | | test.py:216:30:216:30 | ControlFlowNode for x | test.py:217:18:217:18 | ControlFlowNode for x | | test.py:217:5:217:5 | ControlFlowNode for y | test.py:218:5:218:5 | ControlFlowNode for y | | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | test.py:217:5:217:5 | ControlFlowNode for y | @@ -63,11 +55,6 @@ edges | test.py:217:18:217:18 | ControlFlowNode for x | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | nodes | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | semmle.label | ControlFlowNode for _nil | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil | semmle.label | ControlFlowNode for _nil | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | semmle.label | ControlFlowNode for _deepcopy_atomic() | | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | @@ -141,9 +128,6 @@ nodes | test.py:195:28:195:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:197:18:197:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:198:28:198:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:213:5:213:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:213:9:213:20 | ControlFlowNode for deepcopy() | semmle.label | ControlFlowNode for deepcopy() | -| test.py:214:5:214:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | | test.py:216:30:216:30 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:217:5:217:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | semmle.label | ControlFlowNode for deepcopy() | @@ -182,6 +166,4 @@ subpaths | test.py:185:9:185:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:185:9:185:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value | | test.py:187:9:187:9 | ControlFlowNode for x | test.py:194:18:194:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:194:18:194:18 | ControlFlowNode for x | default value | | test.py:187:9:187:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value | -| test.py:214:5:214:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | test.py:214:5:214:5 | ControlFlowNode for y | This expression mutates a $@. | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | default value | -| test.py:218:5:218:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | test.py:218:5:218:5 | ControlFlowNode for y | This expression mutates a $@. | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | default value | | test.py:218:5:218:5 | ControlFlowNode for y | test.py:216:30:216:30 | ControlFlowNode for x | test.py:218:5:218:5 | ControlFlowNode for y | This expression mutates a $@. | test.py:216:30:216:30 | ControlFlowNode for x | default value | diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py index c1983a643d2e..afb486207711 100644 --- a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py @@ -211,7 +211,7 @@ def safe_method(x=[]): def flow_from_within_deepcopy_fp(): y = deepcopy([]) - y.append(1) #$ SPURIOUS: modification=y + y.append(1) def flow_through_deepcopy_fp(x=[]): y = deepcopy(x) From d6d59377d3586cad37507d222f48a44f64fe7a38 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 22 Jan 2024 15:40:30 +0000 Subject: [PATCH 4/5] Python: Fix flow through `deepcopy` Or, more generally, any copy step, as these presumably do not preserve object identity. (Arguably, `copy` could still be susceptible to interior mutability, but I think that's outside the scope of this query anyway.) --- .../ModificationOfParameterWithDefault.qll | 5 ++++ ...odificationOfParameterWithDefault.expected | 29 ------------------- .../test.py | 2 +- 3 files changed, 6 insertions(+), 30 deletions(-) diff --git a/python/ql/src/semmle/python/functions/ModificationOfParameterWithDefault.qll b/python/ql/src/semmle/python/functions/ModificationOfParameterWithDefault.qll index a08387725153..77dc4ccafcc0 100644 --- a/python/ql/src/semmle/python/functions/ModificationOfParameterWithDefault.qll +++ b/python/ql/src/semmle/python/functions/ModificationOfParameterWithDefault.qll @@ -8,6 +8,7 @@ private import python import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.internal.TaintTrackingPrivate as TTP /** * Provides a data-flow configuration for detecting modifications of a parameters default value. @@ -69,6 +70,10 @@ module ModificationOfParameterWithDefault { // if we are tracking a empty default, then it is ok to modify non-empty values, // so our tracking ends at those. state = false and node instanceof MustBeNonEmpty + or + // the target of a copy step is (presumably) a different object, and hence modifications of + // this object no longer matter for the purposes of this query. + TTP::copyStep(_, node) and state in [true, false] } } diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected index afcfc006c137..b33afdac80db 100644 --- a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected @@ -1,12 +1,4 @@ edges -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x | | test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l | | test.py:7:11:7:11 | ControlFlowNode for l | test.py:8:5:8:5 | ControlFlowNode for l | | test.py:12:14:12:14 | ControlFlowNode for l | test.py:13:9:13:9 | ControlFlowNode for l | @@ -48,20 +40,7 @@ edges | test.py:195:28:195:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x | | test.py:197:18:197:18 | ControlFlowNode for x | test.py:198:28:198:28 | ControlFlowNode for x | | test.py:198:28:198:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x | -| test.py:216:30:216:30 | ControlFlowNode for x | test.py:217:18:217:18 | ControlFlowNode for x | -| test.py:217:5:217:5 | ControlFlowNode for y | test.py:218:5:218:5 | ControlFlowNode for y | -| test.py:217:9:217:19 | ControlFlowNode for deepcopy() | test.py:217:5:217:5 | ControlFlowNode for y | -| test.py:217:18:217:18 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | -| test.py:217:18:217:18 | ControlFlowNode for x | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | nodes -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | semmle.label | ControlFlowNode for _deepcopy_atomic() | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:2:12:2:12 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | | test.py:3:5:3:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | | test.py:7:11:7:11 | ControlFlowNode for l | semmle.label | ControlFlowNode for l | @@ -128,14 +107,7 @@ nodes | test.py:195:28:195:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:197:18:197:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:198:28:198:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:216:30:216:30 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:217:5:217:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:217:9:217:19 | ControlFlowNode for deepcopy() | semmle.label | ControlFlowNode for deepcopy() | -| test.py:217:18:217:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:218:5:218:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | subpaths -| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | -| test.py:217:18:217:18 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | test.py:217:9:217:19 | ControlFlowNode for deepcopy() | #select | test.py:3:5:3:5 | ControlFlowNode for l | test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l | This expression mutates a $@. | test.py:2:12:2:12 | ControlFlowNode for l | default value | | test.py:8:5:8:5 | ControlFlowNode for l | test.py:7:11:7:11 | ControlFlowNode for l | test.py:8:5:8:5 | ControlFlowNode for l | This expression mutates a $@. | test.py:7:11:7:11 | ControlFlowNode for l | default value | @@ -166,4 +138,3 @@ subpaths | test.py:185:9:185:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:185:9:185:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value | | test.py:187:9:187:9 | ControlFlowNode for x | test.py:194:18:194:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:194:18:194:18 | ControlFlowNode for x | default value | | test.py:187:9:187:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value | -| test.py:218:5:218:5 | ControlFlowNode for y | test.py:216:30:216:30 | ControlFlowNode for x | test.py:218:5:218:5 | ControlFlowNode for y | This expression mutates a $@. | test.py:216:30:216:30 | ControlFlowNode for x | default value | diff --git a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py index afb486207711..b67fa985f513 100644 --- a/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py +++ b/python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py @@ -215,4 +215,4 @@ def flow_from_within_deepcopy_fp(): def flow_through_deepcopy_fp(x=[]): y = deepcopy(x) - y.append(1) #$ SPURIOUS: modification=y + y.append(1) From 96b1b8e4023ddc571abefd78ee2403e823523d4d Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jan 2024 12:31:23 +0000 Subject: [PATCH 5/5] Python: Remove empty lines from test file --- .../Functions/general/DeprecatedSliceMethod.expected | 6 +++--- .../query-tests/Functions/general/functions_test.py | 10 ---------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.expected b/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.expected index d2fa86f6f277..1a6df6eca47d 100644 --- a/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.expected +++ b/python/ql/test/query-tests/Functions/general/DeprecatedSliceMethod.expected @@ -1,3 +1,3 @@ -| functions_test.py:99:5:99:40 | Function DeprecatedSliceMethods.__getslice__ | __getslice__ method has been deprecated since Python 2.0. | -| functions_test.py:102:5:102:47 | Function DeprecatedSliceMethods.__setslice__ | __setslice__ method has been deprecated since Python 2.0. | -| functions_test.py:105:5:105:40 | Function DeprecatedSliceMethods.__delslice__ | __delslice__ method has been deprecated since Python 2.0. | +| functions_test.py:95:5:95:40 | Function DeprecatedSliceMethods.__getslice__ | __getslice__ method has been deprecated since Python 2.0. | +| functions_test.py:98:5:98:47 | Function DeprecatedSliceMethods.__setslice__ | __setslice__ method has been deprecated since Python 2.0. | +| functions_test.py:101:5:101:40 | Function DeprecatedSliceMethods.__delslice__ | __delslice__ method has been deprecated since Python 2.0. | diff --git a/python/ql/test/query-tests/Functions/general/functions_test.py b/python/ql/test/query-tests/Functions/general/functions_test.py index 23419d738b17..741599abd5b0 100644 --- a/python/ql/test/query-tests/Functions/general/functions_test.py +++ b/python/ql/test/query-tests/Functions/general/functions_test.py @@ -36,10 +36,6 @@ def ok3(x): def ok4(x = []): return len(x) - - - - def use_implicit_return_value(arg): x = do_nothing() return call_non_callable(arg) @@ -128,12 +124,6 @@ def mutli_return(arg): else: return do_nothing() - - - - - - #ODASA 3658 from sys import exit #Consistent returns