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