Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bundle-source): Apply evasive transforms to Endo archives #2768

Merged
merged 6 commits into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/bundle-source/demo/comments/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/** @typedef {import('./types.js').Bogus} Bogus */
5 changes: 4 additions & 1 deletion packages/bundle-source/demo/dir1/encourage.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export const message = `You're great!`;
export const encourage = nick => `Hey ${nick}! ${message}`;
export const makeError = msg => Error(msg);
// Without an evasive transform, the following comment will trip the SES censor
// for dynamic imports. */
/** @type {import('./types.js').EncourageFn} */
export const encourage = nick => `Hey ${nick}! ${message}`;
5 changes: 5 additions & 0 deletions packages/bundle-source/demo/dir1/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* @callback EncourageFn
* @param {string} nick
* @returns {string}
*/
2 changes: 1 addition & 1 deletion packages/bundle-source/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"dependencies": {
"@agoric/acorn-eventual-send": "^2.1.4",
"@agoric/babel-parser": "^7.6.4",
"@agoric/compartment-mapper": "^0.2.3",
"@agoric/compartment-mapper": "^0.2.4",
"@agoric/transform-eventual-send": "^1.4.4",
"@babel/generator": "^7.6.4",
"@babel/traverse": "^7.8.3",
Expand Down
34 changes: 28 additions & 6 deletions packages/bundle-source/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const IMPORT_RE = new RegExp('\\b(import)(\\s*(?:\\(|/[/*]))', 'sg');
const HTML_COMMENT_START_RE = new RegExp(`${'<'}!--`, 'g');
const HTML_COMMENT_END_RE = new RegExp(`--${'>'}`, 'g');

const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
const read = async location => fs.promises.readFile(new URL(location).pathname);

export function tildotPlugin() {
Expand Down Expand Up @@ -92,15 +94,20 @@ async function makeLocationUnmapper({ sourceMap, ast }) {
function transformAst(ast, unmapLoc) {
babelTraverse(ast, {
enter(p) {
const { loc, leadingComments, trailingComments } = p.node;
if (p.node.comments) {
p.node.comments = [];
}
const {
loc,
comments,
leadingComments,
innerComments,
trailingComments,
} = p.node;
(comments || []).forEach(node => rewriteComment(node, unmapLoc));
// Rewrite all comments.
(leadingComments || []).forEach(node => rewriteComment(node, unmapLoc));
if (p.node.type.startsWith('Comment')) {
rewriteComment(p.node, unmapLoc);
}
(innerComments || []).forEach(node => rewriteComment(node, unmapLoc));
// If not a comment, and we are unmapping the source maps,
// then do it for this location.
if (unmapLoc) {
Expand All @@ -111,11 +118,15 @@ function transformAst(ast, unmapLoc) {
});
}

async function transformSource(code, { sourceMap, useLocationUnmap }) {
async function transformSource(
code,
{ sourceMap, useLocationUnmap, sourceType } = {},
) {
// Parse the rolled-up chunk with Babel.
// We are prepared for different module systems.
const ast = (babelParser.parse || babelParser)(code, {
plugins: ['bigInt'],
sourceType,
});

let unmapLoc;
Expand Down Expand Up @@ -148,7 +159,18 @@ export default async function bundleSource(
// individual package.jsons and driven by the compartment mapper.
const base = new URL(`file://${process.cwd()}`).toString();
const entry = new URL(startFilename, base).toString();
const bytes = await makeArchive(read, entry);
const bytes = await makeArchive(read, entry, {
moduleTransforms: {
async mjs(sourceBytes) {
const source = textDecoder.decode(sourceBytes);
const { code: object } = await transformSource(source, {
sourceType: 'module',
});
const objectBytes = textEncoder.encode(object);
return { bytes: objectBytes, parser: 'mjs' };
},
},
});
const endoZipBase64 = encodeBase64(bytes);
return { endoZipBase64, moduleFormat };
}
Expand Down
7 changes: 5 additions & 2 deletions packages/bundle-source/test/sanity.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function makeSanityTests(stackFiltering) {
const err = bundle.makeError('foo');
// console.log(err.stack);
t.assert(
stackContains(err.stack, 'encourage.js:3:'),
stackContains(err.stack, 'encourage.js:2:'),
'bundled source is in stack trace with correct line number',
);

Expand Down Expand Up @@ -120,7 +120,10 @@ export function makeSanityTests(stackFiltering) {
moduleFormat: mf2,
source: src2,
sourceMap: map2,
} = await bundleSource(`${__dirname}/../demo/dir1/encourage.js`);
} = await bundleSource(
`${__dirname}/../demo/dir1/encourage.js`,
'nestedEvaluate',
);
t.is(mf2, 'nestedEvaluate', 'module format 2 is nestedEvaluate');

const srcMap2 = `(${src2})\n${map2}`;
Expand Down
22 changes: 22 additions & 0 deletions packages/bundle-source/test/test-comment.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* global __dirname */
import '@agoric/install-ses';
import test from 'ava';
import { decodeBase64 } from '@endo/base64';
import { parseArchive } from '@agoric/compartment-mapper';
import bundleSource from '..';

function evaluate(src, endowments) {
Expand Down Expand Up @@ -61,3 +63,23 @@ test('comment block closer', async t => {
const srcMap1 = `(${src1})`;
nestedEvaluate(srcMap1)();
});

test('comments not associated with a code AST node', async t => {
t.plan(1);
const { endoZipBase64 } = await bundleSource(
`${__dirname}/../demo/comments/types.js`,
'endoZipBase64',
);
const endoZipBytes = decodeBase64(endoZipBase64);
const application = await parseArchive(endoZipBytes);
// If the TypeScript comment in this module does not get rewritten,
// attempting to import the module will throw a SES censorship error since
// import calls in comments are not distinguishable from containment escape
// through dynamic import.
// To verify, disable this line in src/index.js and observe that this test
// fails:
// (innerComments || []).forEach(node => rewriteComment(node, unmapLoc));
// eslint-disable-next-line dot-notation
await application['import']('./demo/comments/types.js');
t.is(1, 1);
});
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
dependencies:
ses "^0.11.0"

"@agoric/compartment-mapper@^0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@agoric/compartment-mapper/-/compartment-mapper-0.2.4.tgz#db78b3d3c34db2204aba6eaa29155a3e0a371e73"
integrity sha512-HppJtvTQP9R/a55dX+7H4xXYlj6oPmvN9xVvWVk+BDEq0//AiS1/3qpV+R6B34raNvBPatsBzYuhZQjdHSw3+A==
dependencies:
ses "^0.12.6"

"@agoric/make-hardener@^0.1.0", "@agoric/make-hardener@^0.1.2":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@agoric/make-hardener/-/make-hardener-0.1.3.tgz#807b0072bef95d935c3370d406d9dfeb719f69ee"
Expand Down