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