Skip to content

Consider some more narrow _origin_ matching mechanism? #275

Open

Description

@mikewest

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:

  1. Create an origin-matching variant of test() (and exec(), I suppose) which would only take protocol, hostname, and port into account, returning a match iff those three aspects of the pattern matched while ignoring extraneous attributes.

  2. Create an OriginPattern object (and OriginPatternInit) 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

  1. domenic commented on Jul 23, 2025

    @domenic
    Member

    What would be the difference between (2) and holding URLPattern correctly?

  2. mikewest commented on Jul 23, 2025

    @mikewest
    MemberAuthor

    Two differences that seem like they make it easier to hold correctly for this use case:

    1. Names are helpful, and representing an origin comparison with a URLwhatever object isn't as clear as it could be (this is also why it would be nice to have an Origin object (which I feel like I've talked about with folks before, but can't find an issue... hrm)).

    2. 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 as new OriginPattern("https://*.example.site").

  3. annevk commented on Jul 23, 2025

    @annevk
    Member

    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?

  4. mikewest commented on Jul 23, 2025

    @mikewest
    MemberAuthor

    I don't really like the idea of parsing a serialized origin.

    This is what MessageEvent.origin (and Window.origin for that matter) require today. It seems strictly easier to parse origins than to parse URLs; is your concern ergonomic or something else?

    Note that new URL() will also "add" a slash for special schemes.

    Huh. TIL.

    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?

    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... :)

  5. annevk commented on Jul 23, 2025

    @annevk
    Member

    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.

  6. mikewest commented on Jul 23, 2025

    @mikewest
    MemberAuthor

    I'm not sure they "require" that today. CORS doesn't require it.

    "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") !== -1 to match subdomains vs https://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 of URLPattern might be such a thing, but I'm quite open to other alternatives to string comparisons.

    We don't have a parser defined for serialized origins.

    Let's say we defined that. It's somewhat outside the scope of the URLPattern request, but if you'd be open to an origin parser and/or API, I'd spend some time pulling something together.

    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.

    In this particular case, I think we're safe given that the e.origin attribute is serialized by the browser, but your point is well-taken.

  7. annevk commented on Jul 24, 2025

    @annevk
    Member

    Do we know of use cases outside postMessage()? Because if not, I'd rather define a better matching/filtering mechanism specifically for that.

  8. mikewest commented on Jul 24, 2025

    @mikewest
    MemberAuthor

    Practically, MessageEvent (through postMessage() mostly, and EventSource much less commonly) and ancestorOrigins are 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-parse for 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.

  9. annevk commented on Jul 24, 2025

    @annevk
    Member

    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:

    1. Provide an Origin class and an OriginPattern class. This is robust, but requires people to know about them. Also introduces quite a bit of overhead.
    2. In places where we expose a serialized origin and expect web developers to use it in comparisons, also provide a match method that takes a pattern of sorts and tells you whether it matches the internal origin concept. This will be easier to find for people as the method is defined in the same place that the serialized origin getter is, but does require some amount of duplication. We don't have to define an origin parser as all the places that expose a serialized origin can also hold the actual origins.

    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 for ancestorOrigins, but I still recall that bz objected to ancestorOrigins for exposing more information than it should. I suspect that's still a thing we want to address.

  10. sisidovski commented on Jul 24, 2025

    @sisidovski
    Collaborator

    Cross-origin URL is often checked in the service worker fetch handler e.g. workbox to handle the response.

    Provide an Origin class and an OriginPattern class. This is robust, but requires people to know about them. Also introduces quite a bit of overhead.

    That sounds ideal from the long term ecosystem perspective.

    I thought it may be convenient if we have something like IsSameOrigin() or IsSameSite() in the URLPattern object. And we only handle protocol, hostname, and port components internally, but not sure.

  11. annevk commented on Jul 24, 2025

    @annevk
    Member

    Your workbox example does an equality comparison between two serialized origins. That's already solid and does not justify new API.

  12. mikewest commented on Jul 24, 2025

    @mikewest
    MemberAuthor

    That package does nothing of the sort we are discussing here

    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. :)

    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. op-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().

    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).

    Option 1: Provide an Origin class and an OriginPattern class. This is robust, but requires people to know about them. Also introduces quite a bit of overhead.

    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-site definitions in HTML and matching logic in URLPattern.

    Option 2: In places where we expose a serialized origin and expect web developers to use it in comparisons, also provide a match method that takes a pattern of sorts ...

    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 URLPattern repo'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?

  13. annevk commented on Jul 24, 2025

    @annevk
    Member

    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 MessageEvent and 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.))

  14. mikewest commented on Jul 24, 2025

    @mikewest
    MemberAuthor

    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.

  15. annevk commented on Jul 24, 2025

    @annevk
    Member

    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.)

  16. mikewest commented on Jul 25, 2025

    @mikewest
    MemberAuthor

    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 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.

    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.

    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.)

    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.

  17. deleted a comment from cocobutterninja on Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    addition/proposalNew features or enhancementsneeds implementer interestMoving the issue forward requires implementers to express interest

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions