github.com/rigado/snapd@v2.42.5-go-mod+incompatible/snap/validate_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package snap_test
    21  
    22  import (
    23  	"fmt"
    24  	"regexp"
    25  	"strconv"
    26  	"strings"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	. "github.com/snapcore/snapd/snap"
    31  
    32  	"github.com/snapcore/snapd/testutil"
    33  )
    34  
    35  type ValidateSuite struct {
    36  	testutil.BaseTest
    37  }
    38  
    39  var _ = Suite(&ValidateSuite{})
    40  
    41  func createSampleApp() *AppInfo {
    42  	socket := &SocketInfo{
    43  		Name:         "sock",
    44  		ListenStream: "$SNAP_COMMON/socket",
    45  	}
    46  	app := &AppInfo{
    47  		Snap: &Info{
    48  			SideInfo: SideInfo{
    49  				RealName: "mysnap",
    50  				Revision: R(20),
    51  			},
    52  		},
    53  		Name:  "foo",
    54  		Plugs: map[string]*PlugInfo{"network-bind": {}},
    55  		Sockets: map[string]*SocketInfo{
    56  			"sock": socket,
    57  		},
    58  	}
    59  	socket.App = app
    60  	return app
    61  }
    62  
    63  func (s *ValidateSuite) SetUpTest(c *C) {
    64  	s.BaseTest.SetUpTest(c)
    65  	s.BaseTest.AddCleanup(MockSanitizePlugsSlots(func(snapInfo *Info) {}))
    66  }
    67  
    68  func (s *ValidateSuite) TearDownTest(c *C) {
    69  	s.BaseTest.TearDownTest(c)
    70  }
    71  
    72  func (s *ValidateSuite) TestValidateVersion(c *C) {
    73  	validVersions := []string{
    74  		"0", "v1.0", "0.12+16.04.20160126-0ubuntu1",
    75  		"1:6.0.1+r16-3", "1.0~", "1.0+", "README.~1~",
    76  		"a+++++++++++++++++++++++++++++++",
    77  		"AZaz:.+~-123",
    78  	}
    79  	for _, version := range validVersions {
    80  		err := ValidateVersion(version)
    81  		c.Assert(err, IsNil)
    82  	}
    83  	invalidVersionsTable := [][2]string{
    84  		{"~foo", `must start with an ASCII alphanumeric (and not '~')`},
    85  		{"+foo", `must start with an ASCII alphanumeric (and not '+')`},
    86  
    87  		{"foo:", `must end with an ASCII alphanumeric or one of '+' or '~' (and not ':')`},
    88  		{"foo.", `must end with an ASCII alphanumeric or one of '+' or '~' (and not '.')`},
    89  		{"foo-", `must end with an ASCII alphanumeric or one of '+' or '~' (and not '-')`},
    90  
    91  		{"horrible_underscores", `contains invalid characters: "_"`},
    92  		{"foo($bar^baz$)meep", `contains invalid characters: "($", "^", "$)"`},
    93  
    94  		{"árbol", `must be printable, non-whitespace ASCII`},
    95  		{"日本語", `must be printable, non-whitespace ASCII`},
    96  		{"한글", `must be printable, non-whitespace ASCII`},
    97  		{"ру́сский язы́к", `must be printable, non-whitespace ASCII`},
    98  
    99  		{"~foo$bar:", `must start with an ASCII alphanumeric (and not '~'),` +
   100  			` must end with an ASCII alphanumeric or one of '+' or '~' (and not ':'),` +
   101  			` and contains invalid characters: "$"`},
   102  	}
   103  	for _, t := range invalidVersionsTable {
   104  		version, reason := t[0], t[1]
   105  		err := ValidateVersion(version)
   106  		c.Assert(err, NotNil)
   107  		c.Assert(err.Error(), Equals, fmt.Sprintf("invalid snap version %s: %s", strconv.QuoteToASCII(version), reason))
   108  	}
   109  	// version cannot be empty
   110  	c.Assert(ValidateVersion(""), ErrorMatches, `invalid snap version: cannot be empty`)
   111  	// version length cannot be >32
   112  	c.Assert(ValidateVersion("this-version-is-a-little-bit-older"), ErrorMatches,
   113  		`invalid snap version "this-version-is-a-little-bit-older": cannot be longer than 32 characters \(got: 34\)`)
   114  }
   115  
   116  func (s *ValidateSuite) TestValidateLicense(c *C) {
   117  	validLicenses := []string{
   118  		"GPL-3.0", "(GPL-3.0)", "GPL-3.0+", "GPL-3.0 AND GPL-2.0", "GPL-3.0 OR GPL-2.0", "MIT OR (GPL-3.0 AND GPL-2.0)", "MIT OR(GPL-3.0 AND GPL-2.0)",
   119  	}
   120  	for _, epoch := range validLicenses {
   121  		err := ValidateLicense(epoch)
   122  		c.Assert(err, IsNil)
   123  	}
   124  	invalidLicenses := []string{
   125  		"GPL~3.0", "3.0-GPL", "(GPL-3.0", "(GPL-3.0))", "GPL-3.0++", "+GPL-3.0", "GPL-3.0 GPL-2.0",
   126  	}
   127  	for _, epoch := range invalidLicenses {
   128  		err := ValidateLicense(epoch)
   129  		c.Assert(err, NotNil)
   130  	}
   131  }
   132  
   133  func (s *ValidateSuite) TestValidateHook(c *C) {
   134  	validHooks := []*HookInfo{
   135  		{Name: "a"},
   136  		{Name: "aaa"},
   137  		{Name: "a-a"},
   138  		{Name: "aa-a"},
   139  		{Name: "a-aa"},
   140  		{Name: "a-b-c"},
   141  		{Name: "valid", CommandChain: []string{"valid"}},
   142  	}
   143  	for _, hook := range validHooks {
   144  		err := ValidateHook(hook)
   145  		c.Assert(err, IsNil)
   146  	}
   147  	invalidHooks := []*HookInfo{
   148  		{Name: ""},
   149  		{Name: "a a"},
   150  		{Name: "a--a"},
   151  		{Name: "-a"},
   152  		{Name: "a-"},
   153  		{Name: "0"},
   154  		{Name: "123"},
   155  		{Name: "123abc"},
   156  		{Name: "日本語"},
   157  	}
   158  	for _, hook := range invalidHooks {
   159  		err := ValidateHook(hook)
   160  		c.Assert(err, ErrorMatches, `invalid hook name: ".*"`)
   161  	}
   162  	invalidHooks = []*HookInfo{
   163  		{Name: "valid", CommandChain: []string{"in'valid"}},
   164  		{Name: "valid", CommandChain: []string{"in valid"}},
   165  	}
   166  	for _, hook := range invalidHooks {
   167  		err := ValidateHook(hook)
   168  		c.Assert(err, ErrorMatches, `hook command-chain contains illegal.*`)
   169  	}
   170  }
   171  
   172  // ValidateApp
   173  
   174  func (s *ValidateSuite) TestValidateAppSockets(c *C) {
   175  	app := createSampleApp()
   176  	app.Sockets["sock"].SocketMode = 0600
   177  	c.Check(ValidateApp(app), IsNil)
   178  }
   179  
   180  func (s *ValidateSuite) TestValidateAppSocketsEmptyPermsOk(c *C) {
   181  	app := createSampleApp()
   182  	c.Check(ValidateApp(app), IsNil)
   183  }
   184  
   185  func (s *ValidateSuite) TestValidateAppSocketsWrongPerms(c *C) {
   186  	app := createSampleApp()
   187  	app.Sockets["sock"].SocketMode = 1234
   188  	err := ValidateApp(app)
   189  	c.Assert(err, ErrorMatches, `invalid definition of socket "sock": cannot use mode: 2322`)
   190  }
   191  
   192  func (s *ValidateSuite) TestValidateAppSocketsMissingNetworkBindPlug(c *C) {
   193  	app := createSampleApp()
   194  	delete(app.Plugs, "network-bind")
   195  	err := ValidateApp(app)
   196  	c.Assert(
   197  		err, ErrorMatches,
   198  		`"network-bind" interface plug is required when sockets are used`)
   199  }
   200  
   201  func (s *ValidateSuite) TestValidateAppSocketsEmptyListenStream(c *C) {
   202  	app := createSampleApp()
   203  	app.Sockets["sock"].ListenStream = ""
   204  	err := ValidateApp(app)
   205  	c.Assert(err, ErrorMatches, `invalid definition of socket "sock": "listen-stream" is not defined`)
   206  }
   207  
   208  func (s *ValidateSuite) TestValidateAppSocketsInvalidName(c *C) {
   209  	app := createSampleApp()
   210  	app.Sockets["sock"].Name = "invalid name"
   211  	err := ValidateApp(app)
   212  	c.Assert(err, ErrorMatches, `invalid definition of socket "invalid name": invalid socket name: "invalid name"`)
   213  }
   214  
   215  func (s *ValidateSuite) TestValidateAppSocketsValidListenStreamAddresses(c *C) {
   216  	app := createSampleApp()
   217  	validListenAddresses := []string{
   218  		// socket paths using variables as prefix
   219  		"$SNAP_DATA/my.socket",
   220  		"$SNAP_COMMON/my.socket",
   221  		"$XDG_RUNTIME_DIR/my.socket",
   222  		// abstract sockets
   223  		"@snap.mysnap.my.socket",
   224  		// addresses and ports
   225  		"1",
   226  		"1023",
   227  		"1024",
   228  		"65535",
   229  		"127.0.0.1:8080",
   230  		"[::]:8080",
   231  		"[::1]:8080",
   232  	}
   233  	socket := app.Sockets["sock"]
   234  	for _, validAddress := range validListenAddresses {
   235  		socket.ListenStream = validAddress
   236  		err := ValidateApp(app)
   237  		c.Check(err, IsNil, Commentf(validAddress))
   238  	}
   239  }
   240  
   241  func (s *ValidateSuite) TestValidateAppSocketsInvalidListenStreamPath(c *C) {
   242  	app := createSampleApp()
   243  	invalidListenAddresses := []string{
   244  		// socket paths out of the snap dirs
   245  		"/some/path/my.socket",
   246  		"/var/snap/mysnap/20/my.socket", // path is correct but has hardcoded prefix
   247  	}
   248  	socket := app.Sockets["sock"]
   249  	for _, invalidAddress := range invalidListenAddresses {
   250  		socket.ListenStream = invalidAddress
   251  		err := ValidateApp(app)
   252  		c.Assert(err, ErrorMatches, `invalid definition of socket "sock": invalid "listen-stream": must have a prefix of .*`)
   253  	}
   254  }
   255  
   256  func (s *ValidateSuite) TestValidateAppSocketsInvalidListenStreamPathContainsDots(c *C) {
   257  	app := createSampleApp()
   258  	app.Sockets["sock"].ListenStream = "$SNAP/../some.path"
   259  	err := ValidateApp(app)
   260  	c.Assert(
   261  		err, ErrorMatches,
   262  		`invalid definition of socket "sock": invalid "listen-stream": "\$SNAP/../some.path" should be written as "some.path"`)
   263  }
   264  
   265  func (s *ValidateSuite) TestValidateAppSocketsInvalidListenStreamPathPrefix(c *C) {
   266  	app := createSampleApp()
   267  	invalidListenAddresses := []string{
   268  		"$SNAP/my.socket", // snap dir is not writable
   269  		"$SOMEVAR/my.socket",
   270  	}
   271  	socket := app.Sockets["sock"]
   272  	for _, invalidAddress := range invalidListenAddresses {
   273  		socket.ListenStream = invalidAddress
   274  		err := ValidateApp(app)
   275  		c.Assert(
   276  			err, ErrorMatches,
   277  			`invalid definition of socket "sock": invalid "listen-stream": must have a prefix of \$SNAP_DATA, \$SNAP_COMMON or \$XDG_RUNTIME_DIR`)
   278  	}
   279  }
   280  
   281  func (s *ValidateSuite) TestValidateAppSocketsInvalidListenStreamAbstractSocket(c *C) {
   282  	app := createSampleApp()
   283  	invalidListenAddresses := []string{
   284  		"@snap.mysnap",
   285  		"@snap.mysnap\000.foo",
   286  		"@snap.notmysnap.my.socket",
   287  		"@some.other.name",
   288  		"@snap.myappiswrong.foo",
   289  	}
   290  	socket := app.Sockets["sock"]
   291  	for _, invalidAddress := range invalidListenAddresses {
   292  		socket.ListenStream = invalidAddress
   293  		err := ValidateApp(app)
   294  		c.Assert(err, ErrorMatches, `invalid definition of socket "sock": path for "listen-stream" must be prefixed with.*`)
   295  	}
   296  }
   297  
   298  func (s *ValidateSuite) TestValidateAppSocketsInvalidListenStreamAddress(c *C) {
   299  	app := createSampleApp()
   300  	invalidListenAddresses := []string{
   301  		"10.0.1.1:8080",
   302  		"[fafa::baba]:8080",
   303  		"127.0.0.1\000:8080",
   304  		"127.0.0.1::8080",
   305  	}
   306  	socket := app.Sockets["sock"]
   307  	for _, invalidAddress := range invalidListenAddresses {
   308  		socket.ListenStream = invalidAddress
   309  		err := ValidateApp(app)
   310  		c.Assert(err, ErrorMatches, `invalid definition of socket "sock": invalid "listen-stream" address ".*", must be one of: 127\.0\.0\.1, \[::1\], \[::\]`)
   311  	}
   312  }
   313  
   314  func (s *ValidateSuite) TestValidateAppSocketsInvalidListenStreamPort(c *C) {
   315  	app := createSampleApp()
   316  	invalidPorts := []string{
   317  		"0",
   318  		"66536",
   319  		"-8080",
   320  		"12312345345",
   321  		"[::]:-123",
   322  		"[::1]:3452345234",
   323  		"invalid",
   324  		"[::]:invalid",
   325  	}
   326  	socket := app.Sockets["sock"]
   327  	for _, invalidPort := range invalidPorts {
   328  		socket.ListenStream = invalidPort
   329  		err := ValidateApp(app)
   330  		c.Assert(err, ErrorMatches, `invalid definition of socket "sock": invalid "listen-stream" port number.*`)
   331  	}
   332  }
   333  
   334  func (s *ValidateSuite) TestAppWhitelistSimple(c *C) {
   335  	c.Check(ValidateApp(&AppInfo{Name: "foo", Command: "foo"}), IsNil)
   336  	c.Check(ValidateApp(&AppInfo{Name: "foo", StopCommand: "foo"}), IsNil)
   337  	c.Check(ValidateApp(&AppInfo{Name: "foo", PostStopCommand: "foo"}), IsNil)
   338  }
   339  
   340  func (s *ValidateSuite) TestAppWhitelistWithVars(c *C) {
   341  	c.Check(ValidateApp(&AppInfo{Name: "foo", Command: "foo $SNAP_DATA"}), IsNil)
   342  	c.Check(ValidateApp(&AppInfo{Name: "foo", StopCommand: "foo $SNAP_DATA"}), IsNil)
   343  	c.Check(ValidateApp(&AppInfo{Name: "foo", PostStopCommand: "foo $SNAP_DATA"}), IsNil)
   344  }
   345  
   346  func (s *ValidateSuite) TestAppWhitelistIllegal(c *C) {
   347  	c.Check(ValidateApp(&AppInfo{Name: "x\n"}), NotNil)
   348  	c.Check(ValidateApp(&AppInfo{Name: "test!me"}), NotNil)
   349  	c.Check(ValidateApp(&AppInfo{Name: "test'me"}), NotNil)
   350  	c.Check(ValidateApp(&AppInfo{Name: "foo", Command: "foo\n"}), NotNil)
   351  	c.Check(ValidateApp(&AppInfo{Name: "foo", StopCommand: "foo\n"}), NotNil)
   352  	c.Check(ValidateApp(&AppInfo{Name: "foo", PostStopCommand: "foo\n"}), NotNil)
   353  	c.Check(ValidateApp(&AppInfo{Name: "foo", BusName: "foo\n"}), NotNil)
   354  	c.Check(ValidateApp(&AppInfo{Name: "foo", CommandChain: []string{"bar'baz"}}), NotNil)
   355  	c.Check(ValidateApp(&AppInfo{Name: "foo", CommandChain: []string{"bar baz"}}), NotNil)
   356  }
   357  
   358  func (s *ValidateSuite) TestAppDaemonValue(c *C) {
   359  	for _, t := range []struct {
   360  		daemon string
   361  		ok     bool
   362  	}{
   363  		// good
   364  		{"", true},
   365  		{"simple", true},
   366  		{"forking", true},
   367  		{"oneshot", true},
   368  		{"dbus", true},
   369  		{"notify", true},
   370  		// bad
   371  		{"invalid-thing", false},
   372  	} {
   373  		if t.ok {
   374  			c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: t.daemon}), IsNil)
   375  		} else {
   376  			c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: t.daemon}), ErrorMatches, fmt.Sprintf(`"daemon" field contains invalid value %q`, t.daemon))
   377  		}
   378  	}
   379  }
   380  
   381  func (s *ValidateSuite) TestAppStopMode(c *C) {
   382  	// check services
   383  	for _, t := range []struct {
   384  		stopMode StopModeType
   385  		ok       bool
   386  	}{
   387  		// good
   388  		{"", true},
   389  		{"sigterm", true},
   390  		{"sigterm-all", true},
   391  		{"sighup", true},
   392  		{"sighup-all", true},
   393  		{"sigusr1", true},
   394  		{"sigusr1-all", true},
   395  		{"sigusr2", true},
   396  		{"sigusr2-all", true},
   397  		// bad
   398  		{"invalid-thing", false},
   399  	} {
   400  		if t.ok {
   401  			c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", StopMode: t.stopMode}), IsNil)
   402  		} else {
   403  			c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", StopMode: t.stopMode}), ErrorMatches, fmt.Sprintf(`"stop-mode" field contains invalid value %q`, t.stopMode))
   404  		}
   405  	}
   406  
   407  	// non-services cannot have a stop-mode
   408  	err := ValidateApp(&AppInfo{Name: "foo", Daemon: "", StopMode: "sigterm"})
   409  	c.Check(err, ErrorMatches, `"stop-mode" cannot be used for "foo", only for services`)
   410  }
   411  
   412  func (s *ValidateSuite) TestAppRefreshMode(c *C) {
   413  	// check services
   414  	for _, t := range []struct {
   415  		refreshMode string
   416  		ok          bool
   417  	}{
   418  		// good
   419  		{"", true},
   420  		{"endure", true},
   421  		{"restart", true},
   422  		// bad
   423  		{"invalid-thing", false},
   424  	} {
   425  		if t.ok {
   426  			c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", RefreshMode: t.refreshMode}), IsNil)
   427  		} else {
   428  			c.Check(ValidateApp(&AppInfo{Name: "foo", Daemon: "simple", RefreshMode: t.refreshMode}), ErrorMatches, fmt.Sprintf(`"refresh-mode" field contains invalid value %q`, t.refreshMode))
   429  		}
   430  	}
   431  
   432  	// non-services cannot have a refresh-mode
   433  	err := ValidateApp(&AppInfo{Name: "foo", Daemon: "", RefreshMode: "endure"})
   434  	c.Check(err, ErrorMatches, `"refresh-mode" cannot be used for "foo", only for services`)
   435  }
   436  
   437  func (s *ValidateSuite) TestAppWhitelistError(c *C) {
   438  	err := ValidateApp(&AppInfo{Name: "foo", Command: "x\n"})
   439  	c.Assert(err, NotNil)
   440  	c.Check(err.Error(), Equals, `app description field 'command' contains illegal "x\n" (legal: '^[A-Za-z0-9/. _#:$-]*$')`)
   441  }
   442  
   443  // Validate
   444  
   445  func (s *ValidateSuite) TestDetectIllegalYamlBinaries(c *C) {
   446  	info, err := InfoFromSnapYaml([]byte(`name: foo
   447  version: 1.0
   448  apps:
   449   tes!me:
   450     command: someething
   451  `))
   452  	c.Assert(err, IsNil)
   453  
   454  	err = Validate(info)
   455  	c.Check(err, NotNil)
   456  }
   457  
   458  func (s *ValidateSuite) TestDetectIllegalYamlService(c *C) {
   459  	info, err := InfoFromSnapYaml([]byte(`name: foo
   460  version: 1.0
   461  apps:
   462   tes!me:
   463     command: something
   464     daemon: forking
   465  `))
   466  	c.Assert(err, IsNil)
   467  
   468  	err = Validate(info)
   469  	c.Check(err, NotNil)
   470  }
   471  
   472  func (s *ValidateSuite) TestIllegalSnapName(c *C) {
   473  	info, err := InfoFromSnapYaml([]byte(`name: foo.something
   474  version: 1.0
   475  `))
   476  	c.Assert(err, IsNil)
   477  
   478  	err = Validate(info)
   479  	c.Check(err, ErrorMatches, `invalid snap name: "foo.something"`)
   480  }
   481  
   482  func (s *ValidateSuite) TestValidateChecksName(c *C) {
   483  	info, err := InfoFromSnapYaml([]byte(`
   484  version: 1.0
   485  `))
   486  	c.Assert(err, IsNil)
   487  
   488  	err = Validate(info)
   489  	c.Check(err, ErrorMatches, `snap name cannot be empty`)
   490  }
   491  
   492  func (s *ValidateSuite) TestIllegalSnapEpoch(c *C) {
   493  	_, err := InfoFromSnapYaml([]byte(`name: foo
   494  version: 1.0
   495  epoch: 0*
   496  `))
   497  	c.Assert(err, ErrorMatches, `.*invalid epoch.*`)
   498  }
   499  
   500  func (s *ValidateSuite) TestMissingSnapEpochIsOkay(c *C) {
   501  	info, err := InfoFromSnapYaml([]byte(`name: foo
   502  version: 1.0
   503  `))
   504  	c.Assert(err, IsNil)
   505  	c.Assert(Validate(info), IsNil)
   506  }
   507  
   508  func (s *ValidateSuite) TestIllegalSnapLicense(c *C) {
   509  	info, err := InfoFromSnapYaml([]byte(`name: foo
   510  version: 1.0
   511  license: GPL~3.0
   512  `))
   513  	c.Assert(err, IsNil)
   514  
   515  	err = Validate(info)
   516  	c.Check(err, ErrorMatches, `cannot validate license "GPL~3.0": unknown license: GPL~3.0`)
   517  }
   518  
   519  func (s *ValidateSuite) TestMissingSnapLicenseIsOkay(c *C) {
   520  	info, err := InfoFromSnapYaml([]byte(`name: foo
   521  version: 1.0
   522  `))
   523  	c.Assert(err, IsNil)
   524  	c.Assert(Validate(info), IsNil)
   525  }
   526  
   527  func (s *ValidateSuite) TestIllegalHookName(c *C) {
   528  	hookType := NewHookType(regexp.MustCompile(".*"))
   529  	restore := MockSupportedHookTypes([]*HookType{hookType})
   530  	defer restore()
   531  
   532  	info, err := InfoFromSnapYaml([]byte(`name: foo
   533  version: 1.0
   534  hooks:
   535    123abc:
   536  `))
   537  	c.Assert(err, IsNil)
   538  
   539  	err = Validate(info)
   540  	c.Check(err, ErrorMatches, `invalid hook name: "123abc"`)
   541  }
   542  
   543  func (s *ValidateSuite) TestPlugSlotNamesUnique(c *C) {
   544  	info, err := InfoFromSnapYaml([]byte(`name: snap
   545  version: 0
   546  plugs:
   547   foo:
   548  slots:
   549   foo:
   550  `))
   551  	c.Assert(err, IsNil)
   552  	err = Validate(info)
   553  	c.Check(err, ErrorMatches, `cannot have plug and slot with the same name: "foo"`)
   554  }
   555  
   556  func (s *ValidateSuite) TestIllegalAliasName(c *C) {
   557  	info, err := InfoFromSnapYaml([]byte(`name: foo
   558  version: 1.0
   559  apps:
   560    foo:
   561      aliases: [foo$]
   562  `))
   563  	c.Assert(err, IsNil)
   564  
   565  	err = Validate(info)
   566  	c.Check(err, ErrorMatches, `cannot have "foo\$" as alias name for app "foo" - use only letters, digits, dash, underscore and dot characters`)
   567  }
   568  
   569  func (s *ValidateSuite) TestValidatePlugSlotName(c *C) {
   570  	const yaml1 = `
   571  name: invalid-plugs
   572  version: 1
   573  plugs:
   574    p--lug: null
   575  `
   576  	strk := NewScopedTracker()
   577  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml1), nil, strk)
   578  	c.Assert(err, IsNil)
   579  	c.Assert(info.Plugs, HasLen, 1)
   580  	err = Validate(info)
   581  	c.Assert(err, ErrorMatches, `invalid plug name: "p--lug"`)
   582  
   583  	const yaml2 = `
   584  name: invalid-slots
   585  version: 1
   586  slots:
   587    s--lot: null
   588  `
   589  	strk = NewScopedTracker()
   590  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml2), nil, strk)
   591  	c.Assert(err, IsNil)
   592  	c.Assert(info.Slots, HasLen, 1)
   593  	err = Validate(info)
   594  	c.Assert(err, ErrorMatches, `invalid slot name: "s--lot"`)
   595  
   596  	const yaml3 = `
   597  name: invalid-plugs-iface
   598  version: 1
   599  plugs:
   600    plug:
   601      interface: i--face
   602  `
   603  	strk = NewScopedTracker()
   604  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml3), nil, strk)
   605  	c.Assert(err, IsNil)
   606  	c.Assert(info.Plugs, HasLen, 1)
   607  	err = Validate(info)
   608  	c.Assert(err, ErrorMatches, `invalid interface name "i--face" for plug "plug"`)
   609  
   610  	const yaml4 = `
   611  name: invalid-slots-iface
   612  version: 1
   613  slots:
   614    slot:
   615      interface: i--face
   616  `
   617  	strk = NewScopedTracker()
   618  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml4), nil, strk)
   619  	c.Assert(err, IsNil)
   620  	c.Assert(info.Slots, HasLen, 1)
   621  	err = Validate(info)
   622  	c.Assert(err, ErrorMatches, `invalid interface name "i--face" for slot "slot"`)
   623  }
   624  
   625  func (s *ValidateSuite) TestValidateBaseNone(c *C) {
   626  	const yaml = `name: requires-base
   627  version: 1
   628  base: none
   629  `
   630  	strk := NewScopedTracker()
   631  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), nil, strk)
   632  	c.Assert(err, IsNil)
   633  	err = Validate(info)
   634  	c.Assert(err, IsNil)
   635  	c.Check(info.Base, Equals, "none")
   636  }
   637  
   638  func (s *ValidateSuite) TestValidateBaseNoneError(c *C) {
   639  	yamlTemplate := `name: use-base-none
   640  version: 1
   641  base: none
   642  
   643  %APPS_OR_HOOKS%
   644  `
   645  	const apps = `
   646  apps:
   647    useradd:
   648      command: bin/true
   649  `
   650  	const hooks = `
   651  hooks:
   652    configure:
   653  `
   654  
   655  	for _, appsOrHooks := range []string{apps, hooks} {
   656  		yaml := strings.Replace(yamlTemplate, "%APPS_OR_HOOKS%", appsOrHooks, -1)
   657  		strk := NewScopedTracker()
   658  		info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), nil, strk)
   659  		c.Assert(err, IsNil)
   660  		err = Validate(info)
   661  		c.Assert(err, ErrorMatches, `cannot have apps or hooks with base "none"`)
   662  	}
   663  }
   664  
   665  type testConstraint string
   666  
   667  func (constraint testConstraint) IsOffLimits(path string) bool {
   668  	return true
   669  }
   670  
   671  func (s *ValidateSuite) TestValidateLayout(c *C) {
   672  	si := &Info{SuggestedName: "foo"}
   673  	// Several invalid layouts.
   674  	c.Check(ValidateLayout(&Layout{Snap: si}, nil),
   675  		ErrorMatches, "layout cannot use an empty path")
   676  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo"}, nil),
   677  		ErrorMatches, `layout "/foo" must define a bind mount, a filesystem mount or a symlink`)
   678  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Bind: "/bar", Type: "tmpfs"}, nil),
   679  		ErrorMatches, `layout "/foo" must define a bind mount, a filesystem mount or a symlink`)
   680  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Bind: "/bar", BindFile: "/froz"}, nil),
   681  		ErrorMatches, `layout "/foo" must define a bind mount, a filesystem mount or a symlink`)
   682  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Symlink: "/bar", BindFile: "/froz"}, nil),
   683  		ErrorMatches, `layout "/foo" must define a bind mount, a filesystem mount or a symlink`)
   684  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Type: "tmpfs", BindFile: "/froz"}, nil),
   685  		ErrorMatches, `layout "/foo" must define a bind mount, a filesystem mount or a symlink`)
   686  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Bind: "/bar", Symlink: "/froz"}, nil),
   687  		ErrorMatches, `layout "/foo" must define a bind mount, a filesystem mount or a symlink`)
   688  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Type: "tmpfs", Symlink: "/froz"}, nil),
   689  		ErrorMatches, `layout "/foo" must define a bind mount, a filesystem mount or a symlink`)
   690  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Type: "ext4"}, nil),
   691  		ErrorMatches, `layout "/foo" uses invalid filesystem "ext4"`)
   692  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo/bar", Type: "tmpfs", User: "foo"}, nil),
   693  		ErrorMatches, `layout "/foo/bar" uses invalid user "foo"`)
   694  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo/bar", Type: "tmpfs", Group: "foo"}, nil),
   695  		ErrorMatches, `layout "/foo/bar" uses invalid group "foo"`)
   696  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Type: "tmpfs", Mode: 02755}, nil),
   697  		ErrorMatches, `layout "/foo" uses invalid mode 02755`)
   698  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "$FOO", Type: "tmpfs"}, nil),
   699  		ErrorMatches, `layout "\$FOO" uses invalid mount point: reference to unknown variable "\$FOO"`)
   700  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Bind: "$BAR"}, nil),
   701  		ErrorMatches, `layout "/foo" uses invalid bind mount source "\$BAR": reference to unknown variable "\$BAR"`)
   702  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "$SNAP/evil", Bind: "/etc"}, nil),
   703  		ErrorMatches, `layout "\$SNAP/evil" uses invalid bind mount source "/etc": must start with \$SNAP, \$SNAP_DATA or \$SNAP_COMMON`)
   704  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Symlink: "$BAR"}, nil),
   705  		ErrorMatches, `layout "/foo" uses invalid symlink old name "\$BAR": reference to unknown variable "\$BAR"`)
   706  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "$SNAP/evil", Symlink: "/etc"}, nil),
   707  		ErrorMatches, `layout "\$SNAP/evil" uses invalid symlink old name "/etc": must start with \$SNAP, \$SNAP_DATA or \$SNAP_COMMON`)
   708  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo/bar", Bind: "$SNAP/bar/foo"}, []LayoutConstraint{testConstraint("/foo")}),
   709  		ErrorMatches, `layout "/foo/bar" underneath prior layout item "/foo"`)
   710  
   711  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/dev", Type: "tmpfs"}, nil),
   712  		ErrorMatches, `layout "/dev" in an off-limits area`)
   713  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/dev/foo", Type: "tmpfs"}, nil),
   714  		ErrorMatches, `layout "/dev/foo" in an off-limits area`)
   715  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/home", Type: "tmpfs"}, nil),
   716  		ErrorMatches, `layout "/home" in an off-limits area`)
   717  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/proc", Type: "tmpfs"}, nil),
   718  		ErrorMatches, `layout "/proc" in an off-limits area`)
   719  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/sys", Type: "tmpfs"}, nil),
   720  		ErrorMatches, `layout "/sys" in an off-limits area`)
   721  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/run", Type: "tmpfs"}, nil),
   722  		ErrorMatches, `layout "/run" in an off-limits area`)
   723  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/boot", Type: "tmpfs"}, nil),
   724  		ErrorMatches, `layout "/boot" in an off-limits area`)
   725  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/lost+found", Type: "tmpfs"}, nil),
   726  		ErrorMatches, `layout "/lost\+found" in an off-limits area`)
   727  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/media", Type: "tmpfs"}, nil),
   728  		ErrorMatches, `layout "/media" in an off-limits area`)
   729  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/var/snap", Type: "tmpfs"}, nil),
   730  		ErrorMatches, `layout "/var/snap" in an off-limits area`)
   731  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/var/lib/snapd", Type: "tmpfs"}, nil),
   732  		ErrorMatches, `layout "/var/lib/snapd" in an off-limits area`)
   733  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/var/lib/snapd/hostfs", Type: "tmpfs"}, nil),
   734  		ErrorMatches, `layout "/var/lib/snapd/hostfs" in an off-limits area`)
   735  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/lib/firmware", Type: "tmpfs"}, nil),
   736  		ErrorMatches, `layout "/lib/firmware" in an off-limits area`)
   737  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/lib/modules", Type: "tmpfs"}, nil),
   738  		ErrorMatches, `layout "/lib/modules" in an off-limits area`)
   739  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/tmp", Type: "tmpfs"}, nil),
   740  		ErrorMatches, `layout "/tmp" in an off-limits area`)
   741  
   742  	// Several valid layouts.
   743  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/foo", Type: "tmpfs", Mode: 01755}, nil), IsNil)
   744  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/usr", Bind: "$SNAP/usr"}, nil), IsNil)
   745  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/var", Bind: "$SNAP_DATA/var"}, nil), IsNil)
   746  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/var", Bind: "$SNAP_COMMON/var"}, nil), IsNil)
   747  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/etc/foo.conf", Symlink: "$SNAP_DATA/etc/foo.conf"}, nil), IsNil)
   748  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/a/b", Type: "tmpfs", User: "root"}, nil), IsNil)
   749  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/a/b", Type: "tmpfs", Group: "root"}, nil), IsNil)
   750  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/a/b", Type: "tmpfs", Mode: 0655}, nil), IsNil)
   751  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/usr", Symlink: "$SNAP/usr"}, nil), IsNil)
   752  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/var", Symlink: "$SNAP_DATA/var"}, nil), IsNil)
   753  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "/var", Symlink: "$SNAP_COMMON/var"}, nil), IsNil)
   754  	c.Check(ValidateLayout(&Layout{Snap: si, Path: "$SNAP/data", Symlink: "$SNAP_DATA"}, nil), IsNil)
   755  }
   756  
   757  func (s *ValidateSuite) TestValidateLayoutAll(c *C) {
   758  	// /usr/foo prevents /usr/foo/bar from being valid (tmpfs)
   759  	const yaml1 = `
   760  name: broken-layout-1
   761  layout:
   762    /usr/foo:
   763      type: tmpfs
   764    /usr/foo/bar:
   765      type: tmpfs
   766  `
   767  	const yaml1rev = `
   768  name: broken-layout-1
   769  layout:
   770    /usr/foo/bar:
   771      type: tmpfs
   772    /usr/foo:
   773      type: tmpfs
   774  `
   775  
   776  	for _, yaml := range []string{yaml1, yaml1rev} {
   777  		strk := NewScopedTracker()
   778  		info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), &SideInfo{Revision: R(42)}, strk)
   779  		c.Assert(err, IsNil)
   780  		c.Assert(info.Layout, HasLen, 2)
   781  		err = ValidateLayoutAll(info)
   782  		c.Assert(err, ErrorMatches, `layout "/usr/foo/bar" underneath prior layout item "/usr/foo"`)
   783  	}
   784  
   785  	// Same as above but with bind-mounts instead of filesystem mounts.
   786  	const yaml2 = `
   787  name: broken-layout-2
   788  layout:
   789    /usr/foo:
   790      bind: $SNAP
   791    /usr/foo/bar:
   792      bind: $SNAP
   793  `
   794  	const yaml2rev = `
   795  name: broken-layout-2
   796  layout:
   797    /usr/foo/bar:
   798      bind: $SNAP
   799    /usr/foo:
   800      bind: $SNAP
   801  `
   802  	for _, yaml := range []string{yaml2, yaml2rev} {
   803  		strk := NewScopedTracker()
   804  		info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), &SideInfo{Revision: R(42)}, strk)
   805  		c.Assert(err, IsNil)
   806  		c.Assert(info.Layout, HasLen, 2)
   807  		err = ValidateLayoutAll(info)
   808  		c.Assert(err, ErrorMatches, `layout "/usr/foo/bar" underneath prior layout item "/usr/foo"`)
   809  	}
   810  
   811  	// /etc/foo (directory) is not clashing with /etc/foo.conf (file)
   812  	const yaml3 = `
   813  name: valid-layout-1
   814  layout:
   815    /etc/foo:
   816      bind: $SNAP_DATA/foo
   817    /etc/foo.conf:
   818      symlink: $SNAP_DATA/foo.conf
   819  `
   820  	const yaml3rev = `
   821  name: valid-layout-1
   822  layout:
   823    /etc/foo.conf:
   824      symlink: $SNAP_DATA/foo.conf
   825    /etc/foo:
   826      bind: $SNAP_DATA/foo
   827  `
   828  	for _, yaml := range []string{yaml3, yaml3rev} {
   829  		strk := NewScopedTracker()
   830  		info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), &SideInfo{Revision: R(42)}, strk)
   831  		c.Assert(err, IsNil)
   832  		c.Assert(info.Layout, HasLen, 2)
   833  		err = ValidateLayoutAll(info)
   834  		c.Assert(err, IsNil)
   835  	}
   836  
   837  	// /etc/foo file is not clashing with /etc/foobar
   838  	const yaml4 = `
   839  name: valid-layout-2
   840  layout:
   841    /etc/foo:
   842      symlink: $SNAP_DATA/foo
   843    /etc/foobar:
   844      symlink: $SNAP_DATA/foobar
   845  `
   846  	const yaml4rev = `
   847  name: valid-layout-2
   848  layout:
   849    /etc/foobar:
   850      symlink: $SNAP_DATA/foobar
   851    /etc/foo:
   852      symlink: $SNAP_DATA/foo
   853  `
   854  	for _, yaml := range []string{yaml4, yaml4rev} {
   855  		strk := NewScopedTracker()
   856  		info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), &SideInfo{Revision: R(42)}, strk)
   857  		c.Assert(err, IsNil)
   858  		c.Assert(info.Layout, HasLen, 2)
   859  		err = ValidateLayoutAll(info)
   860  		c.Assert(err, IsNil)
   861  	}
   862  
   863  	// /etc/foo file is also clashing with /etc/foo/bar
   864  	const yaml5 = `
   865  name: valid-layout-2
   866  layout:
   867    /usr/foo:
   868      symlink: $SNAP_DATA/foo
   869    /usr/foo/bar:
   870      bind: $SNAP_DATA/foo/bar
   871  `
   872  	const yaml5rev = `
   873  name: valid-layout-2
   874  layout:
   875    /usr/foo/bar:
   876      bind: $SNAP_DATA/foo/bar
   877    /usr/foo:
   878      symlink: $SNAP_DATA/foo
   879  `
   880  	for _, yaml := range []string{yaml5, yaml5rev} {
   881  		strk := NewScopedTracker()
   882  		info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), &SideInfo{Revision: R(42)}, strk)
   883  		c.Assert(err, IsNil)
   884  		c.Assert(info.Layout, HasLen, 2)
   885  		err = ValidateLayoutAll(info)
   886  		c.Assert(err, ErrorMatches, `layout "/usr/foo/bar" underneath prior layout item "/usr/foo"`)
   887  	}
   888  
   889  	const yaml6 = `
   890  name: tricky-layout-1
   891  layout:
   892    /etc/norf:
   893      bind: $SNAP/etc/norf
   894    /etc/norf:
   895      bind-file: $SNAP/etc/norf
   896  `
   897  	strk := NewScopedTracker()
   898  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml6), &SideInfo{Revision: R(42)}, strk)
   899  	c.Assert(err, IsNil)
   900  	c.Assert(info.Layout, HasLen, 1)
   901  	err = ValidateLayoutAll(info)
   902  	c.Assert(err, IsNil)
   903  	c.Assert(info.Layout["/etc/norf"].Bind, Equals, "")
   904  	c.Assert(info.Layout["/etc/norf"].BindFile, Equals, "$SNAP/etc/norf")
   905  
   906  	// Two layouts refer to the same path as a directory and a file.
   907  	const yaml7 = `
   908  name: clashing-source-path-1
   909  layout:
   910    /etc/norf:
   911      bind: $SNAP/etc/norf
   912    /etc/corge:
   913      bind-file: $SNAP/etc/norf
   914  `
   915  
   916  	strk = NewScopedTracker()
   917  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml7), &SideInfo{Revision: R(42)}, strk)
   918  	c.Assert(err, IsNil)
   919  	c.Assert(info.Layout, HasLen, 2)
   920  	err = ValidateLayoutAll(info)
   921  	c.Assert(err, ErrorMatches, `layout "/etc/norf" refers to directory "\$SNAP/etc/norf" but another layout treats it as file`)
   922  
   923  	// Two layouts refer to the same path as a directory and a file (other way around).
   924  	const yaml8 = `
   925  name: clashing-source-path-2
   926  layout:
   927    /etc/norf:
   928      bind-file: $SNAP/etc/norf
   929    /etc/corge:
   930      bind: $SNAP/etc/norf
   931  `
   932  	strk = NewScopedTracker()
   933  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml8), &SideInfo{Revision: R(42)}, strk)
   934  	c.Assert(err, IsNil)
   935  	c.Assert(info.Layout, HasLen, 2)
   936  	err = ValidateLayoutAll(info)
   937  	c.Assert(err, ErrorMatches, `layout "/etc/norf" refers to file "\$SNAP/etc/norf" but another layout treats it as a directory`)
   938  
   939  	// Two layouts refer to the same path, but one uses variable and the other doesn't.
   940  	const yaml9 = `
   941  name: clashing-source-path-3
   942  layout:
   943    /etc/norf:
   944      bind-file: $SNAP/etc/norf
   945    /etc/corge:
   946      bind: /snap/clashing-source-path-3/42/etc/norf
   947  `
   948  	strk = NewScopedTracker()
   949  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml9), &SideInfo{Revision: R(42)}, strk)
   950  	c.Assert(err, IsNil)
   951  	c.Assert(info.Layout, HasLen, 2)
   952  	err = ValidateLayoutAll(info)
   953  	c.Assert(err, ErrorMatches, `layout "/etc/norf" refers to file "\$SNAP/etc/norf" but another layout treats it as a directory`)
   954  
   955  	// Same source path referred from a bind mount and symlink doesn't clash.
   956  	const yaml10 = `
   957  name: non-clashing-source-1
   958  layout:
   959    /etc/norf:
   960      bind: $SNAP/etc/norf
   961    /etc/corge:
   962      symlink: $SNAP/etc/norf
   963  `
   964  
   965  	strk = NewScopedTracker()
   966  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml10), &SideInfo{Revision: R(42)}, strk)
   967  	c.Assert(err, IsNil)
   968  	c.Assert(info.Layout, HasLen, 2)
   969  	err = ValidateLayoutAll(info)
   970  	c.Assert(err, IsNil)
   971  
   972  	// Same source path referred from a file bind mount and symlink doesn't clash.
   973  	const yaml11 = `
   974  name: non-clashing-source-1
   975  layout:
   976    /etc/norf:
   977      bind-file: $SNAP/etc/norf
   978    /etc/corge:
   979      symlink: $SNAP/etc/norf
   980  `
   981  
   982  	strk = NewScopedTracker()
   983  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml11), &SideInfo{Revision: R(42)}, strk)
   984  	c.Assert(err, IsNil)
   985  	c.Assert(info.Layout, HasLen, 2)
   986  	err = ValidateLayoutAll(info)
   987  	c.Assert(err, IsNil)
   988  
   989  	// Layout replacing files in another snap's mount p oit
   990  	const yaml12 = `
   991  name: this-snap
   992  layout:
   993    /snap/that-snap/current/stuff:
   994      symlink: $SNAP/stuff
   995  `
   996  
   997  	strk = NewScopedTracker()
   998  	info, err = InfoFromSnapYamlWithSideInfo([]byte(yaml12), &SideInfo{Revision: R(42)}, strk)
   999  	c.Assert(err, IsNil)
  1000  	c.Assert(info.Layout, HasLen, 1)
  1001  	err = ValidateLayoutAll(info)
  1002  	c.Assert(err, ErrorMatches, `layout "/snap/that-snap/current/stuff" defines a layout in space belonging to another snap`)
  1003  }
  1004  
  1005  func (s *YamlSuite) TestValidateAppStartupOrder(c *C) {
  1006  	meta := []byte(`
  1007  name: foo
  1008  version: 1.0
  1009  `)
  1010  	fooAfterBaz := []byte(`
  1011  apps:
  1012    foo:
  1013      after: [baz]
  1014      daemon: simple
  1015    bar:
  1016      daemon: forking
  1017  `)
  1018  	fooBeforeBaz := []byte(`
  1019  apps:
  1020    foo:
  1021      before: [baz]
  1022      daemon: simple
  1023    bar:
  1024      daemon: forking
  1025  `)
  1026  
  1027  	fooNotADaemon := []byte(`
  1028  apps:
  1029    foo:
  1030      after: [bar]
  1031    bar:
  1032      daemon: forking
  1033  `)
  1034  
  1035  	fooBarNotADaemon := []byte(`
  1036  apps:
  1037    foo:
  1038      after: [bar]
  1039      daemon: forking
  1040    bar:
  1041  `)
  1042  	fooSelfCycle := []byte(`
  1043  apps:
  1044    foo:
  1045      after: [foo]
  1046      daemon: forking
  1047    bar:
  1048  `)
  1049  	// cycle between foo and bar
  1050  	badOrder1 := []byte(`
  1051  apps:
  1052   foo:
  1053     after: [bar]
  1054     daemon: forking
  1055   bar:
  1056     after: [foo]
  1057     daemon: forking
  1058  `)
  1059  	// conflicting schedule for baz
  1060  	badOrder2 := []byte(`
  1061  apps:
  1062   foo:
  1063     before: [bar]
  1064     daemon: forking
  1065   bar:
  1066     after: [foo]
  1067     daemon: forking
  1068   baz:
  1069     before: [foo]
  1070     after: [bar]
  1071     daemon: forking
  1072  `)
  1073  	// conflicting schedule for baz
  1074  	badOrder3Cycle := []byte(`
  1075  apps:
  1076   foo:
  1077     before: [bar]
  1078     after: [zed]
  1079     daemon: forking
  1080   bar:
  1081     before: [baz]
  1082     daemon: forking
  1083   baz:
  1084     before: [zed]
  1085     daemon: forking
  1086   zed:
  1087     daemon: forking
  1088  `)
  1089  	goodOrder1 := []byte(`
  1090  apps:
  1091   foo:
  1092     after: [bar, zed]
  1093     daemon: oneshot
  1094   bar:
  1095     before: [foo]
  1096     daemon: dbus
  1097   baz:
  1098     after: [foo]
  1099     daemon: forking
  1100   zed:
  1101     daemon: dbus
  1102  `)
  1103  	goodOrder2 := []byte(`
  1104  apps:
  1105   foo:
  1106     after: [baz]
  1107     daemon: oneshot
  1108   bar:
  1109     before: [baz]
  1110     daemon: dbus
  1111   baz:
  1112     daemon: forking
  1113   zed:
  1114     daemon: dbus
  1115     after: [foo, bar, baz]
  1116  `)
  1117  
  1118  	tcs := []struct {
  1119  		name string
  1120  		desc []byte
  1121  		err  string
  1122  	}{{
  1123  		name: "foo after baz",
  1124  		desc: fooAfterBaz,
  1125  		err:  `invalid definition of application "foo": before/after references a missing application "baz"`,
  1126  	}, {
  1127  		name: "foo before baz",
  1128  		desc: fooBeforeBaz,
  1129  		err:  `invalid definition of application "foo": before/after references a missing application "baz"`,
  1130  	}, {
  1131  		name: "foo not a daemon",
  1132  		desc: fooNotADaemon,
  1133  		err:  `invalid definition of application "foo": must be a service to define before/after ordering`,
  1134  	}, {
  1135  		name: "foo wants bar, bar not a daemon",
  1136  		desc: fooBarNotADaemon,
  1137  		err:  `invalid definition of application "foo": before/after references a non-service application "bar"`,
  1138  	}, {
  1139  		name: "bad order 1",
  1140  		desc: badOrder1,
  1141  		err:  `applications are part of a before/after cycle: (foo, bar)|(bar, foo)`,
  1142  	}, {
  1143  		name: "bad order 2",
  1144  		desc: badOrder2,
  1145  		err:  `applications are part of a before/after cycle: ((foo|bar|baz)(, )?){3}`,
  1146  	}, {
  1147  		name: "bad order 3 - cycle",
  1148  		desc: badOrder3Cycle,
  1149  		err:  `applications are part of a before/after cycle: ((foo|bar|baz|zed)(, )?){4}`,
  1150  	}, {
  1151  		name: "all good, 3 apps",
  1152  		desc: goodOrder1,
  1153  	}, {
  1154  		name: "all good, 4 apps",
  1155  		desc: goodOrder2,
  1156  	}, {
  1157  		name: "self cycle",
  1158  		desc: fooSelfCycle,
  1159  		err:  `applications are part of a before/after cycle: foo`},
  1160  	}
  1161  	for _, tc := range tcs {
  1162  		c.Logf("trying %q", tc.name)
  1163  		info, err := InfoFromSnapYaml(append(meta, tc.desc...))
  1164  		c.Assert(err, IsNil)
  1165  
  1166  		err = Validate(info)
  1167  		if tc.err != "" {
  1168  			c.Assert(err, ErrorMatches, tc.err)
  1169  		} else {
  1170  			c.Assert(err, IsNil)
  1171  		}
  1172  	}
  1173  }
  1174  
  1175  func (s *ValidateSuite) TestValidateAppWatchdogTimeout(c *C) {
  1176  	s.testValidateAppTimeout(c, "watchdog")
  1177  }
  1178  func (s *ValidateSuite) TestValidateAppStartTimeout(c *C) {
  1179  	s.testValidateAppTimeout(c, "start")
  1180  }
  1181  func (s *ValidateSuite) TestValidateAppStopTimeout(c *C) {
  1182  	s.testValidateAppTimeout(c, "stop")
  1183  }
  1184  
  1185  func (s *ValidateSuite) testValidateAppTimeout(c *C, timeout string) {
  1186  	timeout += "-timeout"
  1187  	meta := []byte(`
  1188  name: foo
  1189  version: 1.0
  1190  `)
  1191  	fooAllGood := []byte(fmt.Sprintf(`
  1192  apps:
  1193    foo:
  1194      daemon: simple
  1195      %s: 12s
  1196  `, timeout))
  1197  	fooNotADaemon := []byte(fmt.Sprintf(`
  1198  apps:
  1199    foo:
  1200      %s: 12s
  1201  `, timeout))
  1202  
  1203  	fooNegative := []byte(fmt.Sprintf(`
  1204  apps:
  1205    foo:
  1206      daemon: simple
  1207      %s: -12s
  1208  `, timeout))
  1209  
  1210  	tcs := []struct {
  1211  		name string
  1212  		desc []byte
  1213  		err  string
  1214  	}{{
  1215  		name: "foo all good",
  1216  		desc: fooAllGood,
  1217  	}, {
  1218  		name: "foo not a service",
  1219  		desc: fooNotADaemon,
  1220  		err:  timeout + ` is only applicable to services`,
  1221  	}, {
  1222  		name: "negative timeout",
  1223  		desc: fooNegative,
  1224  		err:  timeout + ` cannot be negative`,
  1225  	}}
  1226  	for _, tc := range tcs {
  1227  		c.Logf("trying %q", tc.name)
  1228  		info, err := InfoFromSnapYaml(append(meta, tc.desc...))
  1229  		c.Assert(err, IsNil)
  1230  		c.Assert(info, NotNil)
  1231  
  1232  		err = Validate(info)
  1233  		if tc.err != "" {
  1234  			c.Assert(err, ErrorMatches, `invalid definition of application "foo": `+tc.err)
  1235  		} else {
  1236  			c.Assert(err, IsNil)
  1237  		}
  1238  	}
  1239  }
  1240  
  1241  func (s *YamlSuite) TestValidateAppTimer(c *C) {
  1242  	meta := []byte(`
  1243  name: foo
  1244  version: 1.0
  1245  `)
  1246  	allGood := []byte(`
  1247  apps:
  1248    foo:
  1249      daemon: simple
  1250      timer: 10:00-12:00
  1251  `)
  1252  	notAService := []byte(`
  1253  apps:
  1254    foo:
  1255      timer: 10:00-12:00
  1256  `)
  1257  	badTimer := []byte(`
  1258  apps:
  1259    foo:
  1260      daemon: oneshot
  1261      timer: mon,10:00-12:00,mon2-wed3
  1262  `)
  1263  
  1264  	tcs := []struct {
  1265  		name string
  1266  		desc []byte
  1267  		err  string
  1268  	}{{
  1269  		name: "all correct",
  1270  		desc: allGood,
  1271  	}, {
  1272  		name: "not a service",
  1273  		desc: notAService,
  1274  		err:  `timer is only applicable to services`,
  1275  	}, {
  1276  		name: "invalid timer",
  1277  		desc: badTimer,
  1278  		err:  `timer has invalid format: cannot parse "mon2-wed3": invalid schedule fragment`,
  1279  	}}
  1280  	for _, tc := range tcs {
  1281  		c.Logf("trying %q", tc.name)
  1282  		info, err := InfoFromSnapYaml(append(meta, tc.desc...))
  1283  		c.Assert(err, IsNil)
  1284  
  1285  		err = Validate(info)
  1286  		if tc.err != "" {
  1287  			c.Assert(err, ErrorMatches, `invalid definition of application "foo": `+tc.err)
  1288  		} else {
  1289  			c.Assert(err, IsNil)
  1290  		}
  1291  	}
  1292  }
  1293  
  1294  func (s *ValidateSuite) TestValidateOsCannotHaveBase(c *C) {
  1295  	info, err := InfoFromSnapYaml([]byte(`name: foo
  1296  version: 1.0
  1297  type: os
  1298  base: bar
  1299  `))
  1300  	c.Assert(err, IsNil)
  1301  
  1302  	err = Validate(info)
  1303  	c.Check(err, ErrorMatches, `cannot have "base" field on "os" snap "foo"`)
  1304  }
  1305  
  1306  func (s *ValidateSuite) TestValidateOsCanHaveBaseNone(c *C) {
  1307  	info, err := InfoFromSnapYaml([]byte(`name: foo
  1308  version: 1.0
  1309  type: os
  1310  base: none
  1311  `))
  1312  	c.Assert(err, IsNil)
  1313  	c.Assert(Validate(info), IsNil)
  1314  }
  1315  
  1316  func (s *ValidateSuite) TestValidateBaseInorrectSnapName(c *C) {
  1317  	info, err := InfoFromSnapYaml([]byte(`name: foo
  1318  version: 1.0
  1319  base: aAAAA
  1320  `))
  1321  	c.Assert(err, IsNil)
  1322  
  1323  	err = Validate(info)
  1324  	c.Check(err, ErrorMatches, `invalid base name: invalid snap name: \"aAAAA\"`)
  1325  }
  1326  
  1327  func (s *ValidateSuite) TestValidateBaseSnapInstanceNameNotAllowed(c *C) {
  1328  	info, err := InfoFromSnapYaml([]byte(`name: foo
  1329  version: 1.0
  1330  base: foo_abc
  1331  `))
  1332  	c.Assert(err, IsNil)
  1333  
  1334  	err = Validate(info)
  1335  	c.Check(err, ErrorMatches, `base cannot specify a snap instance name: "foo_abc"`)
  1336  }
  1337  
  1338  func (s *ValidateSuite) TestValidateBaseCannotHaveBase(c *C) {
  1339  	info, err := InfoFromSnapYaml([]byte(`name: foo
  1340  version: 1.0
  1341  type: base
  1342  base: bar
  1343  `))
  1344  	c.Assert(err, IsNil)
  1345  
  1346  	err = Validate(info)
  1347  	c.Check(err, ErrorMatches, `cannot have "base" field on "base" snap "foo"`)
  1348  }
  1349  
  1350  func (s *ValidateSuite) TestValidateBaseCanHaveBaseNone(c *C) {
  1351  	info, err := InfoFromSnapYaml([]byte(`name: foo
  1352  version: 1.0
  1353  type: base
  1354  base: none
  1355  `))
  1356  	c.Assert(err, IsNil)
  1357  	c.Assert(Validate(info), IsNil)
  1358  }
  1359  
  1360  func (s *ValidateSuite) TestValidateCommonIDs(c *C) {
  1361  	meta := `
  1362  name: foo
  1363  version: 1.0
  1364  `
  1365  	good := meta + `
  1366  apps:
  1367    foo:
  1368      common-id: org.foo.foo
  1369    bar:
  1370      common-id: org.foo.bar
  1371    baz:
  1372  `
  1373  	bad := meta + `
  1374  apps:
  1375    foo:
  1376      common-id: org.foo.foo
  1377    bar:
  1378      common-id: org.foo.foo
  1379    baz:
  1380  `
  1381  	for i, tc := range []struct {
  1382  		meta string
  1383  		err  string
  1384  	}{
  1385  		{good, ""},
  1386  		{bad, `application ("bar" common-id "org.foo.foo" must be unique, already used by application "foo"|"foo" common-id "org.foo.foo" must be unique, already used by application "bar")`},
  1387  	} {
  1388  		c.Logf("tc #%v", i)
  1389  		info, err := InfoFromSnapYaml([]byte(tc.meta))
  1390  		c.Assert(err, IsNil)
  1391  
  1392  		err = Validate(info)
  1393  		if tc.err == "" {
  1394  			c.Assert(err, IsNil)
  1395  		} else {
  1396  			c.Assert(err, NotNil)
  1397  			c.Check(err, ErrorMatches, tc.err)
  1398  		}
  1399  	}
  1400  }
  1401  
  1402  func (s *validateSuite) TestValidateDescription(c *C) {
  1403  	for _, s := range []string{
  1404  		"xx", // boringest ASCII
  1405  		"🐧🐧", // len("🐧🐧") == 8
  1406  		"á", // á (combining)
  1407  	} {
  1408  		c.Check(ValidateDescription(s), IsNil)
  1409  		c.Check(ValidateDescription(strings.Repeat(s, 2049)), ErrorMatches, `description can have up to 4096 codepoints, got 4098`)
  1410  		c.Check(ValidateDescription(strings.Repeat(s, 2048)), IsNil)
  1411  	}
  1412  }
  1413  
  1414  func (s *validateSuite) TestValidateTitle(c *C) {
  1415  	for _, s := range []string{
  1416  		"xx", // boringest ASCII
  1417  		"🐧🐧", // len("🐧🐧") == 8
  1418  		"á", // á (combining)
  1419  	} {
  1420  		c.Check(ValidateTitle(strings.Repeat(s, 21)), ErrorMatches, `title can have up to 40 codepoints, got 42`)
  1421  		c.Check(ValidateTitle(strings.Repeat(s, 20)), IsNil)
  1422  	}
  1423  }
  1424  
  1425  func (s *validateSuite) TestValidatePlugSlotName(c *C) {
  1426  	validNames := []string{
  1427  		"a", "aa", "aaa", "aaaa",
  1428  		"a-a", "aa-a", "a-aa", "a-b-c",
  1429  		"a0", "a-0", "a-0a",
  1430  	}
  1431  	for _, name := range validNames {
  1432  		c.Assert(ValidatePlugName(name), IsNil)
  1433  		c.Assert(ValidateSlotName(name), IsNil)
  1434  		c.Assert(ValidateInterfaceName(name), IsNil)
  1435  	}
  1436  	invalidNames := []string{
  1437  		// name cannot be empty
  1438  		"",
  1439  		// dashes alone are not a name
  1440  		"-", "--",
  1441  		// double dashes in a name are not allowed
  1442  		"a--a",
  1443  		// name should not end with a dash
  1444  		"a-",
  1445  		// name cannot have any spaces in it
  1446  		"a ", " a", "a a",
  1447  		// a number alone is not a name
  1448  		"0", "123",
  1449  		// identifier must be plain ASCII
  1450  		"日本語", "한글", "ру́сский язы́к",
  1451  	}
  1452  	for _, name := range invalidNames {
  1453  		c.Assert(ValidatePlugName(name), ErrorMatches, `invalid plug name: ".*"`)
  1454  		c.Assert(ValidateSlotName(name), ErrorMatches, `invalid slot name: ".*"`)
  1455  		c.Assert(ValidateInterfaceName(name), ErrorMatches, `invalid interface name: ".*"`)
  1456  	}
  1457  }
  1458  
  1459  func (s *ValidateSuite) TestValidateSnapInstanceNameBadSnapName(c *C) {
  1460  	info, err := InfoFromSnapYaml([]byte(`name: foo_bad
  1461  version: 1.0
  1462  `))
  1463  	c.Assert(err, IsNil)
  1464  
  1465  	err = Validate(info)
  1466  	c.Check(err, ErrorMatches, `invalid snap name: "foo_bad"`)
  1467  }
  1468  
  1469  func (s *ValidateSuite) TestValidateSnapInstanceNameBadInstanceKey(c *C) {
  1470  	info, err := InfoFromSnapYaml([]byte(`name: foo
  1471  version: 1.0
  1472  `))
  1473  	c.Assert(err, IsNil)
  1474  
  1475  	for _, s := range []string{"toolonginstance", "ABCD", "_", "inst@nce", "012345678901"} {
  1476  		info.InstanceKey = s
  1477  		err = Validate(info)
  1478  		c.Check(err, ErrorMatches, fmt.Sprintf(`invalid instance key: %q`, s))
  1479  	}
  1480  }
  1481  
  1482  func (s *ValidateSuite) TestValidateAppRestart(c *C) {
  1483  	meta := []byte(`
  1484  name: foo
  1485  version: 1.0
  1486  `)
  1487  	fooAllGood := []byte(`
  1488  apps:
  1489    foo:
  1490      daemon: simple
  1491      restart-condition: on-abort
  1492      restart-delay: 12s
  1493  `)
  1494  	fooAllGoodDefault := []byte(`
  1495  apps:
  1496    foo:
  1497      daemon: simple
  1498  `)
  1499  	fooAllGoodJustDelay := []byte(`
  1500  apps:
  1501    foo:
  1502      daemon: simple
  1503      restart-delay: 12s
  1504  `)
  1505  	fooConditionNotADaemon := []byte(`
  1506  apps:
  1507    foo:
  1508      restart-condition: on-abort
  1509  `)
  1510  	fooDelayNotADaemon := []byte(`
  1511  apps:
  1512    foo:
  1513      restart-delay: 12s
  1514  `)
  1515  	fooNegativeDelay := []byte(`
  1516  apps:
  1517    foo:
  1518      daemon: simple
  1519      restart-delay: -12s
  1520  `)
  1521  
  1522  	tcs := []struct {
  1523  		name string
  1524  		desc []byte
  1525  		err  string
  1526  	}{{
  1527  		name: "foo all good",
  1528  		desc: fooAllGood,
  1529  	}, {
  1530  		name: "foo all good with default values",
  1531  		desc: fooAllGoodDefault,
  1532  	}, {
  1533  		name: "foo all good with restart-delay only",
  1534  		desc: fooAllGoodJustDelay,
  1535  	}, {
  1536  		name: "foo restart-delay but not a service",
  1537  		desc: fooDelayNotADaemon,
  1538  		err:  `restart-delay is only applicable to services`,
  1539  	}, {
  1540  		name: "foo restart-delay but not a service",
  1541  		desc: fooConditionNotADaemon,
  1542  		err:  `restart-condition is only applicable to services`,
  1543  	}, {
  1544  		name: "negative restart-delay",
  1545  		desc: fooNegativeDelay,
  1546  		err:  `restart-delay cannot be negative`,
  1547  	}}
  1548  	for _, tc := range tcs {
  1549  		c.Logf("trying %q", tc.name)
  1550  		info, err := InfoFromSnapYaml(append(meta, tc.desc...))
  1551  		c.Assert(err, IsNil)
  1552  		c.Assert(info, NotNil)
  1553  
  1554  		err = Validate(info)
  1555  		if tc.err != "" {
  1556  			c.Assert(err, ErrorMatches, `invalid definition of application "foo": `+tc.err)
  1557  		} else {
  1558  			c.Assert(err, IsNil)
  1559  		}
  1560  	}
  1561  }
  1562  
  1563  func (s *ValidateSuite) TestValidateSystemUsernames(c *C) {
  1564  	const yaml1 = `name: binary
  1565  version: 1.0
  1566  system-usernames:
  1567    "b@d": shared
  1568  `
  1569  
  1570  	strk := NewScopedTracker()
  1571  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml1), nil, strk)
  1572  	c.Assert(err, IsNil)
  1573  	c.Assert(info.SystemUsernames, HasLen, 1)
  1574  	err = Validate(info)
  1575  	c.Assert(err, ErrorMatches, `invalid system username "b@d"`)
  1576  }
  1577  
  1578  const yamlNeedDf = `name: need-df
  1579  version: 1.0
  1580  plugs:
  1581    gtk-3-themes:
  1582      interface: content
  1583      default-provider: gtk-common-themes
  1584  `
  1585  
  1586  func (s *ValidateSuite) TestNeededDefaultProviders(c *C) {
  1587  	strk := NewScopedTracker()
  1588  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yamlNeedDf), nil, strk)
  1589  	c.Assert(err, IsNil)
  1590  
  1591  	dps := NeededDefaultProviders(info)
  1592  	c.Check(dps, DeepEquals, []string{"gtk-common-themes"})
  1593  }
  1594  
  1595  const yamlNeedDfWithSlot = `name: need-df
  1596  version: 1.0
  1597  plugs:
  1598    gtk-3-themes:
  1599      interface: content
  1600      default-provider: gtk-common-themes2:with-slot
  1601  `
  1602  
  1603  func (s *ValidateSuite) TestNeededDefaultProvidersLegacyColonSyntax(c *C) {
  1604  	strk := NewScopedTracker()
  1605  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yamlNeedDfWithSlot), nil, strk)
  1606  	c.Assert(err, IsNil)
  1607  
  1608  	dps := NeededDefaultProviders(info)
  1609  	c.Check(dps, DeepEquals, []string{"gtk-common-themes2"})
  1610  }
  1611  
  1612  func (s *validateSuite) TestValidateSnapMissingCore(c *C) {
  1613  	const yaml = `name: some-snap
  1614  version: 1.0`
  1615  
  1616  	strk := NewScopedTracker()
  1617  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), nil, strk)
  1618  	c.Assert(err, IsNil)
  1619  
  1620  	infos := map[string]*Info{"some-snap": info}
  1621  	errors := ValidateBasesAndProviders(infos)
  1622  	c.Assert(errors, HasLen, 1)
  1623  	c.Assert(errors[0], ErrorMatches, `cannot use snap "some-snap": required snap "core" missing`)
  1624  }
  1625  
  1626  func (s *validateSuite) TestValidateSnapMissingBase(c *C) {
  1627  	const yaml = `name: some-snap
  1628  base: some-base
  1629  version: 1.0`
  1630  
  1631  	strk := NewScopedTracker()
  1632  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), nil, strk)
  1633  	c.Assert(err, IsNil)
  1634  
  1635  	infos := map[string]*Info{"some-snap": info}
  1636  	errors := ValidateBasesAndProviders(infos)
  1637  	c.Assert(errors, HasLen, 1)
  1638  	c.Assert(errors[0], ErrorMatches, `cannot use snap "some-snap": base "some-base" is missing`)
  1639  }
  1640  
  1641  func (s *validateSuite) TestValidateSnapMissingDefaultProvider(c *C) {
  1642  	strk := NewScopedTracker()
  1643  	snapInfo, err := InfoFromSnapYamlWithSideInfo([]byte(yamlNeedDf), nil, strk)
  1644  	c.Assert(err, IsNil)
  1645  
  1646  	var coreYaml = `name: core
  1647  version: 1.0
  1648  type: os`
  1649  
  1650  	coreInfo, err := InfoFromSnapYamlWithSideInfo([]byte(coreYaml), nil, strk)
  1651  	c.Assert(err, IsNil)
  1652  
  1653  	infos := map[string]*Info{"some-snap": snapInfo, "core": coreInfo}
  1654  	errors := ValidateBasesAndProviders(infos)
  1655  	c.Assert(errors, HasLen, 1)
  1656  	c.Assert(errors[0], ErrorMatches, `cannot use snap "need-df": default provider "gtk-common-themes" is missing`)
  1657  }
  1658  
  1659  func (s *validateSuite) TestValidateSnapBaseNoneOK(c *C) {
  1660  	const yaml = `name: some-snap
  1661  base: none
  1662  version: 1.0`
  1663  
  1664  	strk := NewScopedTracker()
  1665  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), nil, strk)
  1666  	c.Assert(err, IsNil)
  1667  
  1668  	infos := map[string]*Info{"some-snap": info}
  1669  	errors := ValidateBasesAndProviders(infos)
  1670  	c.Assert(errors, IsNil)
  1671  }
  1672  
  1673  func (s *validateSuite) TestValidateSnapSnapd(c *C) {
  1674  	const yaml = `name: snapd
  1675  type: snapd
  1676  version: 1.0`
  1677  
  1678  	strk := NewScopedTracker()
  1679  	info, err := InfoFromSnapYamlWithSideInfo([]byte(yaml), nil, strk)
  1680  	c.Assert(err, IsNil)
  1681  
  1682  	infos := map[string]*Info{"snapd": info}
  1683  	errors := ValidateBasesAndProviders(infos)
  1684  	c.Assert(errors, IsNil)
  1685  }