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