TestFilenameEvalParser.cpp (18786B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include <string.h> 8 9 #include "gtest/gtest.h" 10 #include "mozilla/ExtensionPolicyService.h" 11 #include "mozilla/dom/ScriptSettings.h" 12 #include "mozilla/dom/SimpleGlobalObject.h" 13 #include "mozilla/extensions/WebExtensionPolicy.h" 14 #include "nsContentSecurityUtils.h" 15 #include "nsStringFwd.h" 16 17 static constexpr auto kChromeURI = "chromeuri"_ns; 18 static constexpr auto kResourceURI = "resourceuri"_ns; 19 static constexpr auto kMozSrcURI = "mozsrcuri"_ns; 20 static constexpr auto kBlobUri = "bloburi"_ns; 21 static constexpr auto kDataUri = "dataurl"_ns; 22 static constexpr auto kAboutUri = "abouturi"_ns; 23 static constexpr auto kSingleString = "singlestring"_ns; 24 static constexpr auto kMozillaExtensionFile = "mozillaextension_file"_ns; 25 static constexpr auto kExtensionURI = "extension_uri"_ns; 26 static constexpr auto kSuspectedUserChromeJS = "suspectedUserChromeJS"_ns; 27 #if defined(XP_WIN) 28 static constexpr auto kSanitizedWindowsURL = "sanitizedWindowsURL"_ns; 29 static constexpr auto kSanitizedWindowsPath = "sanitizedWindowsPath"_ns; 30 #endif 31 static constexpr auto kOther = "other"_ns; 32 33 #define ASSERT_AND_PRINT(first, second, condition) \ 34 fprintf(stderr, "First: %s\n", first.get()); \ 35 fprintf(stderr, "Second: %s\n", NS_ConvertUTF16toUTF8(second).get()); \ 36 ASSERT_TRUE((condition)); 37 // Usage: ASSERT_AND_PRINT(ret.first, ret.second.value(), ... 38 39 #define ASSERT_AND_PRINT_FIRST(first, condition) \ 40 fprintf(stderr, "First: %s\n", (first).get()); \ 41 ASSERT_TRUE((condition)); 42 // Usage: ASSERT_AND_PRINT_FIRST(ret.first, ... 43 44 TEST(FilenameEvalParser, ResourceChrome) 45 { 46 { 47 constexpr auto str = "chrome://firegestures/content/browser.js"_ns; 48 FilenameTypeAndDetails ret = 49 nsContentSecurityUtils::FilenameToFilenameType(str, false); 50 ASSERT_TRUE(ret.first == kChromeURI && ret.second.isSome() && 51 ret.second.value() == str); 52 } 53 { 54 constexpr auto str = "resource://firegestures/content/browser.js"_ns; 55 FilenameTypeAndDetails ret = 56 nsContentSecurityUtils::FilenameToFilenameType(str, false); 57 ASSERT_TRUE(ret.first == kResourceURI && ret.second.isSome() && 58 ret.second.value() == str); 59 } 60 { 61 constexpr auto str = "resource://foo/bar.js#foobar"_ns; 62 FilenameTypeAndDetails ret = 63 nsContentSecurityUtils::FilenameToFilenameType(str, false); 64 ASSERT_EQ(ret.first, kResourceURI); 65 ASSERT_EQ(ret.second.value(), "resource://foo/bar.js"_ns); 66 } 67 { 68 constexpr auto str = "chrome://foo/bar.js?foo"_ns; 69 FilenameTypeAndDetails ret = 70 nsContentSecurityUtils::FilenameToFilenameType(str, false); 71 ASSERT_EQ(ret.first, kChromeURI); 72 ASSERT_EQ(ret.second.value(), "chrome://foo/bar.js"_ns); 73 } 74 { 75 constexpr auto str = "chrome://foo/bar.js?foo#bar"_ns; 76 FilenameTypeAndDetails ret = 77 nsContentSecurityUtils::FilenameToFilenameType(str, false); 78 ASSERT_EQ(ret.first, kChromeURI); 79 ASSERT_EQ(ret.second.value(), "chrome://foo/bar.js"_ns); 80 } 81 { 82 constexpr auto str = 83 "moz-src:///toolkit/components/search/SearchUtils.sys.mjs"_ns; 84 FilenameTypeAndDetails ret = 85 nsContentSecurityUtils::FilenameToFilenameType(str, false); 86 ASSERT_EQ(ret.first, kMozSrcURI); 87 ASSERT_EQ(ret.second.value(), str); 88 } 89 { 90 constexpr auto str = 91 "moz-src:///browser/components/genai/LinkPreview.sys.mjs"_ns; 92 FilenameTypeAndDetails ret = 93 nsContentSecurityUtils::FilenameToFilenameType(str, false); 94 ASSERT_EQ(ret.first, kMozSrcURI); 95 ASSERT_EQ(ret.second.value(), str); 96 } 97 } 98 99 TEST(FilenameEvalParser, BlobData) 100 { 101 { 102 constexpr auto str = "blob://000-000"_ns; 103 FilenameTypeAndDetails ret = 104 nsContentSecurityUtils::FilenameToFilenameType(str, false); 105 ASSERT_TRUE(ret.first == kBlobUri && !ret.second.isSome()); 106 } 107 { 108 constexpr auto str = "blob:000-000"_ns; 109 FilenameTypeAndDetails ret = 110 nsContentSecurityUtils::FilenameToFilenameType(str, false); 111 ASSERT_TRUE(ret.first == kBlobUri && !ret.second.isSome()); 112 } 113 { 114 constexpr auto str = "data://blahblahblah"_ns; 115 FilenameTypeAndDetails ret = 116 nsContentSecurityUtils::FilenameToFilenameType(str, false); 117 ASSERT_TRUE(ret.first == kDataUri && !ret.second.isSome()); 118 } 119 { 120 constexpr auto str = "data:blahblahblah"_ns; 121 FilenameTypeAndDetails ret = 122 nsContentSecurityUtils::FilenameToFilenameType(str, false); 123 ASSERT_TRUE(ret.first == kDataUri && !ret.second.isSome()); 124 } 125 } 126 127 TEST(FilenameEvalParser, MozExtension) 128 { 129 { // Test shield.mozilla.org replacing 130 constexpr auto str = 131 "jar:file:///c:/users/bob/appdata/roaming/mozilla/firefox/profiles/" 132 "foo/" 133 "extensions/federated-learning@shield.mozilla.org.xpi!/experiments/" 134 "study/api.js"_ns; 135 FilenameTypeAndDetails ret = 136 nsContentSecurityUtils::FilenameToFilenameType(str, false); 137 ASSERT_TRUE(ret.first == kMozillaExtensionFile && 138 ret.second.value() == 139 "federated-learning@s!/experiments/study/api.js"_ns); 140 } 141 { // Test mozilla.org replacing 142 constexpr auto str = 143 "jar:file:///c:/users/bob/appdata/roaming/mozilla/firefox/profiles/" 144 "foo/" 145 "extensions/federated-learning@shigeld.mozilla.org.xpi!/experiments/" 146 "study/api.js"_ns; 147 FilenameTypeAndDetails ret = 148 nsContentSecurityUtils::FilenameToFilenameType(str, false); 149 ASSERT_TRUE( 150 ret.first == kMozillaExtensionFile && 151 ret.second.value() == 152 "federated-learning@shigeld.m!/experiments/study/api.js"_ns); 153 } 154 { // Test truncating 155 constexpr auto str = 156 "jar:file:///c:/users/bob/appdata/roaming/mozilla/firefox/profiles/" 157 "foo/" 158 "extensions/federated-learning@shigeld.mozilla.org.xpi!/experiments/" 159 "study/apiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.js"_ns; 160 FilenameTypeAndDetails ret = 161 nsContentSecurityUtils::FilenameToFilenameType(str, false); 162 ASSERT_TRUE(ret.first == kMozillaExtensionFile && 163 ret.second.value() == 164 "federated-learning@shigeld.m!/experiments/" 165 "study/apiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"_ns); 166 } 167 } 168 169 TEST(FilenameEvalParser, UserChromeJS) 170 { 171 { 172 constexpr auto str = "firegestures/content/browser.uc.js"_ns; 173 FilenameTypeAndDetails ret = 174 nsContentSecurityUtils::FilenameToFilenameType(str, false); 175 ASSERT_EQ(ret.first, kSuspectedUserChromeJS); 176 ASSERT_TRUE(ret.second.isNothing()); 177 } 178 { 179 constexpr auto str = "firegestures/content/browser.uc.js?"_ns; 180 FilenameTypeAndDetails ret = 181 nsContentSecurityUtils::FilenameToFilenameType(str, false); 182 ASSERT_EQ(ret.first, kSuspectedUserChromeJS); 183 ASSERT_TRUE(ret.second.isNothing()); 184 } 185 { 186 constexpr auto str = "firegestures/content/browser.uc.js?243244224"_ns; 187 FilenameTypeAndDetails ret = 188 nsContentSecurityUtils::FilenameToFilenameType(str, false); 189 ASSERT_EQ(ret.first, kSuspectedUserChromeJS); 190 ASSERT_TRUE(ret.second.isNothing()); 191 } 192 { 193 constexpr auto str = 194 "file:///b:/fxprofiles/mark/chrome/" 195 "addbookmarkherewithmiddleclick.uc.js?1558444389291"_ns; 196 FilenameTypeAndDetails ret = 197 nsContentSecurityUtils::FilenameToFilenameType(str, false); 198 ASSERT_EQ(ret.first, kSuspectedUserChromeJS); 199 ASSERT_TRUE(ret.second.isNothing()); 200 } 201 202 const nsCString files[] = { 203 "chrome://tabmix-resource/content/bootstrap/Overlays.jsm"_ns, 204 "chrome://tabmixplus/content/utils.js"_ns, 205 "chrome://searchwp/content/searchbox.js"_ns, 206 "chrome://userscripts/content/Geckium_toolbarButtonCreator.uc.js"_ns, 207 "chrome://userchromejs/content/boot.sys.mjs"_ns, 208 "chrome://user_chrome_files/content/user_chrome/toolbars.js"_ns, 209 "chrome://custombuttons/content/depopupnode.js"_ns, 210 "chrome://custombuttons-context/content/button.js"_ns, 211 "chrome://tabgroups-resource/content/modules/utils/Overlays.jsm"_ns, 212 "resource://usl-ucjs/UserScriptLoaderParent.jsm"_ns, 213 "resource://cpmanager-legacy/CPManager.jsm"_ns, 214 "resource://sfm-ucjs/SaveFolderModokiParent.mjs"_ns, 215 "resource://pwa/utils/systemIntegration.sys.mjs"_ns, 216 }; 217 218 for (auto& name : files) { 219 FilenameTypeAndDetails ret = 220 nsContentSecurityUtils::FilenameToFilenameType(name, false); 221 ASSERT_EQ(ret.first, kSuspectedUserChromeJS); 222 ASSERT_EQ(ret.second.value(), name); 223 } 224 } 225 226 TEST(FilenameEvalParser, SingleFile) 227 { 228 { 229 constexpr auto str = "browser.uc.js?2456"_ns; 230 FilenameTypeAndDetails ret = 231 nsContentSecurityUtils::FilenameToFilenameType(str, false); 232 ASSERT_TRUE(ret.first == kSingleString && ret.second.isSome() && 233 ret.second.value() == str); 234 } 235 { 236 constexpr auto str = "debugger"_ns; 237 FilenameTypeAndDetails ret = 238 nsContentSecurityUtils::FilenameToFilenameType(str, false); 239 ASSERT_TRUE(ret.first == kSingleString && ret.second.isSome() && 240 ret.second.value() == str); 241 } 242 } 243 244 TEST(FilenameEvalParser, Other) 245 { 246 { 247 constexpr auto str = "firegestures--content"_ns; 248 FilenameTypeAndDetails ret = 249 nsContentSecurityUtils::FilenameToFilenameType(str, false); 250 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 251 } 252 { 253 constexpr auto str = "gallop://thing/fire"_ns; 254 FilenameTypeAndDetails ret = 255 nsContentSecurityUtils::FilenameToFilenameType(str, false); 256 #if defined(XP_WIN) 257 ASSERT_TRUE(ret.first == kSanitizedWindowsURL && 258 ret.second.value() == "gallop"_ns); 259 #else 260 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 261 #endif 262 } 263 { 264 constexpr auto str = "gallop://fire"_ns; 265 FilenameTypeAndDetails ret = 266 nsContentSecurityUtils::FilenameToFilenameType(str, false); 267 #if defined(XP_WIN) 268 ASSERT_TRUE(ret.first == kSanitizedWindowsURL && 269 ret.second.value() == "gallop"_ns); 270 #else 271 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 272 #endif 273 } 274 { 275 constexpr auto str = "firegestures/content"_ns; 276 FilenameTypeAndDetails ret = 277 nsContentSecurityUtils::FilenameToFilenameType(str, false); 278 #if defined(XP_WIN) 279 ASSERT_TRUE(ret.first == kSanitizedWindowsPath && 280 ret.second.value() == "content"_ns); 281 #else 282 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 283 #endif 284 } 285 { 286 constexpr auto str = "firegestures\\content"_ns; 287 FilenameTypeAndDetails ret = 288 nsContentSecurityUtils::FilenameToFilenameType(str, false); 289 #if defined(XP_WIN) 290 ASSERT_TRUE(ret.first == kSanitizedWindowsPath && 291 ret.second.value() == "content"_ns); 292 #else 293 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 294 #endif 295 } 296 { 297 constexpr auto str = "/home/tom/files/thing"_ns; 298 FilenameTypeAndDetails ret = 299 nsContentSecurityUtils::FilenameToFilenameType(str, false); 300 #if defined(XP_WIN) 301 ASSERT_TRUE(ret.first == kSanitizedWindowsPath && 302 ret.second.value() == "thing"_ns); 303 #else 304 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 305 #endif 306 } 307 { 308 constexpr auto str = "file://c/uers/tom/file.txt"_ns; 309 FilenameTypeAndDetails ret = 310 nsContentSecurityUtils::FilenameToFilenameType(str, false); 311 #if defined(XP_WIN) 312 ASSERT_TRUE(ret.first == kSanitizedWindowsURL && 313 ret.second.value() == "file://.../file.txt"_ns); 314 #else 315 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 316 #endif 317 } 318 { 319 constexpr auto str = "c:/uers/tom/file.txt"_ns; 320 FilenameTypeAndDetails ret = 321 nsContentSecurityUtils::FilenameToFilenameType(str, false); 322 #if defined(XP_WIN) 323 ASSERT_TRUE(ret.first == kSanitizedWindowsPath && 324 ret.second.value() == "file.txt"_ns); 325 #else 326 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 327 #endif 328 } 329 { 330 constexpr auto str = "http://example.com/"_ns; 331 FilenameTypeAndDetails ret = 332 nsContentSecurityUtils::FilenameToFilenameType(str, false); 333 #if defined(XP_WIN) 334 ASSERT_TRUE(ret.first == kSanitizedWindowsURL && 335 ret.second.value() == "http"_ns); 336 #else 337 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 338 #endif 339 } 340 { 341 constexpr auto str = "http://example.com/thing.html"_ns; 342 FilenameTypeAndDetails ret = 343 nsContentSecurityUtils::FilenameToFilenameType(str, false); 344 #if defined(XP_WIN) 345 ASSERT_TRUE(ret.first == kSanitizedWindowsURL && 346 ret.second.value() == "http"_ns); 347 #else 348 ASSERT_TRUE(ret.first == kOther && !ret.second.isSome()); 349 #endif 350 } 351 } 352 353 TEST(FilenameEvalParser, WebExtensionPathParser) 354 { 355 { 356 // Set up an Extension and register it so we can test against it. 357 mozilla::dom::AutoJSAPI jsAPI; 358 ASSERT_TRUE(jsAPI.Init(xpc::PrivilegedJunkScope())); 359 JSContext* cx = jsAPI.cx(); 360 361 mozilla::dom::GlobalObject go(cx, xpc::PrivilegedJunkScope()); 362 auto* wEI = new mozilla::extensions::WebExtensionInit(); 363 364 JS::Rooted<JSObject*> func( 365 cx, (JSObject*)JS_NewFunction(cx, (JSNative)1, 0, 0, "customMethodA")); 366 JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx)); 367 wEI->mLocalizeCallback = new mozilla::dom::WebExtensionLocalizeCallback( 368 cx, func, tempGlobalRoot, nullptr); 369 370 wEI->mAllowedOrigins = 371 mozilla::dom::OwningMatchPatternSetOrStringSequence(); 372 nsString* slotPtr = 373 wEI->mAllowedOrigins.SetAsStringSequence().AppendElement( 374 mozilla::fallible); 375 ASSERT_TRUE(slotPtr != nullptr); 376 nsString& slot = *slotPtr; 377 slot.Truncate(); 378 slot = u"http://example.com"_ns; 379 380 wEI->mName = u"gtest Test Extension"_ns; 381 wEI->mId = u"gtesttestextension@mozilla.org"_ns; 382 wEI->mBaseURL = u"file://foo"_ns; 383 wEI->mMozExtensionHostname = "e37c3c08-beac-a04b-8032-c4f699a1a856"_ns; 384 385 mozilla::ErrorResult eR; 386 RefPtr<mozilla::WebExtensionPolicy> w = 387 mozilla::extensions::WebExtensionPolicy::Constructor(go, *wEI, eR); 388 w->SetActive(true, eR); 389 390 constexpr auto str = 391 "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/path/to/file.js"_ns; 392 FilenameTypeAndDetails ret = 393 nsContentSecurityUtils::FilenameToFilenameType(str, true); 394 395 ASSERT_TRUE(ret.first == kExtensionURI && 396 ret.second.value() == 397 "moz-extension://[gtesttestextension@mozilla.org: " 398 "gtest Test Extension]P=0/path/to/file.js"_ns); 399 400 w->SetActive(false, eR); 401 402 delete wEI; 403 } 404 { 405 // Set up an Extension and register it so we can test against it. 406 mozilla::dom::AutoJSAPI jsAPI; 407 ASSERT_TRUE(jsAPI.Init(xpc::PrivilegedJunkScope())); 408 JSContext* cx = jsAPI.cx(); 409 410 mozilla::dom::GlobalObject go(cx, xpc::PrivilegedJunkScope()); 411 auto wEI = new mozilla::extensions::WebExtensionInit(); 412 413 JS::Rooted<JSObject*> func( 414 cx, (JSObject*)JS_NewFunction(cx, (JSNative)1, 0, 0, "customMethodA")); 415 JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx)); 416 wEI->mLocalizeCallback = new mozilla::dom::WebExtensionLocalizeCallback( 417 cx, func, tempGlobalRoot, NULL); 418 419 wEI->mAllowedOrigins = 420 mozilla::dom::OwningMatchPatternSetOrStringSequence(); 421 nsString* slotPtr = 422 wEI->mAllowedOrigins.SetAsStringSequence().AppendElement( 423 mozilla::fallible); 424 nsString& slot = *slotPtr; 425 slot.Truncate(); 426 slot = u"http://example.com"_ns; 427 428 wEI->mName = u"gtest Test Extension"_ns; 429 wEI->mId = u"gtesttestextension@mozilla.org"_ns; 430 wEI->mBaseURL = u"file://foo"_ns; 431 wEI->mMozExtensionHostname = "e37c3c08-beac-a04b-8032-c4f699a1a856"_ns; 432 wEI->mIsPrivileged = true; 433 434 mozilla::ErrorResult eR; 435 RefPtr<mozilla::WebExtensionPolicy> w = 436 mozilla::extensions::WebExtensionPolicy::Constructor(go, *wEI, eR); 437 w->SetActive(true, eR); 438 439 constexpr auto str = 440 "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/path/to/file.js"_ns; 441 FilenameTypeAndDetails ret = 442 nsContentSecurityUtils::FilenameToFilenameType(str, true); 443 444 ASSERT_TRUE(ret.first == kExtensionURI && 445 ret.second.value() == 446 "moz-extension://[gtesttestextension@mozilla.org: " 447 "gtest Test Extension]P=1/path/to/file.js"_ns); 448 449 w->SetActive(false, eR); 450 451 delete wEI; 452 } 453 { 454 constexpr auto str = 455 "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/path/to/file.js"_ns; 456 FilenameTypeAndDetails ret = 457 nsContentSecurityUtils::FilenameToFilenameType(str, false); 458 ASSERT_TRUE(ret.first == kExtensionURI && !ret.second.isSome()); 459 } 460 { 461 constexpr auto str = 462 "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/file.js"_ns; 463 FilenameTypeAndDetails ret = 464 nsContentSecurityUtils::FilenameToFilenameType(str, true); 465 ASSERT_TRUE( 466 ret.first == kExtensionURI && 467 ret.second.value() == 468 "moz-extension://[failed finding addon by host]/file.js"_ns); 469 } 470 { 471 constexpr auto str = 472 "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856/path/to/" 473 "file.js?querystringx=6"_ns; 474 FilenameTypeAndDetails ret = 475 nsContentSecurityUtils::FilenameToFilenameType(str, true); 476 ASSERT_TRUE(ret.first == kExtensionURI && 477 ret.second.value() == 478 "moz-extension://[failed finding addon " 479 "by host]/path/to/file.js"_ns); 480 } 481 } 482 483 TEST(FilenameEvalParser, AboutPageParser) 484 { 485 { 486 constexpr auto str = "about:about"_ns; 487 FilenameTypeAndDetails ret = 488 nsContentSecurityUtils::FilenameToFilenameType(str, false); 489 ASSERT_TRUE(ret.first == kAboutUri && 490 ret.second.value() == "about:about"_ns); 491 } 492 { 493 constexpr auto str = "about:about?hello"_ns; 494 FilenameTypeAndDetails ret = 495 nsContentSecurityUtils::FilenameToFilenameType(str, false); 496 ASSERT_TRUE(ret.first == kAboutUri && 497 ret.second.value() == "about:about"_ns); 498 } 499 { 500 constexpr auto str = "about:about#mom"_ns; 501 FilenameTypeAndDetails ret = 502 nsContentSecurityUtils::FilenameToFilenameType(str, false); 503 ASSERT_TRUE(ret.first == kAboutUri && 504 ret.second.value() == "about:about"_ns); 505 } 506 { 507 constexpr auto str = "about:about?hello=there#mom"_ns; 508 FilenameTypeAndDetails ret = 509 nsContentSecurityUtils::FilenameToFilenameType(str, false); 510 ASSERT_TRUE(ret.first == kAboutUri && 511 ret.second.value() == "about:about"_ns); 512 } 513 }