github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/usersession/userd/privileged_desktop_launcher_internal_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2021 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 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "strings" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/dirs" 32 "github.com/snapcore/snapd/release" 33 "github.com/snapcore/snapd/strutil" 34 "github.com/snapcore/snapd/testutil" 35 "github.com/snapcore/snapd/usersession/userd" 36 ) 37 38 type privilegedDesktopLauncherInternalSuite struct { 39 testutil.BaseTest 40 } 41 42 var _ = Suite(&privilegedDesktopLauncherInternalSuite{}) 43 44 var mockFileSystem = []string{ 45 "/var/lib/snapd/desktop/applications/mir-kiosk-scummvm_mir-kiosk-scummvm.desktop", 46 "/var/lib/snapd/desktop/applications/multipass_multipass-gui.desktop", 47 "/var/lib/snapd/desktop/applications/cevelop_cevelop.desktop", 48 "/var/lib/snapd/desktop/applications/egmde-confined-desktop_egmde-confined-desktop.desktop", 49 "/var/lib/snapd/desktop/applications/classic-snap-analyzer_classic-snap-analyzer.desktop", 50 "/var/lib/snapd/desktop/applications/vlc_vlc.desktop", 51 "/var/lib/snapd/desktop/applications/gnome-calculator_gnome-calculator.desktop", 52 "/var/lib/snapd/desktop/applications/mir-kiosk-kodi_mir-kiosk-kodi.desktop", 53 "/var/lib/snapd/desktop/applications/gnome-characters_gnome-characters.desktop", 54 "/var/lib/snapd/desktop/applications/clion_clion.desktop", 55 "/var/lib/snapd/desktop/applications/gnome-system-monitor_gnome-system-monitor.desktop", 56 "/var/lib/snapd/desktop/applications/inkscape_inkscape.desktop", 57 "/var/lib/snapd/desktop/applications/gnome-logs_gnome-logs.desktop", 58 "/var/lib/snapd/desktop/applications/foo-bar/baz.desktop", 59 "/var/lib/snapd/desktop/applications/baz/foo-bar.desktop", 60 61 // A desktop file ID provided by a snap may be shadowed by the 62 // host system. 63 "/usr/share/applications/shadow-test.desktop", 64 "/var/lib/snapd/desktop/applications/shadow-test.desktop", 65 } 66 67 var chromiumDesktopFile = `[Desktop Entry] 68 X-SnapInstanceName=chromium 69 Version=1.0 70 Name=Chromium Web Browser 71 Name[ast]=Restolador web Chromium 72 Name[bg]=Уеб четец Chromium 73 Name[bn]=ক্রোমিয়াম ওয়েব ব্রাউজার 74 Name[bs]=Chromium web preglednik 75 Name[ca]=Navegador web Chromium 76 Name[ca@valencia]=Navegador web Chromium 77 Name[da]=Chromium netbrowser 78 Name[de]=Chromium-Webbrowser 79 Name[en_AU]=Chromium Web Browser 80 Name[eo]=Kromiumo retfoliumilo 81 Name[es]=Navegador web Chromium 82 Name[et]=Chromiumi veebibrauser 83 Name[eu]=Chromium web-nabigatzailea 84 Name[fi]=Chromium-selain 85 Name[fr]=Navigateur Web Chromium 86 Name[gl]=Navegador web Chromium 87 Name[he]=דפדפן האינטרנט כרומיום 88 Name[hr]=Chromium web preglednik 89 Name[hu]=Chromium webböngésző 90 Name[hy]=Chromium ոստայն զննարկիչ 91 Name[ia]=Navigator del web Chromium 92 Name[id]=Peramban Web Chromium 93 Name[it]=Browser web Chromium 94 Name[ja]=Chromium ウェブ・ブラウザ 95 Name[ka]=ვებ ბრაუზერი Chromium 96 Name[ko]=Chromium 웹 브라우저 97 Name[kw]=Peurel wias Chromium 98 Name[ms]=Pelayar Web Chromium 99 Name[nb]=Chromium nettleser 100 Name[nl]=Chromium webbrowser 101 Name[pt_BR]=Navegador de Internet Chromium 102 Name[ro]=Navigator Internet Chromium 103 Name[ru]=Веб-браузер Chromium 104 Name[sl]=Chromium spletni brskalnik 105 Name[sv]=Webbläsaren Chromium 106 Name[ug]=Chromium توركۆرگۈ 107 Name[vi]=Trình duyệt Web Chromium 108 Name[zh_CN]=Chromium 网页浏览器 109 Name[zh_HK]=Chromium 網頁瀏覽器 110 Name[zh_TW]=Chromium 網頁瀏覽器 111 GenericName=Web Browser 112 GenericName[ar]=متصفح الشبكة 113 GenericName[ast]=Restolador web 114 GenericName[bg]=Уеб браузър 115 GenericName[bn]=ওয়েব ব্রাউজার 116 GenericName[bs]=Web preglednik 117 GenericName[ca]=Navegador web 118 GenericName[ca@valencia]=Navegador web 119 GenericName[cs]=WWW prohlížeč 120 GenericName[da]=Browser 121 GenericName[de]=Web-Browser 122 GenericName[el]=Περιηγητής ιστού 123 GenericName[en_AU]=Web Browser 124 GenericName[en_GB]=Web Browser 125 GenericName[eo]=Retfoliumilo 126 GenericName[es]=Navegador web 127 GenericName[et]=Veebibrauser 128 GenericName[eu]=Web-nabigatzailea 129 GenericName[fi]=WWW-selain 130 GenericName[fil]=Web Browser 131 GenericName[fr]=Navigateur Web 132 GenericName[gl]=Navegador web 133 GenericName[gu]=વેબ બ્રાઉઝર 134 GenericName[he]=דפדפן אינטרנט 135 GenericName[hi]=वेब ब्राउज़र 136 GenericName[hr]=Web preglednik 137 GenericName[hu]=Webböngésző 138 GenericName[hy]=Ոստայն զննարկիչ 139 GenericName[ia]=Navigator del Web 140 GenericName[id]=Peramban Web 141 GenericName[it]=Browser web 142 GenericName[ja]=ウェブ・ブラウザ 143 GenericName[ka]=ვებ ბრაუზერი 144 GenericName[kn]=ಜಾಲ ವೀಕ್ಷಕ 145 GenericName[ko]=웹 브라우저 146 GenericName[kw]=Peurel wias 147 GenericName[lt]=Žiniatinklio naršyklė 148 GenericName[lv]=Tīmekļa pārlūks 149 GenericName[ml]=വെബ് ബ്രൌസര് 150 GenericName[mr]=वेब ब्राऊजर 151 GenericName[ms]=Pelayar Web 152 GenericName[nb]=Nettleser 153 GenericName[nl]=Webbrowser 154 GenericName[or]=ଓ୍ବେବ ବ୍ରାଉଜର 155 GenericName[pl]=Przeglądarka WWW 156 GenericName[pt]=Navegador Web 157 GenericName[pt_BR]=Navegador web 158 GenericName[ro]=Navigator de Internet 159 GenericName[ru]=Веб-браузер 160 GenericName[sk]=WWW prehliadač 161 GenericName[sl]=Spletni brskalnik 162 GenericName[sr]=Интернет прегледник 163 GenericName[sv]=Webbläsare 164 GenericName[ta]=இணைய உலாவி 165 GenericName[te]=మహాతల అన్వేషి 166 GenericName[th]=เว็บเบราว์เซอร์ 167 GenericName[tr]=Web Tarayıcı 168 GenericName[ug]=توركۆرگۈ 169 GenericName[uk]=Навігатор Тенет 170 GenericName[vi]=Bộ duyệt Web 171 GenericName[zh_CN]=网页浏览器 172 GenericName[zh_HK]=網頁瀏覽器 173 GenericName[zh_TW]=網頁瀏覽器 174 Comment=Access the Internet 175 Comment[ar]=الدخول إلى الإنترنت 176 Comment[ast]=Accesu a Internet 177 Comment[bg]=Достъп до интернет 178 Comment[bn]=ইন্টারনেটে প্রবেশ করুন 179 Comment[bs]=Pristup internetu 180 Comment[ca]=Accediu a Internet 181 Comment[ca@valencia]=Accediu a Internet 182 Comment[cs]=Přístup k internetu 183 Comment[da]=Få adgang til internettet 184 Comment[de]=Internetzugriff 185 Comment[el]=Πρόσβαση στο Διαδίκτυο 186 Comment[en_AU]=Access the Internet 187 Comment[en_GB]=Access the Internet 188 Comment[eo]=Akiri interreton 189 Comment[es]=Acceda a Internet 190 Comment[et]=Pääs Internetti 191 Comment[eu]=Sartu Internetera 192 Comment[fi]=Käytä internetiä 193 Comment[fil]=I-access ang Internet 194 Comment[fr]=Accéder à Internet 195 Comment[gl]=Acceda a Internet 196 Comment[gu]=ઇંટરનેટ ઍક્સેસ કરો 197 Comment[he]=גישה לאינטרנט 198 Comment[hi]=इंटरनेट तक पहुंच स्थापित करें 199 Comment[hr]=Pristupite Internetu 200 Comment[hu]=Az internet elérése 201 Comment[hy]=Մուտք համացանց 202 Comment[ia]=Accede a le Interrete 203 Comment[id]=Akses Internet 204 Comment[it]=Accesso a Internet 205 Comment[ja]=インターネットにアクセス 206 Comment[ka]=ინტერნეტში შესვლა 207 Comment[kn]=ಇಂಟರ್ನೆಟ್ ಅನ್ನು ಪ್ರವೇಶಿಸಿ 208 Comment[ko]=인터넷에 연결합니다 209 Comment[kw]=Hedhes an Kesrosweyth 210 Comment[lt]=Interneto prieiga 211 Comment[lv]=Piekļūt internetam 212 Comment[ml]=ഇന്റര്നെറ്റ് ആക്സസ് ചെയ്യുക 213 Comment[mr]=इंटरनेटमध्ये प्रवेश करा 214 Comment[ms]=Mengakses Internet 215 Comment[nb]=Bruk internett 216 Comment[nl]=Verbinding maken met internet 217 Comment[or]=ଇଣ୍ଟର୍ନେଟ୍ ପ୍ରବେଶ କରନ୍ତୁ 218 Comment[pl]=Skorzystaj z internetu 219 Comment[pt]=Aceder à Internet 220 Comment[pt_BR]=Acessar a internet 221 Comment[ro]=Accesați Internetul 222 Comment[ru]=Доступ в Интернет 223 Comment[sk]=Prístup do siete Internet 224 Comment[sl]=Dostop do interneta 225 Comment[sr]=Приступите Интернету 226 Comment[sv]=Surfa på Internet 227 Comment[ta]=இணையத்தை அணுகுதல் 228 Comment[te]=ఇంటర్నెట్ను ఆక్సెస్ చెయ్యండి 229 Comment[th]=เข้าถึงอินเทอร์เน็ต 230 Comment[tr]=İnternet'e erişin 231 Comment[ug]=ئىنتېرنېت زىيارىتى 232 Comment[uk]=Доступ до Інтернету 233 Comment[vi]=Truy cập Internet 234 Comment[zh_CN]=访问互联网 235 Comment[zh_HK]=連線到網際網路 236 Comment[zh_TW]=連線到網際網路 237 Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/chromium_chromium.desktop /snap/bin/chromium %U 238 Terminal=false 239 Type=Application 240 Icon=/snap/chromium/1193/chromium.png 241 Categories=Network;WebBrowser; 242 MimeType=text/html;text/xml;application/xhtml_xml;x-scheme-handler/http;x-scheme-handler/https; 243 StartupNotify=true 244 StartupWMClass=chromium 245 Actions=NewWindow;Incognito;TempProfile; 246 247 [Desktop Action NewWindow] 248 Name=Open a New Window 249 Name[ast]=Abrir una Ventana Nueva 250 Name[bg]=Отваряне на Нов прозорец 251 Name[bn]=একটি নতুন উইন্ডো খুলুন 252 Name[bs]=Otvori novi prozor 253 Name[ca]=Obre una finestra nova 254 Name[ca@valencia]=Obri una finestra nova 255 Name[da]=Åbn et nyt vindue 256 Name[de]=Ein neues Fenster öffnen 257 Name[en_AU]=Open a New Window 258 Name[eo]=Malfermi novan fenestron 259 Name[es]=Abrir una ventana nueva 260 Name[et]=Ava uus aken 261 Name[eu]=Ireki leiho berria 262 Name[fi]=Avaa uusi ikkuna 263 Name[fr]=Ouvrir une nouvelle fenêtre 264 Name[gl]=Abrir unha nova xanela 265 Name[he]=פתיחת חלון חדש 266 Name[hy]=Բացել նոր պատուհան 267 Name[ia]=Aperi un nove fenestra 268 Name[it]=Apri una nuova finestra 269 Name[ja]=新しいウィンドウを開く 270 Name[ka]=ახალი ფანჯრის გახსნა 271 Name[kw]=Egery fenester noweth 272 Name[ms]=Buka Tetingkap Baru 273 Name[nb]=Åpne et nytt vindu 274 Name[nl]=Nieuw venster openen 275 Name[pt_BR]=Abre uma nova janela 276 Name[ro]=Deschide o fereastră nouă 277 Name[ru]=Открыть новое окно 278 Name[sl]=Odpri novo okno 279 Name[sv]=Öppna ett nytt fönster 280 Name[ug]=يېڭى كۆزنەك ئاچ 281 Name[uk]=Відкрити нове вікно 282 Name[vi]=Mở cửa sổ mới 283 Name[zh_CN]=打开新窗口 284 Name[zh_TW]=開啟新視窗 285 Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/chromium_chromium.desktop /snap/bin/chromium 286 287 [Desktop Action Incognito] 288 Name=Open a New Window in incognito mode 289 Name[ast]=Abrir una ventana nueva en mou incógnitu 290 Name[bg]=Отваряне на нов прозорец в режим \"инкогнито\" 291 Name[bn]=একটি নতুন উইন্ডো খুলুন ইনকোগনিটো অবস্থায় 292 Name[bs]=Otvori novi prozor u privatnom modu 293 Name[ca]=Obre una finestra nova en mode d'incògnit 294 Name[ca@valencia]=Obri una finestra nova en mode d'incògnit 295 Name[de]=Ein neues Fenster im Inkognito-Modus öffnen 296 Name[en_AU]=Open a New Window in incognito mode 297 Name[eo]=Malfermi novan fenestron nekoniĝeble 298 Name[es]=Abrir una ventana nueva en modo incógnito 299 Name[et]=Ava uus aken tundmatus olekus 300 Name[eu]=Ireki leiho berria isileko moduan 301 Name[fi]=Avaa uusi ikkuna incognito-tilassa 302 Name[fr]=Ouvrir une nouvelle fenêtre en mode navigation privée 303 Name[gl]=Abrir unha nova xanela en modo de incógnito 304 Name[he]=פתיחת חלון חדש במצב גלישה בסתר 305 Name[hy]=Բացել նոր պատուհան ծպտյալ աշխատակերպում 306 Name[ia]=Aperi un nove fenestra in modo incognite 307 Name[it]=Apri una nuova finestra in modalità incognito 308 Name[ja]=新しいシークレット ウィンドウを開く 309 Name[ka]=ახალი ფანჯრის ინკოგნიტოდ გახსნა 310 Name[kw]=Egry fenester noweth en modh privedh 311 Name[ms]=Buka Tetingkap Baru dalam mod menyamar 312 Name[nl]=Nieuw venster openen in incognito-modus 313 Name[pt_BR]=Abrir uma nova janela em modo anônimo 314 Name[ro]=Deschide o fereastră nouă în mod incognito 315 Name[ru]=Открыть новое окно в режиме инкогнито 316 Name[sl]=Odpri novo okno v načinu brez beleženja 317 Name[sv]=Öppna ett nytt inkognitofönster 318 Name[ug]=يوشۇرۇن ھالەتتە يېڭى كۆزنەك ئاچ 319 Name[uk]=Відкрити нове вікно у приватному режимі 320 Name[vi]=Mở cửa sổ mới trong chế độ ẩn danh 321 Name[zh_CN]=以隐身模式打开新窗口 322 Name[zh_TW]=以匿名模式開啟新視窗 323 Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/chromium_chromium.desktop /snap/bin/chromium --incognito 324 325 [Desktop Action TempProfile] 326 Name=Open a New Window with a temporary profile 327 Name[ast]=Abrir una ventana nueva con perfil temporal 328 Name[bg]=Отваряне на Нов прозорец с временен профил 329 Name[bn]=সাময়িক প্রোফাইল সহ একটি নতুন উইন্ডো খুলুন 330 Name[bs]=Otvori novi prozor pomoću privremenog profila 331 Name[ca]=Obre una finestra nova amb un perfil temporal 332 Name[ca@valencia]=Obri una finestra nova amb un perfil temporal 333 Name[de]=Ein neues Fenster mit einem temporären Profil öffnen 334 Name[en_AU]=Open a New Window with a temporary profile 335 Name[eo]=Malfermi novan fenestron portempe 336 Name[es]=Abrir una ventana nueva con perfil temporal 337 Name[et]=Ava uus aken ajutise profiiliga 338 Name[eu]=Ireki leiho berria behin-behineko profil batekin 339 Name[fi]=Avaa uusi ikkuna käyttäen väliaikaista profiilia 340 Name[fr]=Ouvrir une nouvelle fenêtre avec un profil temporaire 341 Name[gl]=Abrir unha nova xanela con perfil temporal 342 Name[he]=פתיחת חלון חדש עם פרופיל זמני 343 Name[hy]=Բացել նոր պատուհան ժամանակավոր հատկագրով 344 Name[ia]=Aperi un nove fenestra con un profilo provisori 345 Name[it]=Apri una nuova finestra con un profilo temporaneo 346 Name[ja]=一時プロファイルで新しいウィンドウを開く 347 Name[ka]=ახალი ფანჯრის გახსნა დროებით პროფილში 348 Name[kw]=Egery fenester noweth gen profil dres prys 349 Name[ms]=Buka Tetingkap Baru dengan profil sementara 350 Name[nb]=Åpne et nytt vindu med en midlertidig profil 351 Name[nl]=Nieuw venster openen met een tijdelijk profiel 352 Name[pt_BR]=Abrir uma nova janela com um perfil temporário 353 Name[ro]=Deschide o fereastră nouă cu un profil temporar 354 Name[ru]=Открыть новое окно с временным профилем 355 Name[sl]=Odpri novo okno z začasnim profilom 356 Name[sv]=Öppna ett nytt fönster med temporär profil 357 Name[ug]=ۋاقىتلىق سەپلىمە ھۆججەت بىلەن يېڭى كۆزنەك ئاچ 358 Name[vi]=Mở cửa sổ mới với hồ sơ tạm 359 Name[zh_CN]=以临时配置文件打开新窗口 360 Name[zh_TW]=以暫時性個人身分開啟新視窗 361 Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/chromium_chromium.desktop /snap/bin/chromium --temp-profile 362 ` 363 364 func existsOnMockFileSystem(desktop_file string) (bool, bool, error) { 365 existsOnMockFileSystem := strutil.ListContains(mockFileSystem, desktop_file) 366 return existsOnMockFileSystem, existsOnMockFileSystem, nil 367 } 368 369 func (s *privilegedDesktopLauncherInternalSuite) mockEnv(key, value string) { 370 old := os.Getenv(key) 371 os.Setenv(key, value) 372 s.AddCleanup(func() { 373 os.Setenv(key, old) 374 }) 375 } 376 377 func (s *privilegedDesktopLauncherInternalSuite) TestDesktopFileSearchPath(c *C) { 378 s.mockEnv("HOME", "/home/user") 379 s.mockEnv("XDG_DATA_HOME", "") 380 s.mockEnv("XDG_DATA_DIRS", "") 381 382 // Default search path 383 c.Check(userd.DesktopFileSearchPath(), DeepEquals, []string{ 384 "/home/user/.local/share/applications", 385 "/usr/local/share/applications", 386 "/usr/share/applications", 387 }) 388 389 // XDG_DATA_HOME will override the first path 390 s.mockEnv("XDG_DATA_HOME", "/home/user/share") 391 c.Check(userd.DesktopFileSearchPath(), DeepEquals, []string{ 392 "/home/user/share/applications", 393 "/usr/local/share/applications", 394 "/usr/share/applications", 395 }) 396 397 // XDG_DATA_DIRS changes the remaining paths 398 s.mockEnv("XDG_DATA_DIRS", "/usr/share:/var/lib/snapd/desktop") 399 c.Check(userd.DesktopFileSearchPath(), DeepEquals, []string{ 400 "/home/user/share/applications", 401 "/usr/share/applications", 402 "/var/lib/snapd/desktop/applications", 403 }) 404 } 405 406 func (s *privilegedDesktopLauncherInternalSuite) TestDesktopFileIDToFilenameSucceedsWithValidId(c *C) { 407 restore := userd.MockRegularFileExists(existsOnMockFileSystem) 408 defer restore() 409 s.mockEnv("XDG_DATA_DIRS", "/usr/local/share:/usr/share:/var/lib/snapd/desktop") 410 411 var desktopIdTests = []struct { 412 id string 413 expect string 414 }{ 415 {"mir-kiosk-scummvm_mir-kiosk-scummvm.desktop", "/var/lib/snapd/desktop/applications/mir-kiosk-scummvm_mir-kiosk-scummvm.desktop"}, 416 {"foo-bar-baz.desktop", "/var/lib/snapd/desktop/applications/foo-bar/baz.desktop"}, 417 {"baz-foo-bar.desktop", "/var/lib/snapd/desktop/applications/baz/foo-bar.desktop"}, 418 {"shadow-test.desktop", "/usr/share/applications/shadow-test.desktop"}, 419 } 420 421 for _, test := range desktopIdTests { 422 actual, err := userd.DesktopFileIDToFilename(test.id) 423 c.Assert(err, IsNil) 424 c.Assert(actual, Equals, test.expect) 425 } 426 } 427 428 func (s *privilegedDesktopLauncherInternalSuite) TestDesktopFileIDToFilenameFailsWithInvalidId(c *C) { 429 restore := userd.MockRegularFileExists(existsOnMockFileSystem) 430 defer restore() 431 s.mockEnv("XDG_DATA_DIRS", "/usr/local/share:/usr/share:/var/lib/snapd/desktop") 432 433 var desktopIdTests = []string{ 434 "mir-kiosk-scummvm-mir-kiosk-scummvm.desktop", 435 "bar-foo-baz.desktop", 436 "bar-baz-foo.desktop", 437 "foo-bar_foo-bar.desktop", 438 // special path segments cannot be smuggled inside desktop IDs 439 "bar-..-vlc_vlc.desktop", 440 "foo-bar/baz.desktop", 441 "bar/../vlc_vlc.desktop", 442 "../applications/vlc_vlc.desktop", 443 // Other invalid desktop IDs 444 "---------foo-bar-baz.desktop", 445 "foo-bar-baz.desktop-foo-bar", 446 "not-a-dot-desktop", 447 "以临时配置文件打开新-non-ascii-here-too.desktop", 448 } 449 450 for _, id := range desktopIdTests { 451 _, err := userd.DesktopFileIDToFilename(id) 452 c.Check(err, ErrorMatches, `cannot find desktop file for ".*"`, Commentf(id)) 453 } 454 } 455 456 func (s *privilegedDesktopLauncherInternalSuite) TestVerifyDesktopFileLocation(c *C) { 457 restore := userd.MockRegularFileExists(existsOnMockFileSystem) 458 defer restore() 459 s.mockEnv("XDG_DATA_DIRS", "/usr/local/share:/usr/share:/var/lib/snapd/desktop") 460 461 // Resolved desktop files belonging to snaps will pass verification: 462 filename, err := userd.DesktopFileIDToFilename("mir-kiosk-scummvm_mir-kiosk-scummvm.desktop") 463 c.Assert(err, IsNil) 464 err = userd.VerifyDesktopFileLocation(filename) 465 c.Check(err, IsNil) 466 467 // Desktop IDs belonging to host system apps fail: 468 filename, err = userd.DesktopFileIDToFilename("shadow-test.desktop") 469 c.Assert(err, IsNil) 470 err = userd.VerifyDesktopFileLocation(filename) 471 c.Check(err, ErrorMatches, "only launching snap applications from /var/lib/snapd/desktop/applications is supported") 472 } 473 474 func (s *privilegedDesktopLauncherInternalSuite) TestParseExecCommandSucceedsWithValidEntry(c *C) { 475 var testCases = []struct { 476 cmd string 477 expect []string 478 }{ 479 // valid with no exec variables 480 {"env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/mir-kiosk-scummvm_mir-kiosk-scummvm.desktop /snap/bin/mir-kiosk-scummvm %U", 481 []string{"env", "BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/mir-kiosk-scummvm_mir-kiosk-scummvm.desktop", "/snap/bin/mir-kiosk-scummvm"}}, 482 // valid with literal '%' and no exec variables 483 {"/snap/bin/foo -f %%bar", []string{"/snap/bin/foo", "-f", "%bar"}}, 484 {"/snap/bin/foo -f %%bar %%baz", []string{"/snap/bin/foo", "-f", "%bar", "%baz"}}, 485 // valid where quoted strings are passed through 486 {"/snap/bin/foo '-f %U'", []string{"/snap/bin/foo", "-f %U"}}, 487 {"/snap/bin/foo '-f %%bar'", []string{"/snap/bin/foo", "-f %%bar"}}, 488 {"/snap/bin/foo '-f %U %%bar'", []string{"/snap/bin/foo", "-f %U %%bar"}}, 489 {"/snap/bin/foo \"'-f bar'\"", []string{"/snap/bin/foo", "'-f bar'"}}, 490 {"/snap/bin/foo '\"-f bar\"'", []string{"/snap/bin/foo", "\"-f bar\""}}, 491 // valid with exec variables stripped out 492 {"/snap/bin/foo -f %U", []string{"/snap/bin/foo", "-f"}}, 493 {"/snap/bin/foo -f %U %i", []string{"/snap/bin/foo", "-f", "--icon", "/snap/chromium/1193/chromium.png"}}, 494 {"/snap/bin/foo -f %U bar", []string{"/snap/bin/foo", "-f", "bar"}}, 495 {"/snap/bin/foo -f %U bar %i", []string{"/snap/bin/foo", "-f", "bar", "--icon", "/snap/chromium/1193/chromium.png"}}, 496 // valid with mixture of literal '%' and exec variables 497 {"/snap/bin/foo -f %U %%bar", []string{"/snap/bin/foo", "-f", "%bar"}}, 498 {"/snap/bin/foo -f %U %i %%bar", []string{"/snap/bin/foo", "-f", "--icon", "/snap/chromium/1193/chromium.png", "%bar"}}, 499 {"/snap/bin/foo -f %U %%bar %i", []string{"/snap/bin/foo", "-f", "%bar", "--icon", "/snap/chromium/1193/chromium.png"}}, 500 {"/snap/bin/foo -f %%bar %U %i", []string{"/snap/bin/foo", "-f", "%bar", "--icon", "/snap/chromium/1193/chromium.png"}}, 501 } 502 503 for _, test := range testCases { 504 actual, err := userd.ParseExecCommand(test.cmd, "/snap/chromium/1193/chromium.png") 505 comment := Commentf("cmd=%s", test.cmd) 506 c.Check(err, IsNil, comment) 507 c.Check(actual, DeepEquals, test.expect, comment) 508 } 509 } 510 511 func (s *privilegedDesktopLauncherInternalSuite) TestParseExecCommandFailsWithInvalidEntry(c *C) { 512 var testCases = []struct { 513 cmd string 514 err string 515 }{ 516 // Commands may be rejected for bad quoting 517 {`/snap/bin/foo "unclosed double quote`, "EOF found when expecting closing quote"}, 518 {`/snap/bin/foo 'unclosed single quote`, "EOF found when expecting closing quote"}, 519 520 // Or use of unexpected unknown variables 521 {"/snap/bin/foo %z", `cannot run "/snap/bin/foo %z" due to use of "%z"`}, 522 {"/snap/bin/foo %", `cannot run "/snap/bin/foo %" due to use of "%"`}, 523 } 524 525 for _, test := range testCases { 526 _, err := userd.ParseExecCommand(test.cmd, "/snap/chromium/1193/chromium.png") 527 comment := Commentf("cmd=%s", test.cmd) 528 c.Check(err, ErrorMatches, test.err, comment) 529 } 530 } 531 532 func (s *privilegedDesktopLauncherInternalSuite) testReadExecCommandFromDesktopFileWithValidContent(c *C, desktopFileContent string) { 533 desktopFile := filepath.Join(c.MkDir(), "test.desktop") 534 535 // We need to correct the embedded path to the desktop file before writing the file 536 fileContent := strings.Replace(desktopFileContent, "/var/lib/snapd/desktop/applications/chromium_chromium.desktop", desktopFile, -1) 537 err := ioutil.WriteFile(desktopFile, []byte(fileContent), 0644) 538 c.Assert(err, IsNil) 539 540 exec, icon, err := userd.ReadExecCommandFromDesktopFile(desktopFile) 541 c.Assert(err, IsNil) 542 543 c.Check(exec, Equals, fmt.Sprintf("env BAMF_DESKTOP_FILE_HINT=%s %s/chromium %%U", desktopFile, dirs.SnapBinariesDir)) 544 c.Check(icon, Equals, "/snap/chromium/1193/chromium.png") 545 } 546 547 func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithValidContentPathSnap(c *C) { 548 // pick a system known to use /snap/bin for launcher symlinks 549 restore := release.MockReleaseInfo(&release.OS{ID: "ubuntu"}) 550 defer restore() 551 dirs.SetRootDir("/") 552 defer dirs.SetRootDir("/") 553 s.testReadExecCommandFromDesktopFileWithValidContent(c, chromiumDesktopFile) 554 } 555 556 func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithValidContentPathVarLibSnapd(c *C) { 557 // pick a system known to use /var/lib/snapd/bin for launcher symlinks 558 restore := release.MockReleaseInfo(&release.OS{ID: "fedora"}) 559 defer restore() 560 dirs.SetRootDir("/") 561 defer dirs.SetRootDir("/") 562 563 // fix the Exec= line 564 fileContentWithVarLibSnapBin := strings.Replace(chromiumDesktopFile, " /snap/bin/chromium", " /var/lib/snapd/snap/bin/chromium", -1) 565 s.testReadExecCommandFromDesktopFileWithValidContent(c, fileContentWithVarLibSnapBin) 566 } 567 568 func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithInvalidExec(c *C) { 569 desktopFile := filepath.Join(c.MkDir(), "test.desktop") 570 571 err := ioutil.WriteFile(desktopFile, []byte(chromiumDesktopFile), 0644) 572 c.Assert(err, IsNil) 573 574 _, _, err = userd.ReadExecCommandFromDesktopFile(desktopFile) 575 c.Assert(err, ErrorMatches, `desktop file ".*" has an unsupported 'Exec' value: .*`) 576 } 577 578 func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithNoDesktopEntry(c *C) { 579 desktopFile := filepath.Join(c.MkDir(), "test.desktop") 580 581 // We need to correct the embedded path to the desktop file before writing the file 582 fileContent := strings.Replace(chromiumDesktopFile, "/var/lib/snapd/desktop/applications/chromium_chromium.desktop", desktopFile, -1) 583 fileContent = strings.Replace(fileContent, "[Desktop Entry]", "[garbage]", -1) 584 585 err := ioutil.WriteFile(desktopFile, []byte(fileContent), 0644) 586 c.Assert(err, IsNil) 587 588 _, _, err = userd.ReadExecCommandFromDesktopFile(desktopFile) 589 c.Assert(err, ErrorMatches, `desktop file ".*" has an unsupported 'Exec' value: ""`) 590 } 591 592 func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCOmmandFromDesktopFileMultipleDesktopEntrySections(c *C) { 593 desktopFile := filepath.Join(c.MkDir(), "test.desktop") 594 c.Assert(ioutil.WriteFile(desktopFile, []byte(`[Desktop Entry] 595 Exec=foo 596 597 [Desktop Entry] 598 Exec=bar 599 `), 0644), IsNil) 600 601 _, _, err := userd.ReadExecCommandFromDesktopFile(desktopFile) 602 c.Check(err, ErrorMatches, `desktop file ".*" has multiple \[Desktop Entry\] sections`) 603 } 604 605 func (s *privilegedDesktopLauncherInternalSuite) TestReadExecCommandFromDesktopFileWithNoFile(c *C) { 606 desktopFile := filepath.Join(c.MkDir(), "test.desktop") 607 608 _, _, err := userd.ReadExecCommandFromDesktopFile(desktopFile) 609 c.Assert(err, ErrorMatches, `open .*: no such file or directory`) 610 }