Merge ~jslarraz/review-tools:schema-add-layout into review-tools:master

Proposed by Jorge Sancho Larraz
Status: Merged
Merged at revision: 5934f2a041b7e784ab1bd332d7832536dfc1c5ce
Proposed branch: ~jslarraz/review-tools:schema-add-layout
Merge into: review-tools:master
Diff against target: 1025 lines (+180/-747)
6 files modified
check-names.list (+0/-3)
reviewtools/schemas/snap.json (+57/-0)
reviewtools/sr_lint.py (+0/-206)
reviewtools/tests/schemas/test_schema_snap.py (+123/-0)
reviewtools/tests/test_sr_lint.py (+0/-346)
tests/test.sh.expected (+0/-192)
Reviewer Review Type Date Requested Status
Alex Murray Approve
Review via email: mp+467096@code.launchpad.net

Commit message

many: validate snap.yaml::layout via schema

To post a comment you must log in.
Revision history for this message
Jorge Sancho Larraz (jslarraz) wrote :

reviewtools/tests/schemas/test_schema_against_store.py validates the current snap.yaml schema against all snaps in the store. No additional errors have been found in this version.

Revision history for this message
Jorge Sancho Larraz (jslarraz) wrote :

See inline comments

Revision history for this message
Alex Murray (alexmurray) wrote :

LGTM!

review: Approve
Revision history for this message
Jorge Sancho Larraz (jslarraz) wrote :

Current tests cases have a 100% coverage. Thus, passing all the existent cases indicates that the regular expression is fine

