github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/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 }