Hello,
First of all, thank you for the work on this proposal, I think the overall direction is excellent and the static, typed version of import ... with { type: "..." } makes a lot of sense.
However, I’d like to raise a security concern specifically around the dynamic form:
import(url, { type: "text" })
This is not about XSS attacks, those remain fully in scope regardless of this proposal.
The concern is about large-scale blind supply-chain attacks, and how dynamic import-as-text affects the defender’s ability to harden the runtime.
Background: hashed JS assets and blind discovery attacks
Modern web application build systems (Vite, Webpack, etc.) produce assets like:
/assets/index-CDtdNkf4.js
/assets/chunk-XYZ123.js
These filenames include a hash, which means an attacker who gains same-origin execution through a compromised dependency cannot know ahead of time which chunks contain interesting exports, such as:
- OIDC client instances
- authenticated fetch wrappers
- sensitive state machines
To discover these, a typical attack today must:
- Parse the HTML to find the entrypoint
fetch that asset
- Recursively discover its imports
- Parse each chunk
- Look for patterns corresponding to sensitive exports
Defenders can currently block step (2) by hardening fetch and other runtime built-ins very early, before any other application code runs.
This is extremely useful for libraries that intentionally treat the runtime as a hostile environment (for example, client-side OIDC implementations that aim to prevent token exfiltration even under supply-chain compromise).
Why dynamic import-as-text changes the threat model
Under this proposal, an attacker can do:
import(`/assets/index-CDtdNkf4.js`, { type: "text" })
And this cannot be prevented by:
- monkey-patching
fetch
- freezing built-ins during early initialization
- guarding network primitives
Because dynamic import is a language-level mechanism that defenders cannot intercept.
This means:
The defender loses the ability to mitigate blind discovery attacks
by disabling runtime asset introspection.
Again: this does not impact targeted attacks (the attacker already knows the hash), but it does impact large-scale opportunistic attacks where the adversary does not know the asset graph in advance.
Today, this is still something defenders can block.
With dynamic import-as-text, they cannot.
Why this matters (concrete real-world case)
I am working on a client-side OIDC + PKCE implementation that treats the browser runtime as hostile and aims to prevent token exfiltration even if a dependency in the tree is compromised.
This kind of defensive approach requires limiting introspection of application assets.
If dynamic import-as-text is standardized without an interception/hardening mechanism, this class of protections becomes impossible.
This is a niche use-case, but it is one where security properties materially degrade under this proposal.
Summary
- Static
import ... as text has no issue, its target must be known as build time, it's not levragable for discovring the assets graph.
- Dynamic
import(url, { type: "text" }) introduces a new unpreventable attack surface: language-level introspection of hashed JS assets.
- This removes a defensive technique currently available to developers working in hostile runtime models.
- If possible, the dynamic form should allow hosts to intercept / disable it.
I realize this feedback is arriving at Stage 2, but the concern is not niche.
This affects any web application performing token exchange on the client, which includes a large portion of modern SPAs implementing OAuth/OIDC.
Today, most of the ecosystem implicitly treats “same-origin script execution = game over,” but large-scale NPM supply-chain compromises have shown that this assumption is increasingly unsafe. Some implementations (including mine) actively harden the runtime against hostile co-residents by disabling or mediating built-in primitives before any untrusted code loads.
Thank you for your time and for considering the security implications.
Hello,
First of all, thank you for the work on this proposal, I think the overall direction is excellent and the static, typed version of
import ... with { type: "..." }makes a lot of sense.However, I’d like to raise a security concern specifically around the dynamic form:
This is not about XSS attacks, those remain fully in scope regardless of this proposal.
The concern is about large-scale blind supply-chain attacks, and how dynamic import-as-text affects the defender’s ability to harden the runtime.
Background: hashed JS assets and blind discovery attacks
Modern web application build systems (Vite, Webpack, etc.) produce assets like:
These filenames include a hash, which means an attacker who gains same-origin execution through a compromised dependency cannot know ahead of time which chunks contain interesting exports, such as:
To discover these, a typical attack today must:
fetchthat assetDefenders can currently block step (2) by hardening
fetchand other runtime built-ins very early, before any other application code runs.This is extremely useful for libraries that intentionally treat the runtime as a hostile environment (for example, client-side OIDC implementations that aim to prevent token exfiltration even under supply-chain compromise).
Why dynamic import-as-text changes the threat model
Under this proposal, an attacker can do:
And this cannot be prevented by:
fetchBecause dynamic import is a language-level mechanism that defenders cannot intercept.
This means:
Again: this does not impact targeted attacks (the attacker already knows the hash), but it does impact large-scale opportunistic attacks where the adversary does not know the asset graph in advance.
Today, this is still something defenders can block.
With dynamic import-as-text, they cannot.
Why this matters (concrete real-world case)
I am working on a client-side OIDC + PKCE implementation that treats the browser runtime as hostile and aims to prevent token exfiltration even if a dependency in the tree is compromised.
This kind of defensive approach requires limiting introspection of application assets.
If dynamic import-as-text is standardized without an interception/hardening mechanism, this class of protections becomes impossible.
This is a niche use-case, but it is one where security properties materially degrade under this proposal.
Summary
import ... as texthas no issue, its target must be known as build time, it's not levragable for discovring the assets graph.import(url, { type: "text" })introduces a new unpreventable attack surface: language-level introspection of hashed JS assets.I realize this feedback is arriving at Stage 2, but the concern is not niche.
This affects any web application performing token exchange on the client, which includes a large portion of modern SPAs implementing OAuth/OIDC.
Today, most of the ecosystem implicitly treats “same-origin script execution = game over,” but large-scale NPM supply-chain compromises have shown that this assumption is increasingly unsafe. Some implementations (including mine) actively harden the runtime against hostile co-residents by disabling or mediating built-in primitives before any untrusted code loads.
Thank you for your time and for considering the security implications.