Merge ~alexmurray/review-tools:validate-parsed-plugs-slots-json-for-bare-scalars into review-tools:master

Proposed by Alex Murray
Status: Merged
Merged at revision: 38c1077de96fbceda7d8f7cd716443b919ccb66d
Proposed branch: ~alexmurray/review-tools:validate-parsed-plugs-slots-json-for-bare-scalars
Merge into: review-tools:master
Diff against target: 210 lines (+97/-9)
6 files modified
bin/snap-review (+14/-4)
bin/snap-verify-declaration (+4/-2)
reviewtools/sr_declaration.py (+26/-0)
reviewtools/tests/test_sr_declaration.py (+23/-1)
tests/test-snap-verify-declaration.sh (+11/-0)
tests/test-snap-verify-declaration.sh.expected (+19/-2)
Reviewer Review Type Date Requested Status
Evan Caville Approve
Review via email: mp+464072@code.launchpad.net

Description of the change

many: when parsing plugs/slots json validate for bare scalars

The store will raise an error if the provided plugs/slots json contains bare
scalars - instead these need to be strings. So when parsing these from the user,
validate them against this condition so we can warn the user before they go and
try to update the declaration in the store using something that would ultimately
fail.

Signed-off-by: Alex Murray <email address hidden>

To post a comment you must log in.
Revision history for this message
Evan Caville (evancaville) wrote :

