├── .npmrc ├── .gitattributes ├── package.json ├── .gitignore ├── LICENSE ├── README.md ├── spec.emu └── index.html /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | index.html -diff merge=ours 2 | spec.js -diff merge=ours 3 | spec.css -diff merge=ours 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "dynamic-import-host-adjustment", 4 | "description": "A repository template for ECMAScript proposals.", 5 | "scripts": { 6 | "build": "ecmarkup spec.emu index.html", 7 | "precommit": "npm run build" 8 | }, 9 | "homepage": "https://github.com/tc39/dynamic-import-host-adjustment#readme", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/tc39/dynamic-import-host-adjustment.git" 13 | }, 14 | "license": "MIT", 15 | "devDependencies": { 16 | "ecmarkup": "^3.11.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Only apps should have lockfiles 40 | yarn.lock 41 | package-lock.json 42 | npm-shrinkwrap.json 43 | 44 | # Emacs droppings 45 | *~ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ECMA TC39 and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dynamic Import Host Adjustment 2 | 3 | | | | 4 | | ------------- | ------------- | 5 | | **Stage** | [2](https://tc39.es/process-document/) | 6 | | **Spec** | [source](https://github.com/tc39/dynamic-import-host-adjustment/blob/master/spec.emu), [output](https://tc39.es/dynamic-import-host-adjustment/) | 7 | | **Tests** | [TODO](#testing) | 8 | | **Champion** | @mikesamuel | 9 | | **Reviewers** | @bakkot, @erights, @bmeck | 10 | 11 | [Trusted Types][] guards sensitive APIs; it double checks that values 12 | have been trusted by policy code before performing operations that 13 | cannot be undone. 14 | 15 | The dynamic import operator, `import(...)`, loads code and initializes modules. 16 | Loading code from an untrustworthy source is an operation that cannot be undone. 17 | 18 | This adjusts the host callout which enables dynamic loading. With it: 19 | 20 | 1. The host receives the original specifier (before it is stringified) so can use runtime type 21 | information to decide whether to allow code loading to proceed. 22 | 1. The host callout can control stringification and convey the result to FinishDynamicImport 23 | to avoid repeated stringification, and to integrate with [default policies][default policy]. 24 | 25 | ## Testing 26 | 27 | Tests, to be written, will be implemented as 28 | [web-platform-tests](https://github.com/web-platform-tests/wpt) and 29 | will focus on the following properties: 30 | 31 | 1. Polymorphic objects stringified once. Something like 32 | ```js 33 | importScripts("/resources/testharness.js"); 34 | 35 | test( 36 | () => { 37 | let stringifyCount = 0; 38 | import({ 39 | toString() { 40 | let specifier = `data:text/javascript,export default ${ stringifyCount }`; 41 | ++stringifyCount; 42 | return specifier; 43 | } 44 | }) 45 | .then( 46 | (defaultExport) => { 47 | assert_equals(stringifyCount, 1); 48 | assert_equals(defaultExport, 0); 49 | }, 50 | (err) => { 51 | assert_equals(err, null); 52 | }) 53 | .finally(done); 54 | }, 55 | 'DynamicImportStringifiesSpecifierOnce'); 56 | ``` 57 | 1. The above, with coverage for both *null* and non-*null* referencing modules. 58 | 1. Tests specific to trusted-types host implementation. 59 | 60 | See also [webappsec-csp #243](https://github.com/w3c/webappsec-csp/issues/243) 61 | on `import('data:...')` as CSP bypass. 62 | 63 | [Trusted Types]: https://wicg.github.io/trusted-types/dist/spec/ 64 | [default policy]: https://wicg.github.io/trusted-types/dist/spec/#default-policy-hdr 65 | -------------------------------------------------------------------------------- /spec.emu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | title: Dynamic Import Host Adjustment 8 | stage: 0 9 | contributors: Mike Samuel 10 |11 | 12 |
This proposal allows the host to inspect and adjust the argument, 13 | _x_, in `import(x)` so that Trusted Types can use runtime-type 14 | information to decide whether to allow the import to proceed.
15 | 16 |For more detail see the 17 | explainer.
18 | 19 |This proposal lays out two options:
20 | 21 |First, this proposal adjusts `import(...)` to stringify its argument 31 | via a host callout.
32 |Second, this proposal defines the new host callout.
55 |HostDynamicValueToSpecifier is an implementation-defined
60 | abstract operation that converts a runtime value representing a
61 | module specifier, _specifier_, to a string that corresponds to a
62 | |ModuleSpecifier| String. The host also receives the Script
63 | Record or Module Record, _referencingScriptOrModule_.
64 | _referencingScriptOrModule_ may be *null*, if the
65 | resolution is being performed in the context of
66 | an
The default implementation performs the following steps:
71 |The implementation of HostDynamicValueToSpecifier must 76 | conform to the following requirements:
77 |Like the first option, this proposal adjusts `import(...)` to not stringify 94 | its argument too early. 95 | This means that HostImportModuleDynamically could pass given a 96 | TrustedModuleSpecifier but not given a raw string.
97 |HostImportModuleDynamically is an implementation-defined abstract
130 | operation that performs any necessary setup work in order to make
131 | available the module corresponding to the |ModuleSpecifier| String,
132 | _specifier_, occurring within the context of the script or module
133 | represented by the Script Record or Module Record
134 | _referencingScriptOrModule_. (_referencingScriptOrModule_ may also
135 | be *null*, if there is no active script or module when
136 | the
The implementation of HostImportModuleDynamically must conform to 141 | the following requirements:
142 | 143 |This is necessary to prevent a different apparent order of operation 161 | that could occur if user code received the module promise before 162 | _specifier_ is stringified.
163 | 164 |A non-conforming implementation might behave improperly for:
165 |
166 | const modulePromise = import({
167 | toString() {
168 | console.log('stringified');
169 | return './foo';
170 | },
171 | };
172 | console.log('got promise');
173 | modulePromise.then(
174 | () => { console.log('module resolved'); },
175 | () => { console.log('module rejected'); });
176 |
177 | // Before: stringified, got promise, module ...
178 | // ⤩
179 | // Non-conforming: got promise, stringified, module ...
180 |
181 | Since _specifier_ is not a string, the same would mean 240 | less, but this is not necessary to preserve any single-fetch requirement 241 | since FinishDynamicImport delegates to HostResolveImportModule which says
242 |243 | "This operation must be idempotent if it completes 244 | normally. Each time it is called with a specific 245 | _referencingScriptOrModule_, _specifier_ pair as arguments 246 | it must return the same Module Record instance." 247 |248 |
The actual process performed is implementation-defined, but 259 | typically consists of performing whatever I/O operations are 260 | necessary to allow HostResolveImportedModule to synchronously 261 | retrieve the appropriate Module Record, and then calling its 262 | Evaluate concrete method. This might require performing similar 263 | normalization as HostResolveImportedModule does.
264 |Finally, we adjust FinishDynamicImport to use the module specifier 271 | string computed by the host callout.
272 |FinishDynamicImport completes the process of a dynamic import
277 | originally started by an
278 |
This proposal allows the host to inspect and adjust the argument,
1811 | x, in import(x) so that Trusted Types can use runtime-type
1812 | information to decide whether to allow the import to proceed.
For more detail see the 1815 | 1816 | explainer.
1817 | 1818 |This proposal lays out two options:
1819 | 1820 |First, this proposal adjusts import(...) to stringify its argument
1830 | via a host callout.
Second, this proposal defines the new host callout.
1847 |HostDynamicValueToSpecifier is an implementation-defined
1852 | abstract operation that converts a runtime value representing a
1853 | module specifier, specifier, to a string that corresponds to a
1854 | import()
The default implementation performs the following steps:
1863 |The implementation of HostDynamicValueToSpecifier must 1867 | conform to the following requirements:
1868 |Like the first option, this proposal adjusts import(...) to not stringify
1885 | its argument too early.
1886 | This means that
HostImportModuleDynamically is an implementation-defined abstract
1916 | operation that performs any necessary setup work in order to make
1917 | available the module corresponding to the import()
The implementation of HostImportModuleDynamically must conform to 1927 | the following requirements:
1928 | 1929 |This is necessary to prevent a different apparent order of operation 1949 | that could occur if user code received the module promise before 1950 | specifier is stringified.
1951 | 1952 |A non-conforming implementation might behave improperly for:
1953 |const modulePromise = import({
1954 | toString() {
1955 | console.log('stringified');
1956 | return './foo';
1957 | },
1958 | };
1959 | console.log('got promise');
1960 | modulePromise.then(
1961 | () => { console.log('module resolved'); },
1962 | () => { console.log('module rejected'); });
1963 |
1964 | // Before: stringified, got promise, module ...
1965 | // ⤩
1966 | // Non-conforming: got promise, stringified, module ...
1967 | Since specifier is not a string, the same would mean
2030 | less, but this is not necessary to preserve any single-fetch requirement
2031 | since
2033 | "This operation must be idempotent if it completes 2034 | normally. Each time it is called with a specific 2035 | referencingScriptOrModule, specifier pair as arguments 2036 | it must return the same2039 |Module Record instance." 2037 | 2038 |
The actual process performed is implementation-defined, but
2051 | typically consists of performing whatever I/O operations are
2052 | necessary to allow
Finally, we adjust FinishDynamicImport to use the module specifier 2063 | string computed by the host callout.
2064 |FinishDynamicImport completes the process of a dynamic import
2069 | originally started by an
2070 |
2071 | import()
© 2019 Mike Samuel
2083 | 2084 |All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.
2086 | 2087 |Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
2088 | 2089 |THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2096 | 2097 |