github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/usersession/userd/launcher_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 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 userd_test 21 22 import ( 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 "syscall" 27 "testing" 28 29 "github.com/godbus/dbus" 30 31 . "gopkg.in/check.v1" 32 33 "github.com/snapcore/snapd/osutil/sys" 34 "github.com/snapcore/snapd/testutil" 35 "github.com/snapcore/snapd/usersession/userd" 36 ) 37 38 func Test(t *testing.T) { TestingT(t) } 39 40 type launcherSuite struct { 41 launcher *userd.Launcher 42 43 mockXdgOpen *testutil.MockCmd 44 restoreSnapFromSender func() 45 } 46 47 var _ = Suite(&launcherSuite{}) 48 49 func (s *launcherSuite) SetUpTest(c *C) { 50 s.launcher = &userd.Launcher{} 51 s.mockXdgOpen = testutil.MockCommand(c, "xdg-open", "") 52 s.restoreSnapFromSender = userd.MockSnapFromSender(func(*dbus.Conn, dbus.Sender) (string, error) { 53 return "some-snap", nil 54 }) 55 } 56 57 func (s *launcherSuite) TearDownTest(c *C) { 58 s.mockXdgOpen.Restore() 59 s.restoreSnapFromSender() 60 } 61 62 func (s *launcherSuite) TestOpenURLWithNotAllowedScheme(c *C) { 63 for _, t := range []struct { 64 url string 65 errMatcher string 66 }{ 67 {"tel://049112233445566", "Supplied URL scheme \"tel\" is not allowed"}, 68 {"aabbccdd0011", "Supplied URL scheme \"\" is not allowed"}, 69 {"invälid:%url", dbus.ErrMsgInvalidArg.Error()}, 70 } { 71 err := s.launcher.OpenURL(t.url, ":some-dbus-sender") 72 c.Assert(err, ErrorMatches, t.errMatcher) 73 c.Assert(s.mockXdgOpen.Calls(), IsNil) 74 } 75 } 76 77 func (s *launcherSuite) TestOpenURLWithAllowedSchemeHappy(c *C) { 78 for _, schema := range []string{"http", "https", "mailto", "snap", "help", "apt", "zoommtg", "zoomus", "zoomphonecall", "slack", "msteams"} { 79 err := s.launcher.OpenURL(schema+"://snapcraft.io", ":some-dbus-sender") 80 c.Assert(err, IsNil) 81 c.Assert(s.mockXdgOpen.Calls(), DeepEquals, [][]string{ 82 {"xdg-open", schema + "://snapcraft.io"}, 83 }) 84 s.mockXdgOpen.ForgetCalls() 85 } 86 } 87 88 func (s *launcherSuite) TestOpenURLWithFailingXdgOpen(c *C) { 89 cmd := testutil.MockCommand(c, "xdg-open", "false") 90 defer cmd.Restore() 91 92 err := s.launcher.OpenURL("https://snapcraft.io", ":some-dbus-sender") 93 c.Assert(err, NotNil) 94 c.Assert(err, ErrorMatches, "cannot open supplied URL") 95 } 96 97 func mockUICommands(c *C, script string) (restore func()) { 98 mock := testutil.MockCommand(c, "zenity", script) 99 mock.Also("kdialog", script) 100 101 return func() { 102 mock.Restore() 103 } 104 } 105 106 func (s *launcherSuite) TestOpenFileUserAccepts(c *C) { 107 restore := mockUICommands(c, "true") 108 defer restore() 109 110 path := filepath.Join(c.MkDir(), "test.txt") 111 c.Assert(ioutil.WriteFile(path, []byte("Hello world"), 0644), IsNil) 112 113 file, err := os.Open(path) 114 c.Assert(err, IsNil) 115 defer file.Close() 116 117 dupFd, err := syscall.Dup(int(file.Fd())) 118 c.Assert(err, IsNil) 119 120 err = s.launcher.OpenFile("", dbus.UnixFD(dupFd), ":some-dbus-sender") 121 c.Assert(err, IsNil) 122 c.Assert(s.mockXdgOpen.Calls(), DeepEquals, [][]string{ 123 {"xdg-open", path}, 124 }) 125 } 126 127 func (s *launcherSuite) TestOpenFileUserDeclines(c *C) { 128 restore := mockUICommands(c, "false") 129 defer restore() 130 131 path := filepath.Join(c.MkDir(), "test.txt") 132 c.Assert(ioutil.WriteFile(path, []byte("Hello world"), 0644), IsNil) 133 134 file, err := os.Open(path) 135 c.Assert(err, IsNil) 136 defer file.Close() 137 138 dupFd, err := syscall.Dup(int(file.Fd())) 139 c.Assert(err, IsNil) 140 141 err = s.launcher.OpenFile("", dbus.UnixFD(dupFd), ":some-dbus-sender") 142 c.Assert(err, NotNil) 143 c.Assert(err, ErrorMatches, "permission denied") 144 c.Assert(s.mockXdgOpen.Calls(), IsNil) 145 } 146 147 func (s *launcherSuite) TestOpenFileSucceedsWithDirectory(c *C) { 148 restore := mockUICommands(c, "true") 149 defer restore() 150 151 dir := c.MkDir() 152 fd, err := syscall.Open(dir, syscall.O_RDONLY|syscall.O_DIRECTORY, 0755) 153 c.Assert(err, IsNil) 154 defer syscall.Close(fd) 155 156 dupFd, err := syscall.Dup(fd) 157 c.Assert(err, IsNil) 158 159 err = s.launcher.OpenFile("", dbus.UnixFD(dupFd), ":some-dbus-sender") 160 c.Assert(err, IsNil) 161 c.Assert(s.mockXdgOpen.Calls(), DeepEquals, [][]string{ 162 {"xdg-open", dir}, 163 }) 164 } 165 166 func (s *launcherSuite) TestOpenFileFailsWithDeviceFile(c *C) { 167 restore := mockUICommands(c, "true") 168 defer restore() 169 170 file, err := os.Open("/dev/null") 171 c.Assert(err, IsNil) 172 defer file.Close() 173 174 dupFd, err := syscall.Dup(int(file.Fd())) 175 c.Assert(err, IsNil) 176 177 err = s.launcher.OpenFile("", dbus.UnixFD(dupFd), ":some-dbus-sender") 178 c.Assert(err, NotNil) 179 c.Assert(err, ErrorMatches, "cannot open anything other than regular files or directories") 180 c.Assert(s.mockXdgOpen.Calls(), IsNil) 181 } 182 183 func (s *launcherSuite) TestOpenFileFailsWithPathDescriptor(c *C) { 184 restore := mockUICommands(c, "true") 185 defer restore() 186 187 dir := c.MkDir() 188 fd, err := syscall.Open(dir, sys.O_PATH, 0755) 189 c.Assert(err, IsNil) 190 defer syscall.Close(fd) 191 192 dupFd, err := syscall.Dup(fd) 193 c.Assert(err, IsNil) 194 195 err = s.launcher.OpenFile("", dbus.UnixFD(dupFd), ":some-dbus-sender") 196 c.Assert(err, NotNil) 197 c.Assert(err, ErrorMatches, "cannot use file descriptors opened using O_PATH") 198 c.Assert(s.mockXdgOpen.Calls(), IsNil) 199 }