Firefox · Core
CVE-2026-16374
Logic Error in Core
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifdevtools/server/actors/source.js |
modified |
Files Changed
devtools/server/actors/source.js
Patch
diff --git a/devtools/server/actors/source.js b/devtools/server/actors/source.js
index 2566111d5f0..60f9541f7f6 100644
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -45,10 +45,23 @@ ChromeUtils.defineESModuleGetters(
const windowsDrive = /^([a-zA-Z]:)/;
+/**
+ * Resolve the potentially relative sourceURL to an absolute URL, based on the
+ * base URL of the current target actor owning this source.
+ *
+ * @param {string} sourceURL
+ * The sourceURL to resolve to an absolute URL. If the URL is already an
+ * absolute URL, the same value will be returned.
+ * @param {TargetActor} targetActor
+ * The target actor to use to extract the base URL.
+ *
+ * @return {string|null} The resolved source URL or null.
+ */
function resolveSourceURL(sourceURL, targetActor) {
if (sourceURL) {
let baseURL;
if (targetActor.window) {
+ // Bug 2048537: Should use baseURI here.
baseURL = targetActor.window.location?.href;
}
// For worker, we don't have easy access to location,
@@ -56,6 +69,10 @@ function resolveSourceURL(sourceURL, targetActor) {
if (targetActor.workerUrl) {
baseURL = targetActor.workerUrl;
}
+
+ // TODO: for absolute URLs, baseURL is ignored by URL.parse.
+ // We could check URL.canParse(sourceURL) early and skip the baseURL
+ // computation in most cases.
const parsedURL = URL.parse(sourceURL, baseURL);
if (parsedURL) {
return parsedURL.href;
@@ -64,48 +81,75 @@ function resolveSourceURL(sourceURL, targetActor) {
return null;
}
+
+/**
+ * Return the source's full URL as a string, according to `//# sourceURL` pragma
+ * if present in the source.
+ *
+ * @param {Debugger.Source} source
+ * The source for which we want to get the full/display URL.
+ * @param {TargetActor} targetActor
+ * The target actor owning the thread actor responsible for this source.
+ *
+ * @returns {string|null} The full / display URL or null if it could not be
+ * computed
+ */
function getSourceURL(source, targetActor) {
+ let result = resolveSourceURL(source.displayURL, targetActor);
+ if (!result) {
+ result = getRealSourceURL(source, targetActor);
+ }
+
+ // Avoid returning empty string and return null if no URL is found
+ return result || null;
+}
+
+/**
+ * Return the source's full URL as a string, ignoring any `//# sourceURL` pragma
+ * present in the source.
+ *
+ * @param {Debugger.Source} source
+ * The source for which we want to get the real URL.
+ * @param {TargetActor} targetActor
+ * The target actor owning the thread actor responsible for this source.
+ *
+ * @returns {string|null} The real URL or null if it could not be computed.
+ */
+function getRealSourceURL(source, targetActor) {
// Some eval sources have URLs, but we want to explicitly ignore those because
// they are generally useless strings like "eval" or "debugger eval code".
- let resourceURL = getDebuggerSourceURL(source) || "";
+ let url = getDebuggerSourceURL(source) || "";
// Strip out eventual stack trace stored in Source's url.
// (not clear if that still happens)
- resourceURL = resourceURL.split(" -> ").pop();
+ url = url.split(" -> ").pop();
// Debugger.Source.url attribute may be of the form:
// "http://example.com/foo line 10 > inlineScript"
// because of the following function `js::FormatIntroducedFilename`:
// https://searchfox.org/mozilla-central/rev/253ae246f642fe9619597f44de3b087f94e45a2d/js/src/vm/JSScript.cpp#1816-1846
// This isn't so easy to reproduce, but browser_dbg-breakpoints-popup.js's testPausedInTwoPopups covers this
- resourceURL = resourceURL.replace(/ line \d+ > .*$/, "");
-
- // A "//# sourceURL=" pragma should basically be treated as a source file's
- // full URL, so that is what we want to use as the base if it is present.
- // If this is not an absolute URL, this will mean the maps in the file
- // will not have a valid base URL, but that is up to tooling that
- let result = resolveSourceURL(source.displayURL, targetActor);
- if (!result) {
- result = resolveSourceURL(resourceURL, targetActor) || resourceURL;
-
- // In XPCShell tests, the source URL isn't actually a URL, it's a file path.
- // That causes issues because "C:/folder/file.js" is parsed as a URL with
- // "c:" as the URL scheme, which causes the drive letter to be unexpectedly
- // lower-cased when the parsed URL is re-serialized. To avoid that, we
- // detect that case and re-uppercase it again. This is a bit gross and
- // ideally it seems like XPCShell tests should use file:// URLs for files,
- // but alas they do not.
- if (
- resourceURL &&
- resourceURL.match(windowsDrive) &&
- result.slice(0, 2) == resourceURL.slice(0, 2).toLowerCase()
- ) {
- result = resourceURL.slice(0, 2) + result.slice(2);
- }
+ url = url.replace(/ line \d+ > .*$/, "");
+
+ let sourceURL = resolveSourceURL(url, targetActor) || url;
+
+ // In XPCShell tests, the source URL isn't actually a URL, it's a file path.
+ // That causes issues because "C:/folder/file.js" is parsed as a URL with
+ // "c:" as the URL scheme, which causes the drive letter to be unexpectedly
+ // lower-cased when the parsed URL is re-serialized. To avoid that, we
+ // detect that case and re-uppercase it again. This is a bit gross and
+ // ideally it seems like XPCShell tests should use file:// URLs for files,
+ // but alas they do not.
+ if (
+ url &&
+ sourceURL &&
+ url.match(windowsDrive) &&
+ sourceURL.slice(0, 2) == url.slice(0, 2).toLowerCase()
+ ) {
+ sourceURL = url.slice(0, 2) + sourceURL.slice(2);
}
- // Avoid returning empty string and return null if no URL is found
- return result || null;
+ return sourceURL || null;
}
/**
@@ -211,8 +255,12 @@ class SourceActor extends Actor {
extensionName: this.extensionName,
url: this.url,
isBlackBoxed: this.sourcesManager.isBlackBoxed(this.url),
+ // Bug 1970319: only use the real URL for sourcemap resolution until
+ // requests can be handled in content.
+ // According to the spec (https://tc39.es/ecma426/#sec-linking-generated-code)
+ // sourceMapBaseURL should use the displayURL/sourceURL for inline sources.
sourceMapBaseURL: getSourcemapBaseURL(
- this.url,
+ getRealSourceURL(this._source, this.threadActor.targetActor),
this.threadActor.targetActor.window
),
sourceMapURL: source.sourceMapURL,
Loading diff…
References
On This Page