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 }