Skip to content

Commit

Permalink
feat(compiler): allow to set default context
Browse files Browse the repository at this point in the history
fixes #69
  • Loading branch information
tripodsan committed Jun 5, 2019
1 parent 5a140d3 commit 5c735d0
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 4 deletions.
12 changes: 12 additions & 0 deletions src/compiler/Compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module.exports = class Compiler {
this._includeRuntime = false;
this._modHTLEngine = '@adobe/htlengine';
this._codeTemplate = null;
this._defaultMarkupContext = undefined;
}

withOutputDirectory(dir) {
Expand Down Expand Up @@ -81,6 +82,16 @@ module.exports = class Compiler {
return this;
}

/**
* Sets the default markup context when writing properties to the response.
* @param {MarkupContext} context the default context
* @return this
*/
withDefaultMarkupContext(context) {
this._defaultMarkupContext = context;
return this;
}

/**
* Compiles the specified source file and saves the result, overwriting the
* file name.
Expand Down Expand Up @@ -139,6 +150,7 @@ module.exports = class Compiler {
async _parse(source, baseDir) {
const commands = new TemplateParser()
.withErrorListener(ThrowingErrorListener.INSTANCE)
.withDefaultMarkupContext(this._defaultMarkupContext)
.parse(source);

// find any templates and inject them into the stream
Expand Down
16 changes: 12 additions & 4 deletions src/parser/html/MarkupHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ module.exports = class MarkupHandler {
this._htlParser = new HTLParser().withErrorListener(ThrowingErrorListener.INSTANCE);
this._transformer = new ExpressionTransformer();
this._symbolGenerator = new SymbolGenerator();
this._defaultMarkupContext = MarkupContext.HTML;
}

/**
* Sets the default markup context when writing properties to the response.
* @param {MarkupContext} context the default context
* @return this
*/
withDefaultMarkupContext(context) {
this._defaultMarkupContext = context;
return this;
}

onDocumentStart() {
Expand Down Expand Up @@ -138,7 +149,7 @@ module.exports = class MarkupHandler {
}

onText(text, line, column) {
const markupContext = this._inScriptOrStyle ? MarkupContext.TEXT : MarkupContext.HTML;
const markupContext = this._inScriptOrStyle ? MarkupContext.TEXT : this._defaultMarkupContext;
this._outText(text, markupContext, line, column);
}

Expand Down Expand Up @@ -336,9 +347,6 @@ module.exports = class MarkupHandler {

const location = { line, column };
const interpolation = this._htlParser.parse(content);
if (markupContext == null) {
// interpolation = requireContext(interpolation); todo
}
const plainText = interpolation.getPlainText();
if (plainText != null) {
this._out(plainText);
Expand Down
13 changes: 13 additions & 0 deletions src/parser/html/TemplateParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ module.exports = class TemplateParser {
return this;
}

/**
* Sets the default markup context when writing properties to the response.
* @param {MarkupContext} context the default context
* @return this
*/
withDefaultMarkupContext(context) {
this._defaultMarkupContext = context;
return this;
}

/**
* Parses the input and returns an the generated commands.
* @param {String} input Input text
Expand All @@ -34,6 +44,9 @@ module.exports = class TemplateParser {
parse(input) {
const stream = new CommandStream();
const handler = new MarkupHandler(stream);
if (this._defaultMarkupContext !== undefined) {
handler.withDefaultMarkupContext(this._defaultMarkupContext);
}

HTMLParser.parse(input, handler);
return stream.commands;
Expand Down
21 changes: 21 additions & 0 deletions test/runtime_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const TEMPLATE_SIMPLE_2 = path.resolve(__dirname, 'templates', 'simple2.htl');
const TEMPLATE_XSS = path.resolve(__dirname, 'templates', 'xss.htl');
const EXPECTED_SIMPLE_2 = path.resolve(__dirname, 'templates', 'simple2.html');
const EXPECTED_XSS = path.resolve(__dirname, 'templates', 'xss.html');
const EXPECTED_XSS_UNSAFE = path.resolve(__dirname, 'templates', 'xss_unsafe.html');
const GLOBALS = {
world: 'Earth',
properties: {
Expand Down Expand Up @@ -130,4 +131,24 @@ describe('Runtime Tests', () => {
const { body } = await main(GLOBALS);
assert.equal(body, await fse.readFile(EXPECTED_XSS, 'utf-8'));
});

it('Protects against XSS (unsafe)', async () => {
const outputDir = path.join(__dirname, 'generated');

const compiler = new Compiler()
.withDefaultMarkupContext('unsafe')
.withOutputDirectory(outputDir)
.includeRuntime(true)
.withRuntimeHTLEngine(path.resolve(__dirname, '..', pkgJson.main))
.withOutputFile(path.resolve(outputDir, 'runtime_test_script_4.js'))
.withRuntimeVar(Object.keys(GLOBALS));

const filename = await compiler.compileFile(TEMPLATE_XSS);

// eslint-disable-next-line import/no-dynamic-require,global-require
const { main } = require(filename);

const { body } = await main(GLOBALS);
assert.equal(body.trim(), (await fse.readFile(EXPECTED_XSS_UNSAFE, 'utf-8')).trim());
});
});
45 changes: 45 additions & 0 deletions test/templates/xss_unsafe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<!--
~ Copyright 2019 Adobe. All rights reserved.
~ This file is licensed to you under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License. You may obtain a copy
~ of the License at http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software distributed under
~ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
~ OF ANY KIND, either express or implied. See the License for the specific language
~ governing permissions and limitations under the License.
-->
<html>
<head>
<title>Hello, world!</title>
<meta charset="utf-8"/>
</head>
<body>
<ul>
<li><a href="javascript:alert(0)">XSS Link</a></li>
<li><a href="http://www.valid.url">Non XSS Link</a></li>
<li><a title="javascript:alert(0)">Non XSS Link</a></li>
<li><a name="Foo">XSS Link</a></li>
<li><a>XSS Link</a></li>
<li><a>XSS Link</a></li>
<li><a>XSS Link</a></li>
<li><a href="#" onclick="alert&#x28;0&#x29;">XSS Link</a></li>
<li><img src="javascript:alert(0)"/></li>
<li><img src="javascript:alert(0)"/></li>
<li><img src="fake.jpg" onerror="alert(0)"/></li>
<li><img src=`javascript:alert(0)`/></li>
<li><img src="java script:alert(0)"/></li>
<li><img src="java&#x0A;script:alert(0)"/></li>
<li><img src="fake.jpg" onerror="alert&#x28;0&#x29;"/></li>
</ul>
<script>alert(0);</script>
<script src="http://do.not.serve/this.js"></script>
<script src="//do.not.serve/this.js"></script>
<form action="&quot;&gt;&lt;script&gt;alert&#x28;0&#x29;&#x3b;&lt;&#x2f;script&gt;" onsubmit="alert&#x28;0&#x29;">
<input name="test" value="&quot;&gt;&lt;script&gt;alert&#x28;0&#x29;&#x3b;&lt;&#x2f;script&gt;"/>
</form>
<img src="/foo/bla.jpg"/>
<img src="https://www.primordialsoup.life/image.png"/>
</body>
</html>

0 comments on commit 5c735d0

Please sign in to comment.