See https://gist.github.com/jslarraz/9042092c83160a10af0985af716a382d

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/check-names.list b/check-names.list
2index 5725ecf..687f33f 100644
3--- a/check-names.list
4+++ b/check-names.list
5@@ -90,9 +90,6 @@ lint-snap-v2:icon_theme_size|
6 lint-snap-v2:iffy_files|
7 lint-snap-v2:install-mode|
8 lint-snap-v2:invalid_link|
9-lint-snap-v2:layout_source|
10-lint-snap-v2:layout_target|
11-lint-snap-v2:layout|
12 lint-snap-v2:meta_gui_desktop|
13 lint-snap-v2:mpris_slot_name|
14 lint-snap-v2:personal-files_attrib_valid|
15diff --git a/reviewtools/schemas/snap.json b/reviewtools/schemas/snap.json
16index e8038b0..d45ddbf 100644
17--- a/reviewtools/schemas/snap.json
18+++ b/reviewtools/schemas/snap.json
19@@ -89,6 +89,63 @@
20 }
21 },
22 "additionalProperties": false
23+ },
24+ "layout": {
25+ "description": "Modify the execution environment of a strictly-confined snap.",
26+ "$comment": "See https://forum.snapcraft.io/t/why-are-snap-layouts-limited-to-15-entries/15337/3",
27+ "type": "object",
28+ "propertyNames": {
29+ "not": {
30+ "anyOf": [
31+ {"pattern": "^/boot"},
32+ {"pattern": "^/dev"},
33+ {"pattern": "^/home"},
34+ {"pattern": "^(/usr)?/lib/firmware"},
35+ {"pattern": "^(/usr)?/lib/modules"},
36+ {"pattern": "^/lost+found"},
37+ {"pattern": "^/media"},
38+ {"pattern": "^/proc"},
39+ {"pattern": "^(/var)?/run"},
40+ {"pattern": "^/sys"},
41+ {"pattern": "^/tmp"},
42+ {"pattern": "^/var/lib/snapd"},
43+ {"pattern": "^/var/snap"}
44+ ]
45+ }
46+ },
47+ "patternProperties": {
48+ "^(?:\\$(?:SNAP|SNAP_COMMON|SNAP_DATA))?(\\/(?![\\.]*\\/)[^\\/\\0]*)*[\\/]?$": {
49+ "$comment": "The core of this regex, (\\/[^\\/\\0]*)*, is intended to match groups of (slash + file name), where file name does not contain slash or NULL characters. The negative lookahead (?![\\.]*\\/) is used to ensure that at least one character other than dots is present between two consecutive slashes, thus avoiding non-normalized paths but still allowing hidden files. Optionally the path may start with $SNAP, $SNAP_COMMON or $SNAP_DATA and may have a tailing slash",
50+ "type": "object",
51+ "patternProperties": {
52+ "^bind|bind-file|symlink$": {
53+ "type": "string",
54+ "$comment": "This regex is identical to the previous one with the exception that it always must start with $SNAP, $SNAP_COMMON or $SNAP_DATA",
55+ "pattern": "^\\$(SNAP|SNAP_COMMON|SNAP_DATA)(\\/(?![\\.]*\\/)[^\\/\\0]*)*[\\/]?$"
56+ },
57+ "^mode$": {
58+ "type": "integer",
59+ "minimum": 1,
60+ "maximum": 511
61+ },
62+ "^type$": {
63+ "type": "string",
64+ "enum": ["tmpfs"]
65+ },
66+ "^user|group$": {
67+ "type": "string",
68+ "not": {
69+ "pattern": "^[0-9]+$"
70+ }
71+ }
72+ },
73+ "minProperties": 1,
74+ "additionalProperties": false
75+ }
76+ },
77+ "minProperties": 1,
78+ "maxProperties": 100,
79+ "additionalProperties": false
80 }
81 },
82 "required": ["name", "version"]
83diff --git a/reviewtools/sr_lint.py b/reviewtools/sr_lint.py
84index 8cad5e9..72d6691 100644
85--- a/reviewtools/sr_lint.py
86+++ b/reviewtools/sr_lint.py
87@@ -2360,212 +2360,6 @@ class SnapReviewLint(SnapReview):
88 s = "paths found in both read and write: %s" % ", ".join(both)
89 self._add_result(t, n, s)
90
91- def check_layout(self):
92- """Check layout"""
93- if "layout" not in self.snap_yaml:
94- return
95-
96- # snap/validate.go - ValidateLayout()
97- snap_vars = ["$SNAP", "$SNAP_COMMON", "$SNAP_DATA"]
98- target_bad_prefixes = [
99- "/boot",
100- "/dev",
101- "/home",
102- "(/usr)?/lib/firmware",
103- "(/usr)?/lib/modules",
104- "/lost+found",
105- "/media",
106- "/proc",
107- "(/var)?/run",
108- "/sys",
109- "/tmp",
110- "/var/lib/snapd",
111- "/var/snap",
112- ]
113-
114- def _verify_layout_target(p, disallowed):
115- if "\0" in p or os.path.normpath(p) != p:
116- return False
117-
118- # if the normalized path starts with snap_vars, then it is ok
119- for prefix in snap_vars:
120- if p.startswith("%s/" % prefix):
121- return True
122-
123- # otherwise it must be an absolute path
124- if not p.startswith("/"):
125- return False
126-
127- # p is now guaranteed to start with '/' and not '//...'
128- for dis in disallowed:
129- if re.match(dis, p):
130- return False
131-
132- return True
133-
134- def _verify_layout_source(p, allowed):
135- if "\0" in p or os.path.normpath(p) != p or not p.startswith("$"):
136- return False
137-
138- # p is guaranteed to start with '$'
139- top = p.split("/")[0]
140- if top not in allowed:
141- return False
142-
143- return True
144-
145- # arbitrary; 10 was agreed upon with snapd team (zyga) but found snaps
146- # with 8, so bumped to 15. Unfortunately, others hit the ceiling, so
147- # bumping to 30, then bumped to 100 since surely 100 layouts is enough
148- # for anyone ;)
149- # https://forum.snapcraft.io/t/why-are-snap-layouts-limited-to-15-entries/15337/3
150- maximum_layouts = 100
151-
152- key = "layout"
153- t = "info"
154- n = self._get_check_name(key)
155- s = "OK"
156- if not isinstance(self.snap_yaml[key], dict):
157- t = "error"
158- s = "invalid %s entry: %s (not a dict)" % (key, self.snap_yaml[key])
159- self._add_result(t, n, s)
160- return
161- elif len(self.snap_yaml[key].keys()) < 1:
162- t = "error"
163- s = "invalid %s entry (empty)" % (key)
164- self._add_result(t, n, s)
165- return
166- elif len(self.snap_yaml[key].keys()) > maximum_layouts:
167- t = "error"
168- s = "too many defined layouts (%d > %d)" % (
169- len(self.snap_yaml[key].keys()),
170- maximum_layouts,
171- )
172- self._add_result(t, n, s)
173- return
174- self._add_result(t, n, s)
175-
176- for target in self.snap_yaml[key]:
177- t = "info"
178- n = self._get_check_name("%s_target" % key, app=target)
179- s = "OK"
180-
181- if not isinstance(self.snap_yaml[key][target], dict):
182- t = "error"
183- s = "invalid entry: %s (not a dict)" % (self.snap_yaml[key][target])
184- self._add_result(t, n, s)
185- continue
186- elif len(self.snap_yaml[key][target].keys()) < 1:
187- t = "error"
188- s = "invalid target '%s' (empty)" % (target)
189- self._add_result(t, n, s)
190- continue
191- elif not _verify_layout_target(target, target_bad_prefixes):
192- t = "error"
193- s = (
194- "invalid mount target: '%s'" % target
195- + " (should be "
196- + "a legal path (ie, absolute path or one that starts "
197- + "with: %s)" % ", ".join(snap_vars)
198- + ". If absolute, "
199- + "should not start with: %s" % ", ".join(target_bad_prefixes)
200- )
201- self._add_result(t, n, s)
202- continue
203- self._add_result(t, n, s)
204-
205- # from snap/info_snap.go
206- known = ["bind", "bind-file", "symlink", "type", "user", "group", "mode"]
207- for ltype in self.snap_yaml[key][target]:
208- t = "info"
209- n = self._get_check_name("%s" % key, app=target, extra=ltype)
210- s = "OK"
211-
212- if ltype not in known:
213- t = "error"
214- s = "invalid layout: '%s' (should be one of %s)" % (
215- ltype,
216- ", ".join(known),
217- )
218- self._add_result(t, n, s)
219- continue
220-
221- if ltype in ["bind", "bind-file", "symlink"]:
222- target = target
223- source = self.snap_yaml[key][target][ltype]
224- t = "info"
225- n = self._get_check_name(
226- "%s_source" % key, app=target, extra=source
227- )
228- s = "OK"
229-
230- if not isinstance(source, str):
231- t = "error"
232- s = "invalid source: %s (not a str)" % source
233- elif not _verify_layout_source(source, snap_vars):
234- t = "error"
235- s = (
236- "invalid source mount: '%s' " % source
237- + "(should be a legal path and start with one "
238- + "of: %s" % ", ".join(snap_vars)
239- )
240- elif ltype == "mode":
241- ltype = "mode"
242- rdata = self.snap_yaml[key][target][ltype]
243- if not isinstance(rdata, int) and not isinstance(rdata, str):
244- t = "error"
245- s = "invalid mode: should be an integer (eg, 0755)"
246- self._add_result(t, n, s)
247- continue
248-
249- mode = None
250- if isinstance(rdata, str):
251- try:
252- mode = int(rdata, 8)
253- except Exception:
254- t = "error"
255- s = (
256- "mode '%s' should be an integer " % rdata
257- + "within 1-777 octal"
258- )
259- self._add_result(t, n, s)
260- continue
261- else:
262- mode = rdata
263-
264- if mode < 0o1 or mode > 0o777:
265- t = "error"
266- s = "mode '%s' must be within 1-777 octal" % format(mode, "o")
267- elif ltype == "type":
268- ltype = "type"
269- rdata = self.snap_yaml[key][target][ltype]
270- if not isinstance(rdata, str):
271- t = "error"
272- s = "invalid %s (not a str)" % ltype
273- self._add_result(t, n, s)
274- continue
275-
276- if rdata != "tmpfs":
277- t = "error"
278- s = "invalid type: %s != tmpfs" % rdata
279- elif ltype in ["user", "group"]:
280- rdata = self.snap_yaml[key][target][ltype]
281- if not isinstance(rdata, str):
282- t = "error"
283- s = "invalid %s (not a str)" % ltype
284- self._add_result(t, n, s)
285- continue
286-
287- # Don't allow specifying uids
288- try:
289- int(self.snap_yaml[key][target][ltype])
290- t = "error"
291- s = "invalid %s (should not be a number)" % ltype
292- except Exception:
293- pass
294-
295- self._add_result(t, n, s)
296-
297 def check_apps_install_mode(self):
298 """Check apps - install-mode"""
299 if "apps" not in self.snap_yaml:
300diff --git a/reviewtools/tests/schemas/test_schema_snap.py b/reviewtools/tests/schemas/test_schema_snap.py
301index beed77e..96484d3 100644
302--- a/reviewtools/tests/schemas/test_schema_snap.py
303+++ b/reviewtools/tests/schemas/test_schema_snap.py
304@@ -354,3 +354,126 @@ class TestSchemaSnap(TestSchemaBase):
305 with self.subTest(value=value):
306 error = error.replace("{value}", str(value)) if error else error
307 self._test_value("environment", value, error)
308+
309+ def test_layout(self):
310+ for value, error in [
311+ # test_check_layout
312+ (
313+ {
314+ "/etc/demo": {"bind": "$SNAP_COMMON/etc/demo"},
315+ "/etc/demo.cfg": {"symlink": "$SNAP_COMMON/etc/demo.conf"},
316+ "/etc/demo.conf": {"bind-file": "$SNAP_COMMON/etc/demo.conf"},
317+ "/opt/demo": {"bind": "$SNAP/opt/demo"},
318+ "/usr/share/demo": {"bind": "$SNAP/usr/share/demo"},
319+ "/var/cache/demo": {"bind": "$SNAP_DATA/var/cache/demo"},
320+ "/var/lib/demo": {"bind": "$SNAP_DATA/var/lib/demo"},
321+ "/var/lib/foo": {"type": "tmpfs", "mode": 0o755},
322+ "/var/lib/bar": {
323+ "type": "tmpfs",
324+ "user": "username",
325+ "group": "groupname",
326+ "mode": 0o755,
327+ },
328+ },
329+ None,
330+ ),
331+ # test_check_layout_bad - list
332+ ([], "{value} is not of type 'object'"),
333+ # test_check_layout_empty
334+ ({}, "{value} does not have enough properties"),
335+ # test_check_layout_source_target_use_snap_vars - source/target use $SNAP
336+ ({"$SNAP/db": {"bind": "$SNAP_DATA/db"}}, None),
337+ # test_check_layout_target_bad_snap_var - bad target (SNAP_USER_COMMON)
338+ (
339+ {"$SNAP_USER_COMMON/db": {"bind": "$SNAP_DATA/db"}},
340+ "'$SNAP_USER_COMMON/db' does not match ",
341+ ),
342+ # test_check_layout_target_bad_val - bad target (list)
343+ ({"/etc/demo": []}, "[] is not of type 'object'"),
344+ # test_check_layout_target_bad_empty
345+ ({"/etc/demo": {}}, "{} does not have enough properties"),
346+ # test_check_layout_target_bad_too_many
347+ (
348+ dict.fromkeys(["/a/%s" % i for i in range(101)], {"bind": "$SNAP/db"}),
349+ "{value} has too many properties",
350+ ),
351+ # test_check_layout_target_bad - bad target (normpath)
352+ (
353+ {"/etc/../demo": {"bind": "$SNAP_COMMON/etc/demo"}},
354+ "'/etc/../demo' does not match ",
355+ ),
356+ # test_check_layout_target_bad_prefix - bad target (prefix)
357+ (
358+ {"/proc/cmdline": {"bind": "$SNAP_COMMON/etc/demo"}},
359+ " is not allowed for '/proc/cmdline'",
360+ ),
361+ (
362+ {"/lib/firmware/foo": {"bind": "$SNAP_COMMON/etc/demo"}},
363+ "is not allowed for '/lib/firmware/foo'",
364+ ),
365+ (
366+ {"/usr/lib/firmware": {"bind": "$SNAP_COMMON/etc/demo"}},
367+ "is not allowed for '/usr/lib/firmware'",
368+ ),
369+ (
370+ {"/run": {"bind": "$SNAP_COMMON/etc/demo"}},
371+ "is not allowed for '/run'",
372+ ),
373+ (
374+ {"/var/run": {"bind": "$SNAP_COMMON/etc/demo"}},
375+ "is not allowed for '/var/run'",
376+ ),
377+ # test_check_layout_source_bad_prefix - bad source (prefix)
378+ (
379+ {"/etc/demo": {"bind": "/bad/etc/demo"}},
380+ "'/bad/etc/demo' does not match ",
381+ ),
382+ # test_check_layout_source_bad_prefix_home - bad source (prefix $HOME)
383+ (
384+ {"/var/tmp/other": {"bind": "$HOME/snap/other"}},
385+ "'$HOME/snap/other' does not match ",
386+ ),
387+ # test_check_layout_source_bad_val - bad source (list)
388+ ({"/etc/demo": {"bind": []}}, "[] is not of type 'string'"),
389+ # test_check_layout_source_bad - bad source (normpath)
390+ (
391+ {"/etc/demo": {"bind": "$SNAP_COMMON/etc/../demo"}},
392+ "'$SNAP_COMMON/etc/../demo' does not match",
393+ ),
394+ # test_check_layout_source_bad_type - bad source (type)
395+ (
396+ {"/etc/demo": {"nonexistent": "$SNAP_COMMON/etc/demo"}},
397+ "'nonexistent' does not match any of the regexes: ",
398+ ),
399+ # test_check_layout_mode_bad
400+ (
401+ {"/var/lib/foo": {"type": "tmpfs", "mode": []}},
402+ "[] is not of type 'integer'",
403+ ),
404+ # test_check_layout_mode_range - mode range TODO: can we limit it to integers? why no 0?
405+ (
406+ {"/var/lib/foo": {"type": "tmpfs", "mode": 0}},
407+ "0 is less than the minimum of 1",
408+ ),
409+ (
410+ {"/var/lib/foo": {"type": "tmpfs", "mode": 512}},
411+ "512 is greater than the maximum of 511",
412+ ),
413+ (
414+ {"/var/lib/foo": {"type": "tmpfs", "mode": "778"}},
415+ "'778' is not of type 'integer'",
416+ ),
417+ # test_check_layout_type_bad
418+ ({"/var/lib/foo": {"type": []}}, "[] is not of type 'string'"),
419+ # test_check_layout_type_nonexistent
420+ ({"/var/lib/foo": {"type": "nonexistent"}}, "'nonexistent' is not one of "),
421+ # test_check_layout_user_bad
422+ ({"/var/lib/foo": {"user": []}}, "[] is not of type 'string'"),
423+ # test_check_layout_user_invalid
424+ ({"/var/lib/foo": {"user": "0"}}, " is not allowed for '0'"),
425+ # test_check_layout_group_bad
426+ ({"/var/lib/foo": {"group": []}}, "[] is not of type 'string'"),
427+ ]:
428+ with self.subTest(value=value):
429+ error = error.replace("{value}", str(value)) if error else error
430+ self._test_value("layout", value, error)
431diff --git a/reviewtools/tests/test_sr_lint.py b/reviewtools/tests/test_sr_lint.py
432index 44f88df..14d0a54 100644
433--- a/reviewtools/tests/test_sr_lint.py
434+++ b/reviewtools/tests/test_sr_lint.py
435@@ -4448,352 +4448,6 @@ class TestSnapReviewLint(sr_tests.TestSnapReview):
436 expected_counts = {"info": None, "warn": 0, "error": 1}
437 self.check_results(r, expected_counts)
438
439- def test_check_layout(self):
440- """Test check_layout()"""
441- self.set_test_snap_yaml(
442- "layout",
443- {
444- "/etc/demo": {"bind": "$SNAP_COMMON/etc/demo"},
445- "/etc/demo.cfg": {"symlink": "$SNAP_COMMON/etc/demo.conf"},
446- "/etc/demo.conf": {"bind-file": "$SNAP_COMMON/etc/demo.conf"},
447- "/opt/demo": {"bind": "$SNAP/opt/demo"},
448- "/usr/share/demo": {"bind": "$SNAP/usr/share/demo"},
449- "/var/cache/demo": {"bind": "$SNAP_DATA/var/cache/demo"},
450- "/var/lib/demo": {"bind": "$SNAP_DATA/var/lib/demo"},
451- "/var/lib/foo": {"type": "tmpfs", "mode": "755"},
452- "/var/lib/bar": {
453- "type": "tmpfs",
454- "user": "username",
455- "group": "groupname",
456- "mode": "0755",
457- },
458- },
459- )
460- c = SnapReviewLint(SnapContainer(self.test_name))
461- c.check_layout()
462- r = c.review_report
463- expected_counts = {"info": 23, "warn": 0, "error": 0}
464- self.check_results(r, expected_counts)
465-
466- def test_check_layout_bad(self):
467- """Test check_layout() - bad (list)"""
468- self.set_test_snap_yaml("layout", [])
469- c = SnapReviewLint(SnapContainer(self.test_name))
470- c.check_layout()
471- r = c.review_report
472- expected_counts = {"info": None, "warn": 0, "error": 1}
473- self.check_results(r, expected_counts)
474-
475- def test_check_layout_empty(self):
476- """Test check_layout() - bad (empty)"""
477- self.set_test_snap_yaml("layout", {})
478- c = SnapReviewLint(SnapContainer(self.test_name))
479- c.check_layout()
480- r = c.review_report
481- expected_counts = {"info": None, "warn": 0, "error": 1}
482- self.check_results(r, expected_counts)
483-
484- def test_check_layout_source_target_use_snap_vars(self):
485- """Test check_layout() - source/target use $SNAP..."""
486- self.set_test_snap_yaml("layout", {"$SNAP/db": {"bind": "$SNAP_DATA/db"}})
487- c = SnapReviewLint(SnapContainer(self.test_name))
488- c.check_layout()
489- r = c.review_report
490- expected_counts = {"info": 3, "warn": 0, "error": 0}
491- self.check_results(r, expected_counts)
492-
493- def test_check_layout_target_bad_snap_var(self):
494- """Test check_layout() - bad target (SNAP_USER_COMMON)"""
495- self.set_test_snap_yaml(
496- "layout", {"$SNAP_USER_COMMON/db": {"bind": "$SNAP_DATA/db"}}
497- )
498- c = SnapReviewLint(SnapContainer(self.test_name))
499- c.check_layout()
500- r = c.review_report
501- expected_counts = {"info": None, "warn": 0, "error": 1}
502- self.check_results(r, expected_counts)
503-
504- def test_check_layout_target_bad_val(self):
505- """Test check_layout() - bad target (list)"""
506- self.set_test_snap_yaml("layout", {"/etc/demo": []})
507- c = SnapReviewLint(SnapContainer(self.test_name))
508- c.check_layout()
509- r = c.review_report
510- expected_counts = {"info": None, "warn": 0, "error": 1}
511- self.check_results(r, expected_counts)
512-
513- def test_check_layout_target_bad_empty(self):
514- """Test check_layout() - bad target (empty)"""
515- self.set_test_snap_yaml("layout", {"/etc/demo": {}})
516- c = SnapReviewLint(SnapContainer(self.test_name))
517- c.check_layout()
518- r = c.review_report
519- expected_counts = {"info": None, "warn": 0, "error": 1}
520- self.check_results(r, expected_counts)
521-
522- def test_check_layout_target_bad_too_many(self):
523- """Test check_layout() - bad target (too many)"""
524- self.set_test_snap_yaml(
525- "layout",
526- {
527- "/a/1": {"bind": "$SNAP/1"},
528- "/a/2": {"bind": "$SNAP/2"},
529- "/a/3": {"bind": "$SNAP/3"},
530- "/a/4": {"bind": "$SNAP/4"},
531- "/a/5": {"bind": "$SNAP/5"},
532- "/a/6": {"bind": "$SNAP/6"},
533- "/a/7": {"bind": "$SNAP/7"},
534- "/a/8": {"bind": "$SNAP/8"},
535- "/a/9": {"bind": "$SNAP/9"},
536- "/a/10": {"bind": "$SNAP/10"},
537- "/a/11": {"bind": "$SNAP/11"},
538- "/a/12": {"bind": "$SNAP/12"},
539- "/a/13": {"bind": "$SNAP/13"},
540- "/a/14": {"bind": "$SNAP/14"},
541- "/a/15": {"bind": "$SNAP/15"},
542- "/a/16": {"bind": "$SNAP/16"},
543- "/a/17": {"bind": "$SNAP/17"},
544- "/a/18": {"bind": "$SNAP/19"},
545- "/a/19": {"bind": "$SNAP/19"},
546- "/a/20": {"bind": "$SNAP/20"},
547- "/a/21": {"bind": "$SNAP/21"},
548- "/a/22": {"bind": "$SNAP/22"},
549- "/a/23": {"bind": "$SNAP/23"},
550- "/a/24": {"bind": "$SNAP/24"},
551- "/a/25": {"bind": "$SNAP/25"},
552- "/a/26": {"bind": "$SNAP/26"},
553- "/a/27": {"bind": "$SNAP/27"},
554- "/a/28": {"bind": "$SNAP/28"},
555- "/a/29": {"bind": "$SNAP/29"},
556- "/a/30": {"bind": "$SNAP/30"},
557- "/a/31": {"bind": "$SNAP/31"},
558- "/a/32": {"bind": "$SNAP/32"},
559- "/a/33": {"bind": "$SNAP/33"},
560- "/a/34": {"bind": "$SNAP/34"},
561- "/a/35": {"bind": "$SNAP/35"},
562- "/a/36": {"bind": "$SNAP/36"},
563- "/a/37": {"bind": "$SNAP/37"},
564- "/a/38": {"bind": "$SNAP/38"},
565- "/a/39": {"bind": "$SNAP/39"},
566- "/a/40": {"bind": "$SNAP/40"},
567- "/a/41": {"bind": "$SNAP/41"},
568- "/a/42": {"bind": "$SNAP/42"},
569- "/a/43": {"bind": "$SNAP/43"},
570- "/a/44": {"bind": "$SNAP/44"},
571- "/a/45": {"bind": "$SNAP/45"},
572- "/a/46": {"bind": "$SNAP/46"},
573- "/a/47": {"bind": "$SNAP/47"},
574- "/a/48": {"bind": "$SNAP/48"},
575- "/a/49": {"bind": "$SNAP/49"},
576- "/a/50": {"bind": "$SNAP/50"},
577- "/a/51": {"bind": "$SNAP/51"},
578- "/a/52": {"bind": "$SNAP/52"},
579- "/a/53": {"bind": "$SNAP/53"},
580- "/a/54": {"bind": "$SNAP/54"},
581- "/a/55": {"bind": "$SNAP/55"},
582- "/a/56": {"bind": "$SNAP/56"},
583- "/a/57": {"bind": "$SNAP/57"},
584- "/a/58": {"bind": "$SNAP/58"},
585- "/a/59": {"bind": "$SNAP/59"},
586- "/a/60": {"bind": "$SNAP/60"},
587- "/a/61": {"bind": "$SNAP/61"},
588- "/a/62": {"bind": "$SNAP/62"},
589- "/a/63": {"bind": "$SNAP/63"},
590- "/a/64": {"bind": "$SNAP/64"},
591- "/a/65": {"bind": "$SNAP/65"},
592- "/a/66": {"bind": "$SNAP/66"},
593- "/a/67": {"bind": "$SNAP/67"},
594- "/a/68": {"bind": "$SNAP/68"},
595- "/a/69": {"bind": "$SNAP/69"},
596- "/a/70": {"bind": "$SNAP/70"},
597- "/a/71": {"bind": "$SNAP/71"},
598- "/a/72": {"bind": "$SNAP/72"},
599- "/a/73": {"bind": "$SNAP/73"},
600- "/a/74": {"bind": "$SNAP/74"},
601- "/a/75": {"bind": "$SNAP/75"},
602- "/a/76": {"bind": "$SNAP/76"},
603- "/a/77": {"bind": "$SNAP/77"},
604- "/a/78": {"bind": "$SNAP/78"},
605- "/a/79": {"bind": "$SNAP/79"},
606- "/a/80": {"bind": "$SNAP/80"},
607- "/a/81": {"bind": "$SNAP/81"},
608- "/a/82": {"bind": "$SNAP/82"},
609- "/a/83": {"bind": "$SNAP/83"},
610- "/a/84": {"bind": "$SNAP/84"},
611- "/a/85": {"bind": "$SNAP/85"},
612- "/a/86": {"bind": "$SNAP/86"},
613- "/a/87": {"bind": "$SNAP/87"},
614- "/a/88": {"bind": "$SNAP/88"},
615- "/a/89": {"bind": "$SNAP/89"},
616- "/a/90": {"bind": "$SNAP/90"},
617- "/a/91": {"bind": "$SNAP/91"},
618- "/a/92": {"bind": "$SNAP/92"},
619- "/a/93": {"bind": "$SNAP/93"},
620- "/a/94": {"bind": "$SNAP/94"},
621- "/a/95": {"bind": "$SNAP/95"},
622- "/a/96": {"bind": "$SNAP/96"},
623- "/a/97": {"bind": "$SNAP/97"},
624- "/a/98": {"bind": "$SNAP/98"},
625- "/a/99": {"bind": "$SNAP/99"},
626- "/a/100": {"bind": "$SNAP/100"},
627- "/a/101": {"bind": "$SNAP/101"},
628- },
629- )
630- c = SnapReviewLint(SnapContainer(self.test_name))
631- c.check_layout()
632- r = c.review_report
633- expected_counts = {"info": None, "warn": 0, "error": 1}
634- self.check_results(r, expected_counts)
635-
636- def test_check_layout_target_bad(self):
637- """Test check_layout() - bad target (normpath)"""
638- self.set_test_snap_yaml(
639- "layout", {"/etc/../demo": {"bind": "$SNAP_COMMON/etc/demo"}}
640- )
641- c = SnapReviewLint(SnapContainer(self.test_name))
642- c.check_layout()
643- r = c.review_report
644- expected_counts = {"info": None, "warn": 0, "error": 1}
645- self.check_results(r, expected_counts)
646-
647- def test_check_layout_target_bad_prefix(self):
648- """Test check_layout() - bad target (prefix)"""
649- for prefix in [
650- "/proc/cmdline",
651- "/lib/firmware/foo",
652- "/usr/lib/firmware",
653- "/run",
654- "/var/run",
655- ]:
656- self.set_test_snap_yaml("layout", {prefix: {"bind": "$SNAP/etc/demo"}})
657- c = SnapReviewLint(SnapContainer(self.test_name))
658- c.check_layout()
659- r = c.review_report
660- expected_counts = {"info": None, "warn": 0, "error": 1}
661- self.check_results(r, expected_counts)
662-
663- def test_check_layout_source_bad_prefix(self):
664- """Test check_layout() - bad source (prefix)"""
665- self.set_test_snap_yaml("layout", {"/etc/demo": {"bind": "/bad/etc/demo"}})
666- c = SnapReviewLint(SnapContainer(self.test_name))
667- c.check_layout()
668- r = c.review_report
669- expected_counts = {"info": None, "warn": 0, "error": 1}
670- self.check_results(r, expected_counts)
671-
672- def test_check_layout_source_bad_prefix_home(self):
673- """Test check_layout() - bad source (prefix $HOME)"""
674- self.set_test_snap_yaml(
675- "layout", {"/var/tmp/other": {"bind": "$HOME/snap/other"}}
676- )
677- c = SnapReviewLint(SnapContainer(self.test_name))
678- c.check_layout()
679- r = c.review_report
680- expected_counts = {"info": None, "warn": 0, "error": 1}
681- self.check_results(r, expected_counts)
682-
683- def test_check_layout_source_bad_val(self):
684- """Test check_layout() - bad source (list)"""
685- self.set_test_snap_yaml("layout", {"/etc/demo": {"bind": []}})
686- c = SnapReviewLint(SnapContainer(self.test_name))
687- c.check_layout()
688- r = c.review_report
689- expected_counts = {"info": None, "warn": 0, "error": 1}
690- self.check_results(r, expected_counts)
691-
692- def test_check_layout_source_bad(self):
693- """Test check_layout() - bad source (normpath)"""
694- self.set_test_snap_yaml(
695- "layout", {"/etc/demo": {"bind": "$SNAP_COMMON/etc/../demo"}}
696- )
697- c = SnapReviewLint(SnapContainer(self.test_name))
698- c.check_layout()
699- r = c.review_report
700- expected_counts = {"info": None, "warn": 0, "error": 1}
701- self.check_results(r, expected_counts)
702-
703- def test_check_layout_source_bad_type(self):
704- """Test check_layout() - bad source (type)"""
705- self.set_test_snap_yaml(
706- "layout", {"/etc/demo": {"nonexistent": "$SNAP_COMMON/etc/demo"}}
707- )
708- c = SnapReviewLint(SnapContainer(self.test_name))
709- c.check_layout()
710- r = c.review_report
711- expected_counts = {"info": None, "warn": 0, "error": 1}
712- self.check_results(r, expected_counts)
713-
714- def test_check_layout_mode_bad(self):
715- """Test check_layout() - mode bad"""
716- self.set_test_snap_yaml(
717- "layout", {"/var/lib/foo": {"type": "tmpfs", "mode": []}}
718- )
719- c = SnapReviewLint(SnapContainer(self.test_name))
720- c.check_layout()
721- r = c.review_report
722- expected_counts = {"info": None, "warn": 0, "error": 1}
723- self.check_results(r, expected_counts)
724-
725- def test_check_layout_mode_range(self):
726- """Test check_layout() - mode range"""
727- self.set_test_snap_yaml(
728- "layout",
729- {
730- "/var/lib/foo": {"type": "tmpfs", "mode": 0},
731- "/var/lib/bar": {"type": "tmpfs", "mode": "778"},
732- },
733- )
734- c = SnapReviewLint(SnapContainer(self.test_name))
735- c.check_layout()
736- r = c.review_report
737- expected_counts = {"info": None, "warn": 0, "error": 2}
738- self.check_results(r, expected_counts)
739-
740- def test_check_layout_type_bad(self):
741- """Test check_layout() - type bad"""
742- self.set_test_snap_yaml("layout", {"/var/lib/foo": {"type": []}})
743- c = SnapReviewLint(SnapContainer(self.test_name))
744- c.check_layout()
745- r = c.review_report
746- expected_counts = {"info": None, "warn": 0, "error": 1}
747- self.check_results(r, expected_counts)
748-
749- def test_check_layout_type_nonexistent(self):
750- """Test check_layout() - type nonexistent"""
751- self.set_test_snap_yaml("layout", {"/var/lib/foo": {"type": "nonexistent"}})
752- c = SnapReviewLint(SnapContainer(self.test_name))
753- c.check_layout()
754- r = c.review_report
755- expected_counts = {"info": None, "warn": 0, "error": 1}
756- self.check_results(r, expected_counts)
757-
758- def test_check_layout_user_bad(self):
759- """Test check_layout() - user bad"""
760- self.set_test_snap_yaml("layout", {"/var/lib/foo": {"user": []}})
761- c = SnapReviewLint(SnapContainer(self.test_name))
762- c.check_layout()
763- r = c.review_report
764- expected_counts = {"info": None, "warn": 0, "error": 1}
765- self.check_results(r, expected_counts)
766-
767- def test_check_layout_user_invalid(self):
768- """Test check_layout() - user invalid"""
769- self.set_test_snap_yaml("layout", {"/var/lib/foo": {"user": "0"}})
770- c = SnapReviewLint(SnapContainer(self.test_name))
771- c.check_layout()
772- r = c.review_report
773- expected_counts = {"info": None, "warn": 0, "error": 1}
774- self.check_results(r, expected_counts)
775-
776- def test_check_layout_group_bad(self):
777- """Test check_layout() - group bad"""
778- self.set_test_snap_yaml("layout", {"/var/lib/foo": {"group": []}})
779- c = SnapReviewLint(SnapContainer(self.test_name))
780- c.check_layout()
781- r = c.review_report
782- expected_counts = {"info": None, "warn": 0, "error": 1}
783- self.check_results(r, expected_counts)
784-
785 def test_check_apps_install_mode(self):
786 """Test check_apps_install_mode()"""
787 self.set_test_snap_yaml("apps", {"foo": {"install-mode": "enable"}})
788diff --git a/tests/test.sh.expected b/tests/test.sh.expected
789index 7e394ec..e74cc2c 100644
790--- a/tests/test.sh.expected
791+++ b/tests/test.sh.expected
792@@ -4852,18 +4852,6 @@ nix-example-jormungandr_f7xva0vh9fzv20vhyr121yd6ahplqh9v_amd64.snap: pass
793 "manual_review": false,
794 "text": "OK"
795 },
796- "lint-snap-v2:layout": {
797- "manual_review": false,
798- "text": "OK"
799- },
800- "lint-snap-v2:layout_source:/nix:$SNAP/nix": {
801- "manual_review": false,
802- "text": "OK"
803- },
804- "lint-snap-v2:layout_target:/nix": {
805- "manual_review": false,
806- "text": "OK"
807- },
808 "lint-snap-v2:snap_type_redflag": {
809 "manual_review": false,
810 "text": "OK"
811@@ -5015,18 +5003,6 @@ nix-example-jormungandr_f7xva0vh9fzv20vhyr121yd6ahplqh9v_amd64.snap: pass
812 "manual_review": false,
813 "text": "OK"
814 },
815- "lint-snap-v2:layout": {
816- "manual_review": false,
817- "text": "OK"
818- },
819- "lint-snap-v2:layout_source:/nix:$SNAP/nix": {
820- "manual_review": false,
821- "text": "OK"
822- },
823- "lint-snap-v2:layout_target:/nix": {
824- "manual_review": false,
825- "text": "OK"
826- },
827 "lint-snap-v2:snap_type_redflag": {
828 "manual_review": false,
829 "text": "OK"
830@@ -5268,18 +5244,6 @@ nix-example_g7qmi8r4qwws6fhwschfb8aib5wl0x1q_amd64.snap: pass
831 "manual_review": false,
832 "text": "OK"
833 },
834- "lint-snap-v2:layout": {
835- "manual_review": false,
836- "text": "OK"
837- },
838- "lint-snap-v2:layout_source:/nix:$SNAP/nix": {
839- "manual_review": false,
840- "text": "OK"
841- },
842- "lint-snap-v2:layout_target:/nix": {
843- "manual_review": false,
844- "text": "OK"
845- },
846 "lint-snap-v2:meta_gui_desktop": {
847 "manual_review": false,
848 "text": "desktop interfaces (x11) specified without a corresponding meta/gui/*.desktop file. If your application does not require a desktop file, you may ignore this. Otherwise, if using snapcraft, please see https://snapcraft.io/docs/build-snaps/metadata#fixed-assets or provide a desktop file in meta/gui/*.desktop (it should reference one of the 'apps' from your snapcraft/snap.yaml)."
849@@ -5516,18 +5480,6 @@ nix-example_g7qmi8r4qwws6fhwschfb8aib5wl0x1q_amd64.snap: pass
850 "manual_review": false,
851 "text": "OK"
852 },
853- "lint-snap-v2:layout": {
854- "manual_review": false,
855- "text": "OK"
856- },
857- "lint-snap-v2:layout_source:/nix:$SNAP/nix": {
858- "manual_review": false,
859- "text": "OK"
860- },
861- "lint-snap-v2:layout_target:/nix": {
862- "manual_review": false,
863- "text": "OK"
864- },
865 "lint-snap-v2:meta_gui_desktop": {
866 "manual_review": false,
867 "text": "desktop interfaces (x11) specified without a corresponding meta/gui/*.desktop file. If your application does not require a desktop file, you may ignore this. Otherwise, if using snapcraft, please see https://snapcraft.io/docs/build-snaps/metadata#fixed-assets or provide a desktop file in meta/gui/*.desktop (it should reference one of the 'apps' from your snapcraft/snap.yaml)."
868@@ -35331,78 +35283,6 @@ test-snapd-layout_1.0_all.snap: pass
869 "manual_review": false,
870 "text": "OK"
871 },
872- "lint-snap-v2:layout": {
873- "manual_review": false,
874- "text": "OK"
875- },
876- "lint-snap-v2:layout:/opt/foo:mode": {
877- "manual_review": false,
878- "text": "OK"
879- },
880- "lint-snap-v2:layout:/opt/foo:type": {
881- "manual_review": false,
882- "text": "OK"
883- },
884- "lint-snap-v2:layout_source:/etc/demo.cfg:$SNAP_COMMON/etc/demo.conf": {
885- "manual_review": false,
886- "text": "OK"
887- },
888- "lint-snap-v2:layout_source:/etc/demo.conf:$SNAP_COMMON/etc/demo.conf": {
889- "manual_review": false,
890- "text": "OK"
891- },
892- "lint-snap-v2:layout_source:/etc/demo:$SNAP_COMMON/etc/demo": {
893- "manual_review": false,
894- "text": "OK"
895- },
896- "lint-snap-v2:layout_source:/opt/demo:$SNAP/opt/demo": {
897- "manual_review": false,
898- "text": "OK"
899- },
900- "lint-snap-v2:layout_source:/usr/share/demo:$SNAP/usr/share/demo": {
901- "manual_review": false,
902- "text": "OK"
903- },
904- "lint-snap-v2:layout_source:/var/cache/demo:$SNAP_DATA/var/cache/demo": {
905- "manual_review": false,
906- "text": "OK"
907- },
908- "lint-snap-v2:layout_source:/var/lib/demo:$SNAP_DATA/var/lib/demo": {
909- "manual_review": false,
910- "text": "OK"
911- },
912- "lint-snap-v2:layout_target:/etc/demo": {
913- "manual_review": false,
914- "text": "OK"
915- },
916- "lint-snap-v2:layout_target:/etc/demo.cfg": {
917- "manual_review": false,
918- "text": "OK"
919- },
920- "lint-snap-v2:layout_target:/etc/demo.conf": {
921- "manual_review": false,
922- "text": "OK"
923- },
924- "lint-snap-v2:layout_target:/opt/demo": {
925- "manual_review": false,
926- "text": "OK"
927- },
928- "lint-snap-v2:layout_target:/opt/foo": {
929- "manual_review": false,
930- "text": "OK"
931- },
932- "lint-snap-v2:layout_target:/usr/share/demo": {
933- "manual_review": false,
934- "text": "OK"
935- },
936- "lint-snap-v2:layout_target:/var/cache/demo": {
937- "manual_review": false,
938- "text": "OK"
939- },
940- "lint-snap-v2:layout_target:/var/lib/demo": {
941- "manual_review": false,
942- "text": "OK"
943- },
944 "lint-snap-v2:snap_type_redflag": {
945 "manual_review": false,
946 "text": "OK"
947@@ -35522,78 +35402,6 @@ test-snapd-layout_1.0_all.snap: pass
948 "manual_review": false,
949 "text": "OK"
950 },
951- "lint-snap-v2:layout": {
952- "manual_review": false,
953- "text": "OK"
954- },
955- "lint-snap-v2:layout:/opt/foo:mode": {
956- "manual_review": false,
957- "text": "OK"
958- },
959- "lint-snap-v2:layout:/opt/foo:type": {
960- "manual_review": false,
961- "text": "OK"
962- },
963- "lint-snap-v2:layout_source:/etc/demo.cfg:$SNAP_COMMON/etc/demo.conf": {
964- "manual_review": false,
965- "text": "OK"
966- },
967- "lint-snap-v2:layout_source:/etc/demo.conf:$SNAP_COMMON/etc/demo.conf": {
968- "manual_review": false,
969- "text": "OK"
970- },
971- "lint-snap-v2:layout_source:/etc/demo:$SNAP_COMMON/etc/demo": {
972- "manual_review": false,
973- "text": "OK"
974- },
975- "lint-snap-v2:layout_source:/opt/demo:$SNAP/opt/demo": {
976- "manual_review": false,
977- "text": "OK"
978- },
979- "lint-snap-v2:layout_source:/usr/share/demo:$SNAP/usr/share/demo": {
980- "manual_review": false,
981- "text": "OK"
982- },
983- "lint-snap-v2:layout_source:/var/cache/demo:$SNAP_DATA/var/cache/demo": {
984- "manual_review": false,
985- "text": "OK"
986- },
987- "lint-snap-v2:layout_source:/var/lib/demo:$SNAP_DATA/var/lib/demo": {
988- "manual_review": false,
989- "text": "OK"
990- },
991- "lint-snap-v2:layout_target:/etc/demo": {
992- "manual_review": false,
993- "text": "OK"
994- },
995- "lint-snap-v2:layout_target:/etc/demo.cfg": {
996- "manual_review": false,
997- "text": "OK"
998- },
999- "lint-snap-v2:layout_target:/etc/demo.conf": {
1000- "manual_review": false,
1001- "text": "OK"
1002- },
1003- "lint-snap-v2:layout_target:/opt/demo": {
1004- "manual_review": false,
1005- "text": "OK"
1006- },
1007- "lint-snap-v2:layout_target:/opt/foo": {
1008- "manual_review": false,
1009- "text": "OK"
1010- },
1011- "lint-snap-v2:layout_target:/usr/share/demo": {
1012- "manual_review": false,
1013- "text": "OK"
1014- },
1015- "lint-snap-v2:layout_target:/var/cache/demo": {
1016- "manual_review": false,
1017- "text": "OK"
1018- },
1019- "lint-snap-v2:layout_target:/var/lib/demo": {
1020- "manual_review": false,
1021- "text": "OK"
1022- },
1023 "lint-snap-v2:snap_type_redflag": {
1024 "manual_review": false,
1025 "text": "OK"

Subscribers

People subscribed via source and target branches