03:34
<guybedford>
seems there was a pretty critical oversight in the type css import spec - https://github.com/whatwg/html/issues/11629
03:34
<guybedford>
hopefully it gets corrected soon!
03:35
<guybedford>
see you all tomorrow
16:02
<nicolo-ribaudo>
Meeting time!
21:28
<kriskowal>
nicolo-ribaudo guybedford what’s the current deal for preserving the referrer of a module source if it’s transferred over post message to another worker, such that relative module specifiers converge on the same sources for shallow dependencies?
21:30
<kriskowal>
I assume it’s currently an implementation- or host-defined behavior backed by the host data internal slot, made transferrable of postmessage and structuredclone.
21:34
<kriskowal>
I’m mulling a mechanism to bind a module source to its specifier explicitly like import(new ModuleSource('import "../x.js"'), { base: new URL('lib/y.js', import.meta.url).href }) or import.source with same args to avoid execution (and incidentally have a different module memo key)
21:37
<kriskowal>
@bakkot hinted that using dynamic import to bind sources was an option, given that we (including me) disfavor using the ModuleSource constructor. He was referring specifically to importHook, which doesn’t necessarily apply to the base module specifier but could. If the base specifier is currently an implementation specific detail that of transferrable module sources, given that it’s necessarily a serializable string, we could adopt it in 262 and bind it either way.
22:12
<guybedford>
It's just the baseURL property of the HTML module script here, which is separately serialized and deserialized when transferring a module script. Because the module script is [[HostDefined]] on the module record, both the record and its "HTML wrapper" of the module script get serialized together
22:15
<kriskowal>
Yeah, that’s what I recall. Thanks.
22:15
<guybedford>
I think indirection would be needed either through an instance or through another field to support configurable ids. Alternatively this could be done specifically at registry injection time i.e. return { id?, source } from the load hook
22:16
<kriskowal>
The importHook case is actually fine, because the hook is invoked knowing the cache key, which is the base specifier already.
22:17
<kriskowal>
The case of a ModuleSource(text) lifted from text is not really germane yet since we don’t have a lift-from-text, but I’m thinking ahead to how we bind that to the supplementary base/referrer.
22:18
<kriskowal>
It can’t be the [[HostDefined]] slot because we can’t stomp over origin metadata for purposes of CSP.
22:19
<kriskowal>
So I think we just end up with a slot in 262 that tends to coïncide with information currently only in [[HostDefined]].
22:19
<guybedford>
in the ModuleSource(text) case we might have more flexibility to override the baseURL actually since theres another field on the module script I added to track whether it is 'evalish' or not
22:19
<guybedford>
so the baseURL is no longer a security check, but just for resolution at that point
22:20
<guybedford>
it's more in the non-eval case that it's a csp concern to override, although I guess we could always just have two baseURLs as well - cspBaseURL and resolverBaseURL
22:20
<kriskowal>
Alright, we agree up to this point and I think it’s just a flavor choice whether to bind the base with the ModuleSource constructor or import.source. I’m favoring ModuleSource(text, { base })
22:21
<kriskowal>
I think we agreed to have these separate a long while back.
22:21
<guybedford>
I wonder if we allow new ModuleSource(existingModuleSource, { base }) for "reassigning" the base
22:21
<guybedford>
since new ModuleSource() is effectively the eval mode, that would bypass CSP concerns
22:21
<kriskowal>
Yeah, that’s an option in my opinion.
22:22
<guybedford>
I don't hate it either
22:22
<kriskowal>
There are cases where relocating the base for a host-imported source is useful. Moddable uses them.
22:23
<kriskowal>
For example, they often put a bunch of sources in ROM that are never executed in the initial realm but exist solely for mapping into a Compartment in different locations.
22:25
<guybedford>
I guess my remaining question is how to square this with registry hooks - if the result of the hook can override the URL
22:25
<guybedford>
maybe that's fine - the initial resolve picks a URL, the hook returns a module for that URL, the returned module overrides the URL
22:26
<kriskowal>
That’s actually table stakes.
22:26
<kriskowal>
There are lots of cases of aliasing, particularly through package.json exports/imports, where you import by one specifier and the module is executed from elsewhere.
22:27
<kriskowal>
But some of those cases are trickier than others and there are a variety of ways to handle it.
22:27
<guybedford>
the problem comes where the function is async - you would need the load hook to be sync with an async load - load(url) -> { url, promise<ModuleSource> }
22:27
<guybedford>
async resolver joyee pointed out earlier is a real concern
22:28
<kriskowal>
Yeah, the Compartment shim handles this case a bit more gracefully than that.
22:29
<guybedford>
fwiw in the module source proposal, I never precluded { id, moduleSource } from allowing identity distinct from moduleSource, and did call this out in a slide
22:29
<kriskowal>
The importHook can return a ModuleSource, and we’ve agreed that the ModuleSource carries a base. The Compartment is calling importHook(specifier, options) after creating an entry in the module map for the module record that the hook will fill, so cycles are handled.
22:30
<guybedford>
could be a poor version of instance identity to allow { id?, moduleSource } where id defaults to moduleSource.baseURL when otherwise unprovided
22:30
<kriskowal>
The importHook can also return a namespace or TBD a handle for a given module memo key (specifier, with options), to cover other cases.
22:32
<kriskowal>
Again, I think the binding to ID is adequately solved by new ModuleSource(source, { base }) so importHook just returns a source that might be bound to a different base. For the purposes of importHook, the identity of the module source instance is not taken into account. That’s only germane if you import(source).
22:32
<guybedford>
hmm I suppose { id, moduleSource } is no different in ability than new ModuleSource(moduleSource, { base }), you're right ok I agree
22:33
<guybedford>
the problem is just the async resolver though, if we want to permit sync pipelines
22:33
<kriskowal>
That is, import(specifier) invokes importHook with a key based on the specifier and import(source) does not invoke importHook and uses the module source’s identity as the key in the module map.
22:33
<guybedford>
agreed
22:34
<kriskowal>
The Compartment shim handles synchronous pipelines with an importNowHook that is obliged to return synchronously.
22:35
<kriskowal>
In the absence of an importNowHook, importNow(specifier) works only if the transitive dependencies have already been loaded. As joyee mentioned today, a host like Node.js can avoid the races by implementing importNowHook and not providing an async hook at all.
22:35
<kriskowal>
But to cover the web and other environments, we have to provide the async loader path.
22:36
<kriskowal>
As a baseline.
22:36
<guybedford>
maybe we can discuss this more next time - but I still think it's nice to have a design that doesn't treat async and sync as separate whole implementations, but allows them to layer nicely
22:36
<guybedford>
of course, that's not a strong requirement, but if it's possible it seems slightly preferable to me
22:37
<kriskowal>
I agree. The question is possibility, given colored functions on the bottom.
22:37
<guybedford>
well it makes for a colourful discussion
22:39
<kriskowal>
It’s certainly possible to have async import fall through to the sync import if there’s a clear partition between synchronously and asynchronously loadable specifiers.
22:39
<guybedford>
The more I think about it I actually really like new ModuleSource(moduleSource, { base }) so far, and the way it fits into the proposal. ModuleSource constructor spec when!?
22:41
<kriskowal>
Given the mood in the room, ModuleSource(text) might have to be in an Annex if anywhere.
22:42
<kriskowal>
But new ModuleSource(source, { base }) would not have to wait.
22:42
<guybedford>
oh really? I wasn't aware there was still resistance to a new eval primitive
22:42
<kriskowal>
The proposal I’m working with Zb Tenerowicz (ZTZ/naugtur) on does not rely on new ModuleSource(text) but our big motivating use case really benefits from it.
22:42
<guybedford>
I mean, it already exists today with Blob, would just be much nicer to have a real representation
22:43
<kriskowal>
“No new paths to evaluating text” was in the feedback at last plenary.
22:44
<guybedford>
I see, I'd worry then if new ModuleSource(source, { base }) could allow cross-agent violations between policies
22:45
<kriskowal>
I was just lamenting the other day that a gzip(Uint8Array)=>Promise<Uint8Array> function is trivialish on the current web and portable with Node.js but obligates the implementation to take a weird detour through Blob and Response with an arbitrary (application/octet-stream anyone?) MIME type.
22:46
<kriskowal>
That’s interesting. I would expect a mechanism like postMessage to serialize both the CSP origin and import base separately and not be a problem for cross-agent enforcement.
22:46
<guybedford>
right, so base is still required to be separate from the CSP-related baseURL, and we're back at two URLs on module scripts
22:46
<guybedford>
well three with the responseURL
22:47
<kriskowal>
This seems fine to me. Is that unpalatable?
22:47
<guybedford>
I don't think so, it's just another pointer
22:48
<guybedford>
but alternatively we could treat new ModuleSource(source, { base }) as non-transferrable
22:49
<kriskowal>
I can’t imagine why that would be better but it also wouldn’t impact any of the cases I’m concerned with.
22:50
<guybedford>
also - if you don't need new ModuleSource(text) it might be an option to just permit load -> Promise<{ id: string, module: ModuleSource }> to not have to spec the constructor
22:51
<guybedford>
yeah it doesn't seem a major concern to me, just thinking it through as well. but these seem promising directions overall. please do feel free to ping me if I can help review
23:19
<bakkot>
that was probably me and I do still have that opinion
23:19
<bakkot>
I don't know if anyone else shares it though
23:20
<kriskowal>
(We can make progress without new ModuleSource(text) but I prefer new ModuleSource(sourceOrText, { base? }) because it would be more coherent in the preferred end state. It’s been tough, in the face of irreducible requirements, to keep the importHook return type simple.)
23:25
<kriskowal>
I am hoping that, in the fullness of time, I can convince you that our carefully respecting CSP has your concerns covered, but until then, yours is an opinion I’m accommodating to the extent I can, at least, by factoring these proposals so that the concern is separable from others. For some environments, having the option of lifting text will be attractive, so having an annex to keep them aligned is an accommodation that might work for everyone.
23:26
<bakkot>
I don't think anyone should respect CSP :P
23:26
<bakkot>
(in the sense of, it is my opinion that it is a terrible specification, not in the sense of, we don't have to play by its rules)
23:26
<kriskowal>
Well, respect in a sense.
23:31
<kriskowal>
I too come from a camp that agrees that there are better ways to make the web safe than CSP, just not necessarily that SBOM provenance chains are always the best solution. Honestly, if CSP actually closed all exfiltration holes, I would consider it useful under certain circumstances.
23:33
<bakkot>
Definitely useful under certain circumstances but they're mostly either "you are literally google" or "you are a single-team shop", which covers approximately 0% of the web
23:33
<bakkot>
which reminds me I need to put together a talk on that subject...
23:34
<kriskowal>
I’m prepared to clap.