Canonicalize URLs prior to Policy specifier matching
Currently policies preempt the resolution process entirely and work off of raw specifiers. If file:///app/component.cjs
requires require("./util.js")
or imports import("./util.js")
policies only look for an exact match of "./util.js"
in "dependencies"
. This was found as a usability issue by @giltayar while comparing with / looking at import maps compatibility. In particular, with import maps the specifiers are always canonicalized prior to performing matching, both the specifiers in the map that can be matched and the specifier from the call site. This allows a mapping table to cover all ways to get a hold of file:///app/util.js
in a more concise manner.
Consider this as the source texts :
// `file:///app/component.cjs`
require('./dir/foo.cjs');
require('./bar.cjs');
// `file:///app/dir/foo.cjs`
require('./bar.cjs');
and a policy at file:///policy.json
containing:
{
"scopes": {
"file:": {
"cascade": true,
"integrity": true,
"dependencies": {
"./bar.cjs": "file:///app/bar.js"
}
}
}
}
This policy will intercept both the loads from file:///app/dir/foo.cjs
and file:///app/component.cjs
. And have both resolve to file:///app/bar.js
.
Canonicalizing early would mean that it only intercepts resolution that PRIOR TO NODE RESOLUTION would point to file:///bar.cjs
regardless of the file containing the load. This actually is a breaking change to policies and one would have to alter some data as seen in the tests in this PR but it would greatly simplify some cases so that instead of needing to specify all routes to dependencies one only needs to specify the eventual target if resolved prior to node resolving. This must be done prior to node resolving so that things like fs
and react
are still properly able to be intercepted. Due to the number of closures this uses we likely should add a benchmark and migrate to be less closure heavy and pass around objects instead.