github.com/rigado/snapd@v2.42.5-go-mod+incompatible/snap/container_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  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"syscall"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/snap"
    31  	"github.com/snapcore/snapd/snap/snapdir"
    32  
    33  	"github.com/snapcore/snapd/testutil"
    34  )
    35  
    36  type FileSuite struct{}
    37  
    38  var _ = Suite(&FileSuite{})
    39  
    40  func (s *FileSuite) TestFileOpenForSnapDir(c *C) {
    41  	sd := c.MkDir()
    42  	snapYaml := filepath.Join(sd, "meta", "snap.yaml")
    43  	err := os.MkdirAll(filepath.Dir(snapYaml), 0755)
    44  	c.Assert(err, IsNil)
    45  	err = ioutil.WriteFile(snapYaml, []byte(`name: foo`), 0644)
    46  	c.Assert(err, IsNil)
    47  
    48  	f, err := snap.Open(sd)
    49  	c.Assert(err, IsNil)
    50  	c.Assert(f, FitsTypeOf, &snapdir.SnapDir{})
    51  }
    52  
    53  func (s *FileSuite) TestFileOpenForSnapDirErrors(c *C) {
    54  	_, err := snap.Open(c.MkDir())
    55  	c.Assert(err, FitsTypeOf, snap.NotSnapError{})
    56  	c.Assert(err, ErrorMatches, `"/.*" is not a snap or snapdir`)
    57  }
    58  
    59  type validateSuite struct {
    60  	testutil.BaseTest
    61  	log func(string, ...interface{})
    62  }
    63  
    64  var _ = Suite(&validateSuite{})
    65  
    66  func discard(string, ...interface{}) {}
    67  
    68  func (s *validateSuite) SetUpTest(c *C) {
    69  	s.BaseTest.SetUpTest(c)
    70  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    71  }
    72  
    73  func (s *validateSuite) TearDownTest(c *C) {
    74  	s.BaseTest.TearDownTest(c)
    75  }
    76  
    77  func (s *validateSuite) TestValidateContainerReallyEmptyFails(c *C) {
    78  	const yaml = `name: empty-snap
    79  version: 1
    80  `
    81  	d := c.MkDir()
    82  	// the snap dir is a 0700 directory with nothing in it
    83  
    84  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
    85  	c.Assert(err, IsNil)
    86  
    87  	err = snap.ValidateContainer(snapdir.New(d), info, discard)
    88  	c.Check(err, Equals, snap.ErrMissingPaths)
    89  }
    90  
    91  func (s *validateSuite) TestValidateContainerEmptyButBadPermFails(c *C) {
    92  	const yaml = `name: empty-snap
    93  version: 1
    94  `
    95  	d := c.MkDir()
    96  
    97  	stat, err := os.Stat(d)
    98  	c.Assert(err, IsNil)
    99  	c.Check(stat.Mode().Perm(), Equals, os.FileMode(0700)) // just to be sure
   100  
   101  	c.Assert(os.Mkdir(filepath.Join(d, "meta"), 0755), IsNil)
   102  	c.Assert(ioutil.WriteFile(filepath.Join(d, "meta", "snap.yaml"), nil, 0444), IsNil)
   103  
   104  	// snapdir has /meta/snap.yaml, but / is 0700
   105  
   106  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   107  	c.Assert(err, IsNil)
   108  
   109  	err = snap.ValidateContainer(snapdir.New(d), info, discard)
   110  	c.Check(err, Equals, snap.ErrBadModes)
   111  }
   112  
   113  func (s *validateSuite) TestValidateContainerMissingSnapYamlFails(c *C) {
   114  	const yaml = `name: empty-snap
   115  version: 1
   116  `
   117  	d := c.MkDir()
   118  	c.Assert(os.Chmod(d, 0755), IsNil)
   119  	c.Assert(os.Mkdir(filepath.Join(d, "meta"), 0755), IsNil)
   120  
   121  	// snapdir's / and /meta are 0755 (i.e. OK), but no /meta/snap.yaml
   122  
   123  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   124  	c.Assert(err, IsNil)
   125  
   126  	err = snap.ValidateContainer(snapdir.New(d), info, discard)
   127  	c.Check(err, Equals, snap.ErrMissingPaths)
   128  }
   129  
   130  func (s *validateSuite) TestValidateContainerSnapYamlBadPermsFails(c *C) {
   131  	const yaml = `name: empty-snap
   132  version: 1
   133  `
   134  	d := c.MkDir()
   135  	c.Assert(os.Chmod(d, 0755), IsNil)
   136  	c.Assert(os.Mkdir(filepath.Join(d, "meta"), 0755), IsNil)
   137  	c.Assert(ioutil.WriteFile(filepath.Join(d, "meta", "snap.yaml"), nil, 0), IsNil)
   138  
   139  	// snapdir's / and /meta are 0755 (i.e. OK),
   140  	// /meta/snap.yaml exists, but isn't readable
   141  
   142  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   143  	c.Assert(err, IsNil)
   144  
   145  	err = snap.ValidateContainer(snapdir.New(d), info, discard)
   146  	c.Check(err, Equals, snap.ErrBadModes)
   147  }
   148  
   149  func (s *validateSuite) TestValidateContainerSnapYamlNonRegularFails(c *C) {
   150  	const yaml = `name: empty-snap
   151  version: 1
   152  `
   153  	d := c.MkDir()
   154  	c.Assert(os.Chmod(d, 0755), IsNil)
   155  	c.Assert(os.Mkdir(filepath.Join(d, "meta"), 0755), IsNil)
   156  	c.Assert(syscall.Mkfifo(filepath.Join(d, "meta", "snap.yaml"), 0444), IsNil)
   157  
   158  	// snapdir's / and /meta are 0755 (i.e. OK),
   159  	// /meta/snap.yaml exists, is readable, but isn't a file
   160  
   161  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   162  	c.Assert(err, IsNil)
   163  
   164  	err = snap.ValidateContainer(snapdir.New(d), info, discard)
   165  	c.Check(err, Equals, snap.ErrBadModes)
   166  }
   167  
   168  // emptyContainer returns a minimal container that passes
   169  // ValidateContainer: / and /meta exist and are 0755, and
   170  // /meta/snap.yaml is a regular world-readable file.
   171  func emptyContainer(c *C) *snapdir.SnapDir {
   172  	d := c.MkDir()
   173  	c.Assert(os.Chmod(d, 0755), IsNil)
   174  	c.Assert(os.Mkdir(filepath.Join(d, "meta"), 0755), IsNil)
   175  	c.Assert(ioutil.WriteFile(filepath.Join(d, "meta", "snap.yaml"), nil, 0444), IsNil)
   176  	return snapdir.New(d)
   177  }
   178  
   179  func (s *validateSuite) TestValidateContainerMinimalOKPermWorks(c *C) {
   180  	const yaml = `name: empty-snap
   181  version: 1
   182  `
   183  	d := emptyContainer(c)
   184  	// snapdir's / and /meta are 0755 (i.e. OK),
   185  	// /meta/snap.yaml exists, is readable regular file
   186  	// (this could be considered a test of emptyContainer)
   187  
   188  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   189  	c.Assert(err, IsNil)
   190  
   191  	err = snap.ValidateContainer(d, info, discard)
   192  	c.Check(err, IsNil)
   193  }
   194  
   195  func (s *validateSuite) TestValidateContainerMissingAppsFails(c *C) {
   196  	const yaml = `name: empty-snap
   197  version: 1
   198  apps:
   199   foo:
   200    command: foo
   201  `
   202  	d := emptyContainer(c)
   203  	// snapdir is empty: no apps
   204  
   205  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   206  	c.Assert(err, IsNil)
   207  
   208  	err = snap.ValidateContainer(d, info, discard)
   209  	c.Check(err, Equals, snap.ErrMissingPaths)
   210  }
   211  
   212  func (s *validateSuite) TestValidateContainerBadAppPermsFails(c *C) {
   213  	const yaml = `name: empty-snap
   214  version: 1
   215  apps:
   216   foo:
   217    command: foo
   218  `
   219  	d := emptyContainer(c)
   220  	c.Assert(ioutil.WriteFile(filepath.Join(d.Path(), "foo"), nil, 0444), IsNil)
   221  
   222  	// snapdir contains the app, but the app is not executable
   223  
   224  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   225  	c.Assert(err, IsNil)
   226  
   227  	err = snap.ValidateContainer(d, info, discard)
   228  	c.Check(err, Equals, snap.ErrBadModes)
   229  }
   230  
   231  func (s *validateSuite) TestValidateContainerBadAppDirPermsFails(c *C) {
   232  	const yaml = `name: empty-snap
   233  version: 1
   234  apps:
   235   foo:
   236    command: apps/foo
   237  `
   238  	d := emptyContainer(c)
   239  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "apps"), 0700), IsNil)
   240  	c.Assert(ioutil.WriteFile(filepath.Join(d.Path(), "apps", "foo"), nil, 0555), IsNil)
   241  
   242  	// snapdir contains executable app, but path to executable isn't rx
   243  
   244  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   245  	c.Assert(err, IsNil)
   246  
   247  	err = snap.ValidateContainer(d, info, discard)
   248  	c.Check(err, Equals, snap.ErrBadModes)
   249  }
   250  
   251  func (s *validateSuite) TestValidateContainerBadSvcPermsFails(c *C) {
   252  	const yaml = `name: empty-snap
   253  version: 1
   254  apps:
   255   bar:
   256    command: svcs/bar
   257    daemon: simple
   258  `
   259  	d := emptyContainer(c)
   260  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "svcs"), 0755), IsNil)
   261  	c.Assert(ioutil.WriteFile(filepath.Join(d.Path(), "svcs", "bar"), nil, 0), IsNil)
   262  
   263  	// snapdir contains service, but it isn't executable
   264  
   265  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   266  	c.Assert(err, IsNil)
   267  
   268  	err = snap.ValidateContainer(d, info, discard)
   269  	c.Check(err, Equals, snap.ErrBadModes)
   270  }
   271  
   272  func (s *validateSuite) TestValidateContainerCompleterFails(c *C) {
   273  	const yaml = `name: empty-snap
   274  version: 1
   275  apps:
   276   foo:
   277    command: cmds/foo
   278    completer: comp/foo.sh
   279  `
   280  	d := emptyContainer(c)
   281  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "cmds"), 0755), IsNil)
   282  	c.Assert(ioutil.WriteFile(filepath.Join(d.Path(), "cmds", "foo"), nil, 0555), IsNil)
   283  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "comp"), 0755), IsNil)
   284  
   285  	// snapdir contains executable app, in a rx path, but refers
   286  	// to a completer that doesn't exist
   287  
   288  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   289  	c.Assert(err, IsNil)
   290  
   291  	err = snap.ValidateContainer(d, info, discard)
   292  	c.Check(err, Equals, snap.ErrMissingPaths)
   293  }
   294  
   295  func (s *validateSuite) TestValidateContainerBadAppPathOK(c *C) {
   296  	// we actually support this, but don't validate it here
   297  	const yaml = `name: empty-snap
   298  version: 1
   299  apps:
   300   foo:
   301    command: ../../../bin/echo
   302  `
   303  	d := emptyContainer(c)
   304  
   305  	// snapdir does not contain the app, but the command is
   306  	// "outside" so it might be OK
   307  
   308  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   309  	c.Assert(err, IsNil)
   310  
   311  	err = snap.ValidateContainer(d, info, discard)
   312  	c.Check(err, IsNil)
   313  }
   314  
   315  func (s *validateSuite) TestValidateContainerSymlinksFails(c *C) {
   316  	c.Skip("checking symlink targets not implemented yet")
   317  	const yaml = `name: empty-snap
   318  version: 1
   319  apps:
   320   foo:
   321    command: foo
   322  `
   323  	d := emptyContainer(c)
   324  	fn := filepath.Join(d.Path(), "foo")
   325  	c.Assert(ioutil.WriteFile(fn+".real", nil, 0444), IsNil)
   326  	c.Assert(os.Symlink(fn+".real", fn), IsNil)
   327  
   328  	// snapdir contains a command that's a symlink to a file that's not world-rx
   329  
   330  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   331  	c.Assert(err, IsNil)
   332  
   333  	err = snap.ValidateContainer(d, info, discard)
   334  	c.Check(err, Equals, snap.ErrBadModes)
   335  }
   336  
   337  func (s *validateSuite) TestValidateContainerSymlinksOK(c *C) {
   338  	const yaml = `name: empty-snap
   339  version: 1
   340  apps:
   341   foo:
   342    command: foo
   343  `
   344  	d := emptyContainer(c)
   345  	fn := filepath.Join(d.Path(), "foo")
   346  	c.Assert(ioutil.WriteFile(fn+".real", nil, 0555), IsNil)
   347  	c.Assert(os.Symlink(fn+".real", fn), IsNil)
   348  
   349  	// snapdir contains a command that's a symlink to a file that's world-rx
   350  
   351  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   352  	c.Assert(err, IsNil)
   353  
   354  	err = snap.ValidateContainer(d, info, discard)
   355  	c.Check(err, IsNil)
   356  }
   357  
   358  func (s *validateSuite) TestValidateContainerAppsOK(c *C) {
   359  	const yaml = `name: empty-snap
   360  version: 1
   361  apps:
   362   foo:
   363    command: cmds/foo
   364    completer: comp/foo.sh
   365   bar:
   366    command: svcs/bar
   367    daemon: simple
   368   baz:
   369    command: cmds/foo --with=baz
   370   quux:
   371    command: cmds/foo
   372    daemon: simple
   373   meep:
   374    command: comp/foo.sh
   375    daemon: simple
   376  `
   377  	d := emptyContainer(c)
   378  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "cmds"), 0755), IsNil)
   379  	c.Assert(ioutil.WriteFile(filepath.Join(d.Path(), "cmds", "foo"), nil, 0555), IsNil)
   380  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "comp"), 0755), IsNil)
   381  	c.Assert(ioutil.WriteFile(filepath.Join(d.Path(), "comp", "foo.sh"), nil, 0555), IsNil)
   382  
   383  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "svcs"), 0700), IsNil)
   384  	c.Assert(ioutil.WriteFile(filepath.Join(d.Path(), "svcs", "bar"), nil, 0500), IsNil)
   385  
   386  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "garbage"), 0755), IsNil)
   387  	c.Assert(os.Mkdir(filepath.Join(d.Path(), "garbage", "zero"), 0), IsNil)
   388  	defer os.Chmod(filepath.Join(d.Path(), "garbage", "zero"), 0755)
   389  
   390  	// snapdir contains:
   391  	//  * a command that's world-rx, and its directory is
   392  	//    world-rx, and its completer is world-r in a world-rx
   393  	//    directory
   394  	// * a service that's root-executable, and its directory is
   395  	//   not readable nor searchable - and that's OK! (NOTE as
   396  	//   this test should pass as non-rooot, the directory is 0700
   397  	//   instead of 0000)
   398  	// * a command with arguments
   399  	// * a service that is also a command
   400  	// * a service that is also a completer (WAT)
   401  	// * an extra directory only root can look at (this would fail
   402  	//   if not running the suite as root, and SkipDir didn't
   403  	//   work)
   404  
   405  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   406  	c.Assert(err, IsNil)
   407  
   408  	err = snap.ValidateContainer(d, info, discard)
   409  	c.Check(err, IsNil)
   410  }