VYPR
Critical severityNVD Advisory· Published Aug 8, 2023· Updated Oct 10, 2024

SES's dynamic import and spread operator provides possible path to arbitrary exfiltration and execution

CVE-2023-39532

Description

SES sandbox vulnerability allows guest programs to escape confinement via spread operator with dynamic import, enabling info disclosure or arbitrary code execution in certain environments.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

SES sandbox vulnerability allows guest programs to escape confinement via spread operator with dynamic import, enabling info disclosure or arbitrary code execution in certain environments.

The vulnerability lies in SES, a JavaScript environment designed for safe execution of arbitrary code in compartments. A guest program with no endowments can break out of its compartment by using the spread operator with dynamic import, e.g., {...import(arbitraryModuleSpecifier)}, gaining access to the host's dynamic import mechanism [1][3]. This affects versions 0.13.0-0.13.4, 0.14.0-0.14.4, 0.15.0-0.15.23, 0.16.0-0.16.0, 0.17.0-0.17.0, and 0.18.0-0.18.6 [1].

Exploitation requires the guest to be running inside a compartment; no additional endowments are needed. The attack surface varies by host environment. On the web or in web extensions, a Content-Security-Policy can mitigate the risk by restricting the modules that can be imported, but without CSP, dynamic import can issue HTTP requests for data exfiltration or code execution from same-origin resources [3]. In Node.js, the attacker gains full access to Node's module system and can import data URLs, enabling arbitrary code execution [3]. Within XS workers, the impact is limited to the host's configured module system, typically only local file system access [1].

The primary impact is information exfiltration or arbitrary code execution, depending on the host configuration and protections [1]. In Node.js, the ability to import data URLs makes it a clear path to arbitrary execution [3].

Patches are available in versions 0.13.5, 0.14.5, 0.15.24, 0.16.1, 0.17.1, and 0.18.7 [1][3]. Workarounds include enforcing a strict Content-Security-Policy on the web to limit import targets [3]. The fix, implemented in commit fc90c64, censors spread import expressions to prevent this bypass [4].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
sesnpm
>= 0.13.0, < 0.13.50.13.5
sesnpm
>= 0.14.0, < 0.14.50.14.5
sesnpm
>= 0.15.0, < 0.15.240.15.24
sesnpm
>= 0.16.0, < 0.16.10.16.1
sesnpm
>= 0.17.0, < 0.17.10.17.1
sesnpm
>= 0.18.0, < 0.18.70.18.7

Affected products

2

Patches

1
fc90c6429604

fix(fix): Censor spread import

https://github.com/endojs/endoKris KowalJul 27, 2023via ghsa
3 files changed · +36 2
  • packages/ses/NEWS.md+8 0 modified
    @@ -1,5 +1,13 @@
     User-visible changes in SES:
     
    +# Next
    +
    +- Censors the pattern `{...import(specifier)`}.
    +  We previously censored `import(specifier)` and expressly allowed
    +  `object.import(specifier)`.
    +  The relaxation for the latter form in version 0.13.0 inadvertently allowed
    +  import with the spread operator.
    +
     # v0.18.5 (2023-07-14)
     
     - Adds `assert.bare` for embedding unquoted strings in details.
    
  • packages/ses/src/transforms.js+1 1 modified
    @@ -106,7 +106,7 @@ export const evadeHtmlCommentTest = src => {
     // /////////////////////////////////////////////////////////////////////////////
     
     const importPattern = new FERAL_REG_EXP(
    -  '(^|[^.])\\bimport(\\s*(?:\\(|/[/*]))',
    +  '(^|[^.]|\\.\\.\\.)\\bimport(\\s*(?:\\(|/[/*]))',
       'g',
     );
     
    
  • packages/ses/test/test-transforms.js+27 1 modified
    @@ -6,7 +6,7 @@ import {
     } from '../src/transforms.js';
     
     test('no-import-expression regexp', t => {
    -  t.plan(9);
    +  t.plan(14);
     
       // Note: we cannot define these as regular functions (and then stringify)
       // because the 'esm' module loader that we use for running the tests (i.e.
    @@ -20,17 +20,23 @@ test('no-import-expression regexp', t => {
       const safe = 'const a = 1';
       const safe2 = "const a = notimport('evil')";
       const safe3 = "const a = importnot('evil')";
    +  const safe4 = "const a = compartment.import('name')";
     
       const obvious = "const a = import('evil')";
       const whitespace = "const a = import ('evil')";
       const comment = "const a = import/*hah*/('evil')";
       const doubleSlashComment = "const a = import // hah\n('evil')";
       const newline = "const a = import\n('evil')";
       const multiline = "\nimport('a')\nimport('b')";
    +  const spread = "{...import('exfil')}";
    +  const spread2 = "{... import('exfil')}";
    +  const spread3 = "{\n...\nimport\n('exfil')}";
    +  const spread4 = "{\n...\nimport/**/\n('exfil')}";
     
       t.is(rejectImportExpressions(safe), safe, 'safe');
       t.is(rejectImportExpressions(safe2), safe2, 'safe2');
       t.is(rejectImportExpressions(safe3), safe3, 'safe3');
    +  t.is(rejectImportExpressions(safe4), safe4, 'safe4');
       t.throws(
         () => rejectImportExpressions(obvious),
         { instanceOf: SyntaxError },
    @@ -62,6 +68,26 @@ test('no-import-expression regexp', t => {
         'possible import expression rejected around line 2',
         'multiline',
       );
    +  t.throws(
    +    () => rejectImportExpressions(spread),
    +    { instanceOf: SyntaxError },
    +    'spread',
    +  );
    +  t.throws(
    +    () => rejectImportExpressions(spread2),
    +    { instanceOf: SyntaxError },
    +    'spread2',
    +  );
    +  t.throws(
    +    () => rejectImportExpressions(spread3),
    +    { instanceOf: SyntaxError },
    +    'spread3',
    +  );
    +  t.throws(
    +    () => rejectImportExpressions(spread4),
    +    { instanceOf: SyntaxError },
    +    'spread4',
    +  );
     });
     
     test('no-html-comment-expression regexp', t => {
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.