Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionPrivilege escalation in the Shell Integration component
ComponentDOM
Bug ClassLogic Error
Tracker2053455
Fix commitb3ccdee1272d (firefox) +34/-12
CISA KEVNot listed
CreditedKhanh Nguyen
Disclosed2026-08-18

Changed Functions

FunctionChangeNotes
for
widget/windows/JumpListBuilder.cpp
modified
if
widget/windows/tests/gtest/TestJumpListBuilder.cpp
modified

Files Changed

  • browser/modules/WindowsJumpLists.sys.mjs
  • dom/chrome-webidl/WindowsJumpListShortcutDescription.webidl
  • widget/windows/JumpListBuilder.cpp
  • widget/windows/tests/gtest/TestJumpListBuilder.cpp
diff --git a/browser/modules/WindowsJumpLists.sys.mjs b/browser/modules/WindowsJumpLists.sys.mjs
index 18d6fb85be4..5ddca6c3776 100644
--- a/browser/modules/WindowsJumpLists.sys.mjs
+++ b/browser/modules/WindowsJumpLists.sys.mjs
@@ -93,7 +93,7 @@ var tasksCfg = [
     get description() {
       return _getString("taskbar.tasks.newTab.description");
     },
-    args: "-new-tab about:blank",
+    args: ["-new-tab", "about:blank"],
     iconIndex: 3, // New window icon
     open: true,
     close: true, // The jump list already has an app launch icon, but
@@ -109,7 +109,7 @@ var tasksCfg = [
     get description() {
       return _getString("taskbar.tasks.newWindow.description");
     },
-    args: "-browser",
+    args: ["-browser"],
     iconIndex: 2, // New tab icon
     open: true,
     close: true, // No point, but we don't always update the list on
@@ -125,7 +125,7 @@ let privateWindowTask = {
   get description() {
     return _getString("taskbar.tasks.newPrivateWindow.description");
   },
-  args: "-private-window",
+  args: ["-private-window"],
   iconIndex: 4, // Private browsing mode icon
   open: true,
   close: true, // No point, but we don't always update the list on
@@ -267,7 +267,7 @@ var Builder = class {
             title: row.getResultByName("title"),
             description: row.getResultByName("title"),
             path: selfPath,
-            arguments: row.getResultByName("url"),
+            arguments: ["-osint", "-url", row.getResultByName("url")],
             fallbackIconIndex: 1,
             iconPath,
           });
diff --git a/dom/chrome-webidl/WindowsJumpListShortcutDescription.webidl b/dom/chrome-webidl/WindowsJumpListShortcutDescription.webidl
index d32eb6c61be..73767c84ac6 100644
--- a/dom/chrome-webidl/WindowsJumpListShortcutDescription.webidl
+++ b/dom/chrome-webidl/WindowsJumpListShortcutDescription.webidl
@@ -27,7 +27,7 @@ dictionary WindowsJumpListShortcutDescription {
    * Arguments to be supplied to the executable when the item is selected in
    * the Jump List.
    */
-  DOMString arguments;
+  sequence<DOMString> arguments;
 
   /**
    * A description of the item that is displayed as a tooltip.
diff --git a/widget/windows/JumpListBuilder.cpp b/widget/windows/JumpListBuilder.cpp
index 02e5f7a0d3b..121e69236c5 100644
--- a/widget/windows/JumpListBuilder.cpp
+++ b/widget/windows/JumpListBuilder.cpp
@@ -13,6 +13,7 @@
 // clang-format on
 
 #include "WinUtils.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WindowsJumpListShortcutDescriptionBinding.h"
@@ -876,10 +877,20 @@ nsresult JumpListBuilder::GetShellLinkFromDescription(
 
   hr = psl->SetDescription(descriptionCopy.get());
 
-  if (aDesc.mArguments.WasPassed() && !aDesc.mArguments.Value().IsEmpty()) {
-    hr = psl->SetArguments(aDesc.mArguments.Value().get());
-  } else {
-    hr = psl->SetArguments(L"");
+  if (aDesc.mArguments.WasPassed()) {
+    const mozilla::dom::Sequence<nsString>& arguments =
+        aDesc.mArguments.Value();
+
+    // MakeCommandLine expects wchar_t**, but we have Sequence<nsString>, so
+    // convert it over.
+    AutoTArray<const wchar_t*, 8> flatArgs;
+    for (const nsString& arg : arguments) {
+      flatArgs.AppendElement(arg.get());
+    }
+
+    UniquePtr<wchar_t[]> commandLine = mozilla::MakeCommandLine(
+        AssertedCast<int>(flatArgs.Length()), flatArgs.Elements());
+    hr = psl->SetArguments(commandLine.get());
   }
 
   // Set up the fallback icon in the event that a valid icon URI has
diff --git a/widget/windows/tests/gtest/TestJumpListBuilder.cpp b/widget/windows/tests/gtest/TestJumpListBuilder.cpp
index a46eb4e5051..d6dd8208fce 100644
--- a/widget/windows/tests/gtest/TestJumpListBuilder.cpp
+++ b/widget/windows/tests/gtest/TestJumpListBuilder.cpp
@@ -17,6 +17,7 @@ PSSTDAPI PropVariantToString(REFPROPVARIANT propvar, PWSTR psz, UINT cch);
 #include "JumpListBuilder.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
 #include "mozilla/SpinEventLoopUntil.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Promise.h"
@@ -113,7 +114,17 @@ MATCHER_P(ShellLinksEq, descs,
     }
 
     if (desc.mArguments.WasPassed()) {
-      if (!desc.mArguments.Value().Equals(argsBuf)) {
+      mozilla::CommandLineParserWin<char16_t> parser;
+      parser.HandleCommandLine(nsDependentString(argsBuf));
+
+      const mozilla::dom::Sequence<nsString>& args = desc.mArguments.Value();
+
+      bool same = std::equal(
+          parser.Argv(), parser.Argv() + parser.Argc(), args.cbegin(),
+          args.cend(), [](const char16_t* aParsed, const nsAString& aExpected) {
+            return aExpected == nsDependentString(aParsed);
+          });
+      if (!same) {
         return false;
       }
     } else {
@@ -310,8 +321,8 @@ void GenerateWindowsJumpListShortcutDescriptions(
     desc.mFallbackIconIndex = 0;
 
     if (!(i % 2)) {
-      nsAutoString arguments(u"-arg1 -arg2 -arg3");
-      desc.mArguments.Construct(arguments);
+      desc.mArguments.Construct(nsTArray<nsString>{
+          u"-arg1"_ns, u"argument with a space"_ns, u"-arg3"_ns});
       nsAutoString iconPath(u"C:\\Some\\icon.png");
       desc.mIconPath.Construct(iconPath);
     }
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/widget/windows/tests/gtest/TestJumpListBuilder.cpp b/widget/windows/tests/gtest/TestJumpListBuilder.cpp
index a46eb4e5051..d6dd8208fce 100644
--- a/widget/windows/tests/gtest/TestJumpListBuilder.cpp
+++ b/widget/windows/tests/gtest/TestJumpListBuilder.cpp
@@ -17,6 +17,7 @@ PSSTDAPI PropVariantToString(REFPROPVARIANT propvar, PWSTR psz, UINT cch);
 #include "JumpListBuilder.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
 #include "mozilla/SpinEventLoopUntil.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Promise.h"
@@ -113,7 +114,17 @@ MATCHER_P(ShellLinksEq, descs,
     }
 
     if (desc.mArguments.WasPassed()) {
-      if (!desc.mArguments.Value().Equals(argsBuf)) {
+      mozilla::CommandLineParserWin<char16_t> parser;
+      parser.HandleCommandLine(nsDependentString(argsBuf));
+
+      const mozilla::dom::Sequence<nsString>& args = desc.mArguments.Value();
+
+      bool same = std::equal(
+          parser.Argv(), parser.Argv() + parser.Argc(), args.cbegin(),
+          args.cend(), [](const char16_t* aParsed, const nsAString& aExpected) {
+            return aExpected == nsDependentString(aParsed);
+          });
+      if (!same) {
         return false;
       }
     } else {
@@ -310,8 +321,8 @@ void GenerateWindowsJumpListShortcutDescriptions(
     desc.mFallbackIconIndex = 0;
 
     if (!(i % 2)) {
-      nsAutoString arguments(u"-arg1 -arg2 -arg3");
-      desc.mArguments.Construct(arguments);
+      desc.mArguments.Construct(nsTArray<nsString>{
+          u"-arg1"_ns, u"argument with a space"_ns, u"-arg3"_ns});
       nsAutoString iconPath(u"C:\\Some\\icon.png");
       desc.mIconPath.Construct(iconPath);
     }
Loading diff…