Repository navigation
Consider some more narrow _origin_ matching mechanism? #275
Description
What problem are you trying to solve?
Hey folks! I wonder whether you've considered a narrower use case of the URLPattern object which would make it more easily possible for folks to match against a pattern representing an origin as opposed to a URL?
I'm thinking about this in the context of handling MessageEvent objects, where we'd like developers to take a close look at the origin attribute before handling the message. URLPattern can certainly support this use case, as in:
function isReasonableOrigin(origin) {
const validOrigin = new URLPattern("https://*.trusted.site");
try {
const url = new URL(origin);
return validOrigin.test(url);
} catch(e) {
return false; // Or `true`, I suppose, if your application needs to
// work with opaque origins.
}
}
window.addEventListener('message', e => {
if (isReasonableOrigin(e.origin)) {
// Exciting activities go here...
}
});This works, as long as developers remember to only populate protocol, hostname, and port in a URLPatternInit dictionary, or to ensure that they don't include a trailing slash in the URL pattern string (new URLPattern("https://*.trusted.site/") is fairly restrictive, for instance).
What solutions exist today?
Developers can hold the URLPattern object correctly.
How would you solve it?
Two possibilities come to mind:
-
Create an origin-matching variant of
test()(andexec(), I suppose) which would only takeprotocol,hostname, andportinto account, returning a match iff those three aspects of the pattern matched while ignoring extraneous attributes. -
Create an
OriginPatternobject (andOriginPatternInit) which only accepts the relevant attributes.
The latter seems clearer to me, though I'm not sure how it would be related to the URLPattern object (subclass? parent class? completely separate thing? It would be nice if it could be used wherever URLPattern was used, so subclassing makes the most sense, but that means it would have a bunch of properties that are somewhat meaningless. Meh.)
Anything else?
No response
Activity
domenic commentedon Jul 23, 2025
What would be the difference between (2) and holding URLPattern correctly?
mikewest commentedon Jul 23, 2025
Two differences that seem like they make it easier to hold correctly for this use case:
Names are helpful, and representing an origin comparison with a
URLwhateverobject isn't as clear as it could be (this is also why it would be nice to have anOriginobject (which I feel like I've talked about with folks before, but can't find an issue... hrm)).We'd be able to either provide developers with useful error messages in cases where the pattern they provided didn't map to an origin, or ignore things they provided that were unnecessary for an origin. That is,
new OriginPattern("https://username@*.example.site/path#anchor")(or the equivalent dictionary) could either throw a helpful error, or it could result in the same outcome asnew OriginPattern("https://*.example.site").annevk commentedon Jul 23, 2025
I don't really like the idea of parsing a serialized origin.
Note that
new URL()will also "add" a slash for special schemes.If
postMessage()is still a big problem perhaps we should take another look at its design to see if we can more fundamentally improve it?mikewest commentedon Jul 23, 2025
This is what
MessageEvent.origin(andWindow.originfor that matter) require today. It seems strictly easier to parse origins than to parse URLs; is your concern ergonomic or something else?Huh. TIL.
I'm noodling on this in the context of https://github.com/mikewest/incentivize-origin-checks, which I haven't thought about enough (or even landed telemetry yet) to actually send out for comment, but since we're here... :)
annevk commentedon Jul 23, 2025
I'm not sure they "require" that today. CORS doesn't require it.
We don't have a parser defined for serialized origins. They happen to mostly parse as URLs (if you ignore null), but if you were to parse them using the URL parser as you did you would end up accepting vastly more inputs than we would serialize. That doesn't seem sound for a security primitive.
mikewest commentedon Jul 23, 2025
"require" is strong, I suppose. But when people do string-based matching today, they're often doing so in ways that folks can bypass (
indexOf("totally-trusted-origin.com") !== -1to match subdomains vshttps://totally-trusted-origin.com.evil.com). I think it would be more than nice to have something to point towards that more clearly represents the comparison they're trying to do. Some subset ofURLPatternmight be such a thing, but I'm quite open to other alternatives to string comparisons.Let's say we defined that. It's somewhat outside the scope of the
URLPatternrequest, but if you'd be open to an origin parser and/or API, I'd spend some time pulling something together.In this particular case, I think we're safe given that the
e.originattribute is serialized by the browser, but your point is well-taken.annevk commentedon Jul 24, 2025
Do we know of use cases outside
postMessage()? Because if not, I'd rather define a better matching/filtering mechanism specifically for that.mikewest commentedon Jul 24, 2025
Practically,
MessageEvent(throughpostMessage()mostly, andEventSourcemuch less commonly) andancestorOriginsare the places where I remember folks getting these checks wrong in ways that caused issues.Philosophically, I think it's unfortunate we're using strings to represent the fundamental security primitive we're building upon. Developers do origin comparisons for arbitrary reasons on arbitrary data that the browser might or might not be responsible for generating: https://www.npmjs.com/package/original is the first NPM package I found in a quick search (and relies on
url-parsefor parsing, which looks like it has more regex than can possibly be good for its health). I think it'd be nice for the platform to entirely obviate that package.annevk commentedon Jul 24, 2025
That package does nothing of the sort we are discussing here: https://github.com/unshiftio/original/blob/master/index.js. All of that is already available with
URL(it appears to be a poorly executed wrapper).If there are actual packages for this that would be good to know.
Some possible solutions to this overall problem:
Originclass and anOriginPatternclass. This is robust, but requires people to know about them. Also introduces quite a bit of overhead.The other thing that makes me a bit hesitant about all this is that we are slowly moving away from origin alone as the authority. Top-level site, has cross-site ancestors, etc., are all slowly becoming more important. It seems those should also count as being important for the purposes of
postMessage(). Less clear forancestorOrigins, but I still recall that bz objected toancestorOriginsfor exposing more information than it should. I suspect that's still a thing we want to address.sisidovski commentedon Jul 24, 2025
Cross-origin URL is often checked in the service worker fetch handler e.g. workbox to handle the response.
That sounds ideal from the long term ecosystem perspective.
I thought it may be convenient if we have something like
IsSameOrigin()orIsSameSite()in theURLPatternobject. And we only handle protocol, hostname, and port components internally, but not sure.annevk commentedon Jul 24, 2025
Your workbox example does an equality comparison between two serialized origins. That's already solid and does not justify new API.
mikewest commentedon Jul 24, 2025
It was meant as a justification for my claim that "developers do origin comparisons", and that they're looking for tools to make that possible. I very much agree that we would not base any addition to the platform on its implementation. :)
I agree with a lot of this. In particular, I'd agree that people are trying to do same-site comparisons today, and that they've become increasingly important. Without platform-level support, folks rely on things like
psl, an 8-month out of date library to extract registrable domains, which even in the best case won't be aligned with the browser's understanding of the "site" distinction).My feeling is that the core of the net-new work would be defining a parser for serialized origins, while much of the remaining overhead is already implicit in the existing
same-origin/same-sitedefinitions in HTML and matching logic in URLPattern.I think this could be a practical and robust solution for the origin comparisons the platform encourages directly, and it would be an improvement upon the status quo to provide. If we're going to provide pattern matching, though, I think we end up in a place similar to where we'd end up with
OriginPattern? That's where I ended up with https://github.com/mikewest/incentivize-origin-checks?tab=readme-ov-file#filtered-registration, in any event.I don't feel like it's sufficient support for developers' broader needs; I'd be happier if we did the work to provide some additional primitives upon which folks can build.
All that said, this has moved somewhat beyond the
URLPatternrepo's remit. How about this: I'll take some time to sketch out a proposal along the lines of your option 1, as that's the outcome that appeals to me initially. I'll throw it in a repo somewhere, and we can talk about whether it's worth bringing to WICG for more discussion?annevk commentedon Jul 24, 2025
To restate my final thought from the prior comment, it's not entirely clear to me that same-origin/same-site solves the problem. If I get a message from origin A, should I not also care about whether that A was partitioned or had cross-site ancestors? That information is currently not exposed on
MessageEventand I'm not sure we'd want to expose each of those as their own field. That will make writing a proper check very complex.Generally exploring this elsewhere seems reasonable, but I think that's a key question we need to address.
(I'm also in favor of exposing the PSL as part of the URL API. It seems like a reasonable start for
URLHost. (And moving the PSL into the WHATWG so it has more reasonable governance.))mikewest commentedon Jul 24, 2025
I agree that developers could benefit from taking even more context into account. They can (unfortunately?) get some of that context today by reaching through
MessageEvent.source.parent, but you're right that would be ideal for us to make decisions about what developers should take into account and make explicit provisions for those bits of data.That said, I think there's fairly concrete evidence that developers want to make origin-/site-bound decisions, and that they have some level of difficulty using the serialized origin that we provide today. I think whatever we'd provide for in the future would make comparisons against some allow-/blocklist possible, and I think that capability would be valuable even outside the
postMessage()context.For the PSL, I think that's whatwg/url#528. IMO, it makes more sense for us to talk about sites than registrable domains (and therefore more sense to tie the comparison to origins than to URLs), but that opinion is not terribly strongly-held.
annevk commentedon Jul 24, 2025
I think what I'm getting at is that the API shape could be such that it forces developers to think about all of those aspects at once. But that's not going to happen if we largely leave the comparison operation fully to web developers and only give them some additional tools to do the comparisons with.
Your point on deriving things from origins rather than domains is interesting. I think that makes sense for security, but not necessarily for rendering. (Although that will also require a script-accessible ToUnicode to be solved well I suppose. Hmm.)
mikewest commentedon Jul 25, 2025
I spent some time on https://github.com/mikewest/origin-api/ yesterday to sketch out a proposal in a little more detail. I got some very helpful feedback from @domenic this morning (Thanks!), and I think it's just sketchy enough to get the point across. I'd appreciate folks' thoughts.
I think I agree that it would be valuable, particularly for
postMessage(), to think through changes to the API that would guide developers towards a set of considerations beyond pure origin matching.At the same time, same-origin and same-site checks seem fairly fundamental, even in cases where we're not talking about distinct document contexts. Given the challenge of polyfilling those correctly, I'd suggest that adding comparison tools to the platform seems necessary, even if a more robust API might be helpful in specific contexts.
In my mind, "same site" checks seem tightly bound to origins, while "schemelessly same site" checks could easily be bound to domains. It might make sense to offer both.
You're right, though: rendering origins is hard. I wonder whether it might make sense to encapsulate some subset of the guidance at https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/url_display_guidelines/url_display_guidelines.md into an API that could give you a marked-up URL (or indexes into a serialized string?) that would allow for consistent styling/elision in contexts where developers wanted to display such a thing.