LGTM - thanks for adding this in!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/bin/snap-review b/bin/snap-review
2index 1b9ebc0..37c9fea 100755
3--- a/bin/snap-review
4+++ b/bin/snap-review
5@@ -18,6 +18,8 @@ from reviewtools.common import (
6 verify_override_state,
7 )
8
9+from reviewtools.sr_declaration import validate_json
10+
11
12 def print_findings(results, description):
13 """
14@@ -211,13 +213,21 @@ def main():
15 if args.plugs:
16 if overrides is None:
17 overrides = {}
18- with open(args.plugs, "r") as plugs_file:
19- overrides["snap_decl_plugs"] = json.loads(plugs_file.read())
20+ try:
21+ with open(args.plugs, "r") as plugs_file:
22+ overrides["snap_decl_plugs"] = json.loads(plugs_file.read())
23+ validate_json(overrides["snap_decl_plugs"])
24+ except Exception as e:
25+ error("Failed to read plugs: %s" % e, output_type=error_output_type)
26 if args.slots:
27 if overrides is None:
28 overrides = {}
29- with open(args.slots, "r") as slots_file:
30- overrides["snap_decl_slots"] = json.loads(slots_file.read())
31+ try:
32+ with open(args.slots, "r") as slots_file:
33+ overrides["snap_decl_slots"] = json.loads(slots_file.read())
34+ validate_json(overrides["snap_decl_slots"])
35+ except Exception as e:
36+ error("Failed to read slots: %s" % e, output_type=error_output_type)
37 if args.allow_classic:
38 if overrides is None:
39 overrides = {}
40diff --git a/bin/snap-verify-declaration b/bin/snap-verify-declaration
41index b596387..152c6c7 100755
42--- a/bin/snap-verify-declaration
43+++ b/bin/snap-verify-declaration
44@@ -63,14 +63,16 @@ def main():
45 try:
46 with open(args.plugs, "r") as plugs_file:
47 snap_decl["plugs"] = json.loads(plugs_file.read())
48+ sr_declaration.validate_json(snap_decl["plugs"])
49 except Exception as e:
50- error("Could not read plugs: %s" % e, output_type=error_output_type)
51+ error("Failed to read plugs: %s" % e, output_type=error_output_type)
52 if args.slots:
53 try:
54 with open(args.slots, "r") as slots_file:
55 snap_decl["slots"] = json.loads(slots_file.read())
56+ sr_declaration.validate_json(snap_decl["slots"])
57 except Exception as e:
58- error("Could not read slots: %s" % e, output_type=error_output_type)
59+ error("Failed to read slots: %s" % e, output_type=error_output_type)
60
61 try:
62 review = sr_declaration.verify_snap_declaration(snap_decl)
63diff --git a/reviewtools/sr_declaration.py b/reviewtools/sr_declaration.py
64index 2978593..5af713a 100644
65--- a/reviewtools/sr_declaration.py
66+++ b/reviewtools/sr_declaration.py
67@@ -1476,6 +1476,32 @@ class SnapReviewDeclaration(SnapReview):
68 self._verify_declaration_apps_hooks("hooks")
69
70
71+# check there are no bare boolean / integer etc values in the provided json data
72+# since the store will fail to accept them
73+def validate_json(data, path=[]):
74+ def validate_value(value, key, path):
75+ if (
76+ isinstance(value, bool)
77+ or isinstance(value, int)
78+ or isinstance(value, float)
79+ ):
80+ raise ValueError(
81+ "bare %s value %s for '%s' is not allowed - this should be a string \"%s\" instead"
82+ % (str(type(value)), value, ":".join(path + [key]), str(value).lower())
83+ )
84+ if isinstance(value, dict):
85+ validate_json(value, path + [key])
86+ if isinstance(value, list):
87+ validate_json(value, path + [key])
88+
89+ if isinstance(data, dict):
90+ for key, value in data.items():
91+ validate_value(value, key, path)
92+ if isinstance(data, list):
93+ for i, value in enumerate(data):
94+ validate_value(value, str(i), path)
95+
96+
97 #
98 # Helper functions
99 #
100diff --git a/reviewtools/tests/test_sr_declaration.py b/reviewtools/tests/test_sr_declaration.py
101index 21b1f4e..cef3967 100644
102--- a/reviewtools/tests/test_sr_declaration.py
103+++ b/reviewtools/tests/test_sr_declaration.py
104@@ -18,6 +18,7 @@ from reviewtools.sr_declaration import (
105 SnapReviewDeclaration,
106 SnapDeclarationException,
107 verify_snap_declaration,
108+ validate_json,
109 )
110 import reviewtools.sr_tests as sr_tests
111 from unittest import TestCase
112@@ -6297,7 +6298,14 @@ slots:
113 slots = {"iface": {"interface": "upower-observe"}}
114 self.set_test_snap_yaml("slots", slots)
115 self.set_test_snap_yaml("type", "app")
116- overrides = {"snap_decl_slots": {"upower-observe": {"allow-connection": True, "allow-auto-connection": True}}}
117+ overrides = {
118+ "snap_decl_slots": {
119+ "upower-observe": {
120+ "allow-connection": True,
121+ "allow-auto-connection": True,
122+ }
123+ }
124+ }
125 c = SnapReviewDeclaration(self.test_name, overrides=overrides)
126 self._use_test_base_declaration(c)
127
128@@ -9051,3 +9059,17 @@ class TestSnapDeclarationVerify(TestCase):
129
130 pprint.pprint(r)
131 raise
132+
133+
134+class TestSnapDeclarationValidateJson(TestCase):
135+ def test_validate_json(self):
136+ # test validation of bare scalar values in parsed data
137+ for decl in [
138+ {"foo": 1},
139+ {"foo": [1]},
140+ {"foo": [{"bar": 1}]},
141+ {"foo": [{"bar": [{"baz": True}]}]},
142+ {"foo": [{"bar": [{"baz": [1.0]}]}]},
143+ ]:
144+ with self.assertRaises(ValueError):
145+ validate_json(decl)
146diff --git a/tests/test-snap-verify-declaration.sh b/tests/test-snap-verify-declaration.sh
147index 40b1dd3..3d1d5ad 100755
148--- a/tests/test-snap-verify-declaration.sh
149+++ b/tests/test-snap-verify-declaration.sh
150@@ -107,6 +107,17 @@ EOM
151 run --plugs="./plugs"
152 run --json --plugs="./plugs"
153
154+# invalid (bare scalars)
155+cat > "./plugs" <<EOM
156+{
157+ "mir": {
158+ "allow-auto-connection": true
159+ }
160+}
161+EOM
162+run --plugs="./plugs"
163+run --json --plugs="./plugs"
164+
165
166 cd "$origdir"
167 echo
168diff --git a/tests/test-snap-verify-declaration.sh.expected b/tests/test-snap-verify-declaration.sh.expected
169index 026401b..0149682 100644
170--- a/tests/test-snap-verify-declaration.sh.expected
171+++ b/tests/test-snap-verify-declaration.sh.expected
172@@ -135,7 +135,7 @@ Running: snap-verify-declaration --json --plugs=./plugs
173 }
174
175 Running: snap-verify-declaration --plugs=./plugs
176-ERROR: Could not read plugs: Expecting property name enclosed in double quotes: line 2 column 1 (char 2)
177+ERROR: Failed to read plugs: Expecting property name enclosed in double quotes: line 2 column 1 (char 2)
178
179 Running: snap-verify-declaration --json --plugs=./plugs
180 {
181@@ -143,7 +143,7 @@ Running: snap-verify-declaration --json --plugs=./plugs
182 "error": {
183 "msg": {
184 "manual_review": true,
185- "text": "Could not read plugs: Expecting property name enclosed in double quotes: line 2 column 1 (char 2)"
186+ "text": "Failed to read plugs: Expecting property name enclosed in double quotes: line 2 column 1 (char 2)"
187 }
188 },
189 "info": {},
190@@ -204,3 +204,20 @@ Running: snap-verify-declaration --json --plugs=./plugs
191 "warn": {}
192 }
193
194+Running: snap-verify-declaration --plugs=./plugs
195+ERROR: Failed to read plugs: bare <class 'bool'> value True for 'mir:allow-auto-connection' is not allowed - this should be a string "true" instead
196+
197+Running: snap-verify-declaration --json --plugs=./plugs
198+{
199+ "runtime-errors": {
200+ "error": {
201+ "msg": {
202+ "manual_review": true,
203+ "text": "Failed to read plugs: bare <class 'bool'> value True for 'mir:allow-auto-connection' is not allowed - this should be a string \"true\" instead"
204+ }
205+ },
206+ "info": {},
207+ "warn": {}
208+ }
209+}
210+

Subscribers

People subscribed via source and target branches

to all changes: