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