github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/builtin/bool_file_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 builtin_test 21 22 import ( 23 "fmt" 24 "testing" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/interfaces" 29 "github.com/snapcore/snapd/interfaces/apparmor" 30 "github.com/snapcore/snapd/interfaces/builtin" 31 "github.com/snapcore/snapd/interfaces/dbus" 32 "github.com/snapcore/snapd/interfaces/seccomp" 33 "github.com/snapcore/snapd/interfaces/udev" 34 "github.com/snapcore/snapd/snap" 35 "github.com/snapcore/snapd/snap/snaptest" 36 "github.com/snapcore/snapd/testutil" 37 ) 38 39 func Test(t *testing.T) { 40 TestingT(t) 41 } 42 43 type BoolFileInterfaceSuite struct { 44 testutil.BaseTest 45 iface interfaces.Interface 46 gpioSlotInfo *snap.SlotInfo 47 gpioSlot *interfaces.ConnectedSlot 48 ledSlotInfo *snap.SlotInfo 49 ledSlot *interfaces.ConnectedSlot 50 badPathSlotInfo *snap.SlotInfo 51 badPathSlot *interfaces.ConnectedSlot 52 parentDirPathSlotInfo *snap.SlotInfo 53 parentDirPathSlot *interfaces.ConnectedSlot 54 missingPathSlotInfo *snap.SlotInfo 55 missingPathSlot *interfaces.ConnectedSlot 56 badInterfaceSlot *interfaces.ConnectedSlot 57 plugInfo *snap.PlugInfo 58 plug *interfaces.ConnectedPlug 59 badInterfaceSlotInfo *snap.SlotInfo 60 badInterfacePlugInfo *snap.PlugInfo 61 badInterfacePlug *interfaces.ConnectedPlug 62 } 63 64 var _ = Suite(&BoolFileInterfaceSuite{ 65 iface: builtin.MustInterface("bool-file"), 66 }) 67 68 func (s *BoolFileInterfaceSuite) SetUpTest(c *C) { 69 plugSnapinfo := snaptest.MockInfo(c, ` 70 name: other 71 version: 0 72 plugs: 73 plug: bool-file 74 apps: 75 app: 76 command: foo 77 `, nil) 78 info := snaptest.MockInfo(c, ` 79 name: ubuntu-core 80 version: 0 81 slots: 82 gpio: 83 interface: bool-file 84 path: /sys/class/gpio/gpio13/value 85 led: 86 interface: bool-file 87 path: "/sys/class/leds/input27::capslock/brightness" 88 missing-path: bool-file 89 bad-path: 90 interface: bool-file 91 path: path 92 parent-dir-path: 93 interface: bool-file 94 path: "/sys/class/gpio/../value" 95 bad-interface-slot: other-interface 96 plugs: 97 plug: bool-file 98 bad-interface-plug: other-interface 99 `, &snap.SideInfo{}) 100 s.gpioSlotInfo = info.Slots["gpio"] 101 s.gpioSlot = interfaces.NewConnectedSlot(s.gpioSlotInfo, nil, nil) 102 s.ledSlotInfo = info.Slots["led"] 103 s.ledSlot = interfaces.NewConnectedSlot(s.ledSlotInfo, nil, nil) 104 s.missingPathSlotInfo = info.Slots["missing-path"] 105 s.missingPathSlot = interfaces.NewConnectedSlot(s.missingPathSlotInfo, nil, nil) 106 s.badPathSlotInfo = info.Slots["bad-path"] 107 s.badPathSlot = interfaces.NewConnectedSlot(s.badPathSlotInfo, nil, nil) 108 s.parentDirPathSlotInfo = info.Slots["parent-dir-path"] 109 s.parentDirPathSlot = interfaces.NewConnectedSlot(s.parentDirPathSlotInfo, nil, nil) 110 s.badInterfaceSlotInfo = info.Slots["bad-interface-slot"] 111 s.badInterfaceSlot = interfaces.NewConnectedSlot(s.badInterfaceSlotInfo, nil, nil) 112 s.plugInfo = plugSnapinfo.Plugs["plug"] 113 s.plug = interfaces.NewConnectedPlug(s.plugInfo, nil, nil) 114 s.badInterfacePlugInfo = info.Plugs["bad-interface-plug"] 115 s.badInterfacePlug = interfaces.NewConnectedPlug(s.badInterfacePlugInfo, nil, nil) 116 } 117 118 // TODO: add test for permanent slot when we have hook support. 119 120 func (s *BoolFileInterfaceSuite) TestName(c *C) { 121 c.Assert(s.iface.Name(), Equals, "bool-file") 122 } 123 124 func (s *BoolFileInterfaceSuite) TestSanitizeSlot(c *C) { 125 // Both LED and GPIO slots are accepted 126 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.ledSlotInfo), IsNil) 127 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.gpioSlotInfo), IsNil) 128 // Slots without the "path" attribute are rejected. 129 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.missingPathSlotInfo), ErrorMatches, 130 "bool-file must contain the path attribute") 131 // Slots without the "path" attribute are rejected. 132 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.parentDirPathSlotInfo), ErrorMatches, 133 "bool-file can only point at LED brightness or GPIO value") 134 // Slots with incorrect value of the "path" attribute are rejected. 135 c.Assert(interfaces.BeforePrepareSlot(s.iface, s.badPathSlotInfo), ErrorMatches, 136 "bool-file can only point at LED brightness or GPIO value") 137 } 138 139 func (s *BoolFileInterfaceSuite) TestSanitizePlug(c *C) { 140 c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil) 141 } 142 143 func (s *BoolFileInterfaceSuite) TestPlugSnippetHandlesSymlinkErrors(c *C) { 144 // Symbolic link traversal is handled correctly 145 builtin.MockEvalSymlinks(&s.BaseTest, func(path string) (string, error) { 146 return "", fmt.Errorf("broken symbolic link") 147 }) 148 149 apparmorSpec := &apparmor.Specification{} 150 err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.gpioSlot) 151 c.Assert(err, ErrorMatches, "cannot compute plug security snippet: broken symbolic link") 152 c.Assert(apparmorSpec.SecurityTags(), HasLen, 0) 153 } 154 155 func (s *BoolFileInterfaceSuite) TestPlugSnippetDereferencesSymlinks(c *C) { 156 // Use a fake (successful) dereferencing function for the remainder of the test. 157 builtin.MockEvalSymlinks(&s.BaseTest, func(path string) (string, error) { 158 return "(dereferenced)" + path, nil 159 }) 160 // Extra apparmor permission to access GPIO value 161 // The path uses dereferenced symbolic links. 162 apparmorSpec := &apparmor.Specification{} 163 err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.gpioSlot) 164 c.Assert(err, IsNil) 165 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app"}) 166 c.Assert(apparmorSpec.SnippetForTag("snap.other.app"), Equals, "(dereferenced)/sys/class/gpio/gpio13/value rwk,") 167 // Extra apparmor permission to access LED brightness. 168 // The path uses dereferenced symbolic links. 169 apparmorSpec = &apparmor.Specification{} 170 err = apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.ledSlot) 171 c.Assert(err, IsNil) 172 c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app"}) 173 c.Assert(apparmorSpec.SnippetForTag("snap.other.app"), Equals, "(dereferenced)/sys/class/leds/input27::capslock/brightness rwk,") 174 } 175 176 func (s *BoolFileInterfaceSuite) TestConnectedPlugSnippetPanicksOnUnsanitizedSlots(c *C) { 177 // Unsanitized slots should never be used and cause a panic. 178 c.Assert(func() { 179 apparmorSpec := &apparmor.Specification{} 180 apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.missingPathSlot) 181 }, PanicMatches, "slot is not sanitized") 182 } 183 184 func (s *BoolFileInterfaceSuite) TestConnectedPlugSnippetUnusedSecuritySystems(c *C) { 185 for _, slot := range []*interfaces.ConnectedSlot{s.ledSlot, s.gpioSlot} { 186 // No extra seccomp permissions for plug 187 seccompSpec := &seccomp.Specification{} 188 err := seccompSpec.AddConnectedPlug(s.iface, s.plug, slot) 189 c.Assert(err, IsNil) 190 c.Assert(seccompSpec.Snippets(), HasLen, 0) 191 // No extra dbus permissions for plug 192 dbusSpec := &dbus.Specification{} 193 err = dbusSpec.AddConnectedPlug(s.iface, s.plug, slot) 194 c.Assert(err, IsNil) 195 c.Assert(dbusSpec.Snippets(), HasLen, 0) 196 // No extra udev permissions for plug 197 udevSpec := &udev.Specification{} 198 c.Assert(udevSpec.AddConnectedPlug(s.iface, s.plug, slot), IsNil) 199 c.Assert(udevSpec.Snippets(), HasLen, 0) 200 } 201 } 202 203 func (s *BoolFileInterfaceSuite) TestPermanentPlugSnippetUnusedSecuritySystems(c *C) { 204 // No extra seccomp permissions for plug 205 seccompSpec := &seccomp.Specification{} 206 err := seccompSpec.AddPermanentPlug(s.iface, s.plugInfo) 207 c.Assert(err, IsNil) 208 c.Assert(seccompSpec.Snippets(), HasLen, 0) 209 // No extra dbus permissions for plug 210 dbusSpec := &dbus.Specification{} 211 err = dbusSpec.AddPermanentPlug(s.iface, s.plugInfo) 212 c.Assert(err, IsNil) 213 c.Assert(dbusSpec.Snippets(), HasLen, 0) 214 // No extra udev permissions for plug 215 udevSpec := &udev.Specification{} 216 c.Assert(udevSpec.AddPermanentPlug(s.iface, s.plugInfo), IsNil) 217 c.Assert(udevSpec.Snippets(), HasLen, 0) 218 } 219 220 func (s *BoolFileInterfaceSuite) TestInterfaces(c *C) { 221 c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface) 222 }