github.com/tompreston/snapd@v0.0.0-20210817193607-954edfcb9611/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 }