Revision: 5678
Author: [email protected]
Date: Tue Apr 8 17:39:52 2014 UTC
Log: Backport to es53 branch: 5671,5672,5673,5675,5676,5677
https://codereview.appspot.com/85520044
All changes since the previous merge except for r5674 which is an
es53 removal cleanup.
The merge command was
svn merge -c 5671,5672,5673,5675,5676,5677 ^/trunk
and there was one conflict, which was just whitespace.
[email protected]
http://code.google.com/p/google-caja/source/detail?r=5678
Modified:
/branches/es53
/branches/es53/src/com/google/caja/lang/html/html4-attributes-defs.json
/branches/es53/src/com/google/caja/lang/html/html4-attributes-whitelist.json
/branches/es53/src/com/google/caja/lang/html/html5-attributes-defs.json
/branches/es53/src/com/google/caja/lang/html/html5-attributes-whitelist.json
/branches/es53/src/com/google/caja/plugin/domado.js
/branches/es53/src/com/google/caja/plugin/html-emitter.js
/branches/es53/src/com/google/caja/ses/compileExprLater.js
/branches/es53/src/com/google/caja/ses/debug.js
/branches/es53/src/com/google/caja/ses/explicit.html
/branches/es53/src/com/google/caja/ses/makeSimpleAMDLoader.js
/branches/es53/src/com/google/caja/ses/repair-framework.js
/branches/es53/src/com/google/caja/ses/startSES.js
/branches/es53/src/com/google/caja/ses/useHTMLLogger.js
/branches/es53/tests/com/google/caja/plugin/test-domado-dom-guest.html
=======================================
--- /branches/es53/src/com/google/caja/lang/html/html4-attributes-defs.json
Wed May 1 17:22:42 2013 UTC
+++ /branches/es53/src/com/google/caja/lang/html/html4-attributes-defs.json
Tue Apr 8 17:39:52 2014 UTC
@@ -558,20 +558,9 @@
{ "key": "TABLE::SUMMARY",
"description": "purpose/structure for speech output",
"optional": true },
- { "key": "A::TABINDEX", "description": "position in tabbing order",
- "pattern": "[0-9]+", "optional": true },
- { "key": "AREA::TABINDEX", "description": "position in tabbing
order",
- "pattern": "[0-9]+", "optional": true },
- { "key": "BUTTON::TABINDEX", "description": "position in tabbing
order",
- "pattern": "[0-9]+", "optional": true },
- { "key": "INPUT::TABINDEX", "description": "position in tabbing
order",
- "pattern": "[0-9]+", "optional": true },
- { "key": "OBJECT::TABINDEX", "description": "position in tabbing
order",
- "pattern": "[0-9]+", "optional": true },
- { "key": "SELECT::TABINDEX", "description": "position in tabbing
order",
- "pattern": "[0-9]+", "optional": true },
- { "key": "TEXTAREA::TABINDEX", "description": "position in tabbing
order",
- "pattern": "[0-9]+", "optional": true },
+ { "key": "*::TABINDEX", "description": "position in tabbing order",
+ "pattern": "-1|[0-9]+", "optional": true,
+ "comment": "-1 and on-any-element are extensions/HTML5" },
{ "key": "A::TARGET", "description": "render in this frame",
"type": "FRAME_TARGET", "default": "_self", "optional": true },
{ "key": "AREA::TARGET", "description": "render in this frame",
=======================================
---
/branches/es53/src/com/google/caja/lang/html/html4-attributes-whitelist.json
Mon Oct 15 18:33:58 2012 UTC
+++
/branches/es53/src/com/google/caja/lang/html/html4-attributes-whitelist.json
Tue Apr 8 17:39:52 2014 UTC
@@ -193,12 +193,7 @@
"OL::START",
"*::STYLE",
"TABLE::SUMMARY",
- "A::TABINDEX",
- "AREA::TABINDEX",
- "BUTTON::TABINDEX",
- "INPUT::TABINDEX",
- "SELECT::TABINDEX",
- "TEXTAREA::TABINDEX",
+ "*::TABINDEX",
"A::TARGET",
"AREA::TARGET",
"FORM::TARGET",
=======================================
--- /branches/es53/src/com/google/caja/lang/html/html5-attributes-defs.json
Wed Jun 19 20:54:01 2013 UTC
+++ /branches/es53/src/com/google/caja/lang/html/html5-attributes-defs.json
Tue Apr 8 17:39:52 2014 UTC
@@ -248,8 +248,7 @@
"pattern": "-?(?:[0-9]+(?:\\.[0-9]+)?|
\\.[0-9]+)(?:[Ee][-+]?[0-9]+)?",
"optional": true },
{ "key": "INPUT::PATTERN", "description": "Pattern to be matched by
the form control's value",
- "optional": true,
- "comment": "TODO(kpreid): defined to be JS regex; is there a
hazard here?" },
+ "optional": true },
{ "key": "A::PING", "description": "URLs to ping",
"mimeTypes": "*/*", "optional": true,
"type": "URI", "uriEffect": "NEW_DOCUMENT",
=======================================
---
/branches/es53/src/com/google/caja/lang/html/html5-attributes-whitelist.json
Tue Jul 2 21:37:42 2013 UTC
+++
/branches/es53/src/com/google/caja/lang/html/html5-attributes-whitelist.json
Tue Apr 8 17:39:52 2014 UTC
@@ -64,6 +64,7 @@
"*::ONSUBMIT",
"*::ONUNLOAD",
"DETAILS::OPEN",
+ "INPUT::PATTERN",
"INPUT::PLACEHOLDER",
"TEXTAREA::PLACEHOLDER",
"VIDEO::POSTER",
@@ -235,10 +236,6 @@
{ "key": "DIALOG::OPEN",
"comment":
"TODO(kpreid): further review (can this steal page focus/pop
out?)" },
- { "key": "INPUT::PATTERN",
- "comment":
- "TODO(kpreid): further review (allegedly a JS regexp; is that
safe?)"
- },
{ "key": "A::PING",
"comment": "TODO(kpreid): introduce multiple-URI type for this" },
{ "key": "AREA::PING",
=======================================
--- /branches/es53/src/com/google/caja/plugin/domado.js Tue Jan 7 21:20:26
2014 UTC
+++ /branches/es53/src/com/google/caja/plugin/domado.js Tue Apr 8 17:39:52
2014 UTC
@@ -2190,7 +2190,8 @@
var qsaVirtualization = cajaVM.def({
containerClass: null,
idSuffix: idSuffix,
- tagPolicy: tagPolicy
+ tagPolicy: tagPolicy,
+ virtualizeAttrName: sanitizeOneAttr
});
/**
=======================================
--- /branches/es53/src/com/google/caja/plugin/html-emitter.js Mon Aug 26
20:59:09 2013 UTC
+++ /branches/es53/src/com/google/caja/plugin/html-emitter.js Tue Apr 8
17:39:52 2014 UTC
@@ -343,6 +343,7 @@
function hasChild(el, name) {
if (!el) { return false; }
+
var child = makeDOMAccessible(el.firstChild);
for (; child; child = makeDOMAccessible(child.nextSibling)) {
if (child.nodeType === 1 && virtTagName(child) === name) {
@@ -595,12 +596,16 @@
var proxiedUrl = getMitigatedUrl(url);
var mitigateOpts;
if (proxiedUrl) {
- // Disable mitigation
+ // Disable mitigation.
+ // Maintain this list in coordination with the list of
+ // mitigation options documented in startSES.js.
+ // See https://code.google.com/p/google-caja/issues/detail?id=1893
mitigateOpts = {
- parseProgram : true,
- rewriteTopLevelVars : false,
- rewriteTopLevelFuncs : false,
- rewriteTypeOf : false
+ parseFunctionBody: true,
+ rewriteTopLevelVars: false,
+ rewriteTopLevelFuncs: false,
+ rewriteFunctionCalls: false,
+ rewriteTypeOf: false
};
url = proxiedUrl;
} else {
=======================================
--- /branches/es53/src/com/google/caja/ses/compileExprLater.js Tue Feb 4
17:26:19 2014 UTC
+++ /branches/es53/src/com/google/caja/ses/compileExprLater.js Tue Apr 8
17:39:52 2014 UTC
@@ -15,10 +15,21 @@
/**
* @fileoverview Makes a "compileExprLater" function which acts like
* "cajaVM.compileExpr", except that it returns a promise for the
- * outcome of attempting to compile the argument expression.
+ * outcome of attempting to compile the argument expression. There are
+ * two reasons why one might use compileExprLater rather than just
+ * using compileExpr asynchronously:
+ * <ul>
+ * <li>On some browsers (including Safari 7.0.1, which is current at
+ * the time of this writing), code loaded via script tags are given
+ * more accurate source locations in stack traces than code loaded via
+ * eval. Script tags can generally only load code asynchronously.
+ * <li>Some loading scenarios require the code to be translated
+ * first. However, translators can be large, so we may sometimes want
+ * to load them asynchronously.
+ * </ul>
*
* //requires ses.okToLoad, ses.securableWrapperSrc,
ses.atLeastFreeVarNames,
- * //requires ses.makeCompiledExpr,
+ * //requires ses.makeCompiledExpr, ses.prepareExpr
* //provides ses.compileExprLater
* //provides ses.redeemResolver for its own use
* @author Mark S. Miller
@@ -47,11 +58,6 @@
* feature test on the global named "document". If it is absent,
* then we fall back to this implementation which works in any
* standard ES5 environment.
- *
- * <p>Eventually, we should check whether we're in an ES5/3
- * environment, in which case our choice for compileExprLater would
- * be one that sends the expression back up the server to be
- * cajoled.
*/
function compileExprLaterFallback(exprSrc, opt_mitigateOpts) {
// Coercing an object to a string may observably run code, so do
@@ -86,46 +92,28 @@
* Implements an eventual compileExpr using injected script tags
*/
function compileLaterInScript(exprSrc, opt_mitigateOpts) {
+ var prep = ses.prepareExpr(exprSrc, opt_mitigateOpts);
var result = Q.defer();
+ var resolverTicket = getResolverTicket(result.resolve);
- // The portion of the pattern in compileExpr which is appropriate
- // here as well.
- var options = ses.resolveOptions(opt_mitigateOpts);
- var wrapperSrc = ses.securableWrapperSrc(exprSrc);
- var freeNames = ses.atLeastFreeVarNames(exprSrc);
+ // Freenames consist solely of identifier characters (\w|\$)+
+ // which do not need to be escaped further
+ var freeNamesList = prep.freeNames.length == 0 ? '[]' :
+ '["' + prep.freeNames.join('", "') + '"]';
+
+ var scriptSrc =
+ 'ses.redeemResolver(' + resolverTicket + ')(' +
+ 'Object.freeze(ses.makeCompiledExpr(' +
+ prep.wrapperSrc + ',\n' +
+ freeNamesList + ', ' +
+ JSON.stringify(prep.options) +
+ '))' +
+ ');' + prep.suffixSrc;
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
head.insertBefore(script, head.lastChild);
-
- var resolverTicket = getResolverTicket(result.resolve);
-
- var scriptSrc = 'ses.redeemResolver(' + resolverTicket + ')(' +
- 'Object.freeze(ses.makeCompiledExpr(' + wrapperSrc + ',\n' +
- // Freenames consist solely of identifier characters (\w|\$)+
- // which do not need to be escaped further
- '["' + freeNames.join('", "') + '"], ' +
- JSON.stringify(options) + ')));';
-
- if (options.sourceUrl === void 0) {
- // See http://code.google.com/p/google-caja/wiki/SES#typeof_variable
- if (typeof global.URI !== 'undefined' && URI.parse) {
- var parsed = URI.parse(String(options.sourceUrl));
- parsed = null === parsed ? null : parsed.toString();
-
- // Note we could try to encode these characters or search
specifically
- // for */ as a pair of characters but since it is for debugging
only
- // choose to avoid
- if (null !== parsed &&
- parsed.indexOf("<") < 0 &&
- parsed.indexOf(">") < 0 &&
- parsed.indexOf("*") < 0) {
- scriptSrc = '/* from ' + parsed + ' */ ' + scriptSrc;
- }
- }
- }
-
// TODO(erights): It seems that on Chrome at least, the injected
// script actually executes synchronously *now*. Is this
// generally true? If so, perhaps we can even make synchronous
@@ -135,12 +123,9 @@
script.appendChild(document.createTextNode(scriptSrc));
function deleteScriptNode() { script.parentNode.removeChild(script); }
-
Q(result.promise).then(deleteScriptNode, deleteScriptNode).end();
-
return result.promise;
}
-
ses.compileExprLater = compileLaterInScript;
})(this);
=======================================
--- /branches/es53/src/com/google/caja/ses/debug.js Tue Feb 4 17:26:19
2014 UTC
+++ /branches/es53/src/com/google/caja/ses/debug.js Tue Apr 8 17:39:52
2014 UTC
@@ -132,6 +132,7 @@
*
http://code.google.com/p/causeway/source/browse/trunk/src/js/com/teleometry/causeway/purchase_example/workers/makeCausewayLogger.js
*/
function getCWStack(err) {
+ if (Object(err) !== err) { return void 0; }
var sst = ssts.get(err);
if (sst === void 0 && err instanceof Error) {
// We hope it triggers prepareStackTrace
@@ -152,49 +153,127 @@
ses.getCWStack = getCWStack;
})();
- } else if (global.opera) {
+ } else {
(function() {
- // Since pre-ES5 browsers are disqualified, we can assume a
- // minimum of Opera 11.60.
- })();
+ // Each of these patterns should have the first capture group
+ // be the function name, and the second capture group be the
+ // source URL together with position information. Afterwards,
+ // the lineColPattern will pull apart these source position
+ // components. On all, we assume the function name, if any, has
+ // no colon (":"), at-sign ("@"), or open paren ("("), as each
+ // of these are used to recognize other parts of a debug line.
+ // Seen on FF: The function name is sometimes followed by
+ // argument descriptions enclosed in parens, which we
+ // ignore. Then there is always an at-sign followed by possibly
+ // empty source position.
+ var FFFramePattern = /^\s*([^:@(]*?)\s*(?:\(.*\))?@(.*?)$/;
+ // Seen on IE: The line begins with " at ", as on v8, which we
+ // ignore. Then the function name, then the source position
+ // enclosed in parens.
+ var IEFramePattern = /^\s*(?:at\s+)?([^:@(]*?)\s*\((.*?)\)$/;
+ // Seem on Safari (JSC): The name optionally followed by an
+ // at-sign and source position information. This is like FF,
+ // except that the at-sign and source position info may
+ // together be absent.
+ var JSCFramePatt1 = /^\s*([^:@(]*?)\s*(?:@(.*?))?$/;
+ // Also seen on Safari (JSC): Just the source position info by
+ // itself, with no preceding function name. The source position
+ // always seems to contain at least a colon, which is how we
+ // decide that it is a source position rather than a function
+ // name. The pattern here is a bit more flexible, in that it
+ // will accept a function name preceding the source position
+ // and separated by whitespace.
+ var JSCFramePatt2 = /^\s*?([^:@(]*?)\s*?(.*?)$/;
- } else if (new Error().stack) {
- (function() {
- var FFFramePattern = (/^([^@]*)@(.*?):?(\d*)$/);
+ // List the above patterns in priority order, where the first
+ // matching pattern is the one used for any one stack line.
+ var framePatterns = [FFFramePattern, IEFramePattern,
+ JSCFramePatt1, JSCFramePatt2];
- // stacktracejs.com suggests that this indicates FF. Really?
+ // Each of the LineColPatters should have the first capture
+ // group be the source URL if any, the second by the line
+ // number if any, and the third be the column number if any.
+
+ // Seen on FF Nightly 30 for execution in evaled strings
+ var FFEvalLineColPatterns = (/^(?:.*?) line \d+ >
eval():(\d+):(\d+)$/);
+ // If the source position ends in either one or two
+ // colon-digit-sequence suffixes, then the first of these are
+ // the line number, and the second, if present, is the column
+ // number.
+ var MainLineColPattern = /^(.*?)(?::(\d+)(?::(\d+))?)?$/;
+
+ // List the above patterns in priority order, where the first
+ // matching pattern is the one used for any one stack line.
+ var lineColPatterns = [FFEvalLineColPatterns, MainLineColPattern];
+
function getCWStack(err) {
+ if (!(err instanceof Error)) { return void 0; }
var stack = err.stack;
if (!stack) { return void 0; }
var lines = stack.split('\n');
+ if (/^\w*Error:/.test(lines[0])) {
+ lines = lines.slice(1);
+ }
var frames = lines.map(function(line) {
- var match = FFFramePattern.exec(line);
- if (match) {
- return {
- name: match[1].trim() || '?',
- source: match[2].trim() || '?',
- span: [[+match[3]]]
- };
- } else {
- return {
- name: line.trim() || '?',
- source: '?',
- span: []
- };
+ var name = line.trim();
+ var source = '?';
+ var span = [];
+ // Using .some here only because it gives us a way to escape
+ // the loop early. We do not use the results of the .some.
+ framePatterns.some(function(framePattern) {
+ var match = framePattern.exec(line);
+ if (match) {
+ name = match[1] || '?';
+ source = match[2] || '?';
+ // Using .some here only because it gives us a way to escape
+ // the loop early. We do not use the results of the .some.
+ lineColPatterns.some(function(lineColPattern) {
+ var sub = lineColPattern.exec(source);
+ if (sub) {
+ // sub[1] if present is the source URL.
+ // sub[2] if present is the line number.
+ // sub[3] if present is the column number.
+ source = sub[1] || '?';
+ if (sub[2]) {
+ if (sub[3]) {
+ span = [[+sub[2], +sub[3]]];
+ } else {
+ span = [[+sub[2]]];
+ }
+ }
+ return true;
+ }
+ return false;
+ });
+ return true;
+ }
+ return false;
+ });
+ if (name === 'Anonymous function') {
+ // Adjust for weirdness seen on IE
+ name = '?';
+ } else if (name.indexOf('/') !== -1) {
+ // Adjust for function name weirdness seen on FF.
+ name = name.replace(/[/<]/g,'');
+ var parts = name.split('/');
+ name = parts[parts.length -1];
}
+ if (source === 'Unknown script code' || source === 'eval code')
{
+ // Adjust for weirdness seen on IE
+ source = '?';
+ }
+ return {
+ name: name,
+ source: source,
+ span: span
+ };
});
return { calls: frames };
}
ses.getCWStack = getCWStack;
})();
-
- } else {
- (function() {
- // Including Safari and IE10.
- })();
-
}
/**
@@ -224,9 +303,21 @@
function getStack(err) {
if (err !== Object(err)) { return void 0; }
var cwStack = ses.getCWStack(err);
- if (!cwStack) { return void 0; }
- var result = ses.stackString(cwStack);
- if (err instanceof Error) { result = err + '\n' + result; }
+ var result;
+ if (cwStack) {
+ result = ses.stackString(cwStack);
+ } else {
+ if (err instanceof Error &&
+ 'stack' in err &&
+ typeof (result = err.stack) === 'string') {
+ // already in result
+ } else {
+ return void 0;
+ }
+ }
+ if (err instanceof Error) {
+ result = err + '\n' + result;
+ }
return result;
};
ses.getStack = getStack;
=======================================
--- /branches/es53/src/com/google/caja/ses/explicit.html Tue Feb 4
17:26:19 2014 UTC
+++ /branches/es53/src/com/google/caja/ses/explicit.html Tue Apr 8
17:39:52 2014 UTC
@@ -18,7 +18,7 @@
<html>
<head>
-<title>Testing SES/5</title>
+<title>Testing SES</title>
<style type="text/css">
.log { color: black; }
.info { color: navy; }
@@ -153,39 +153,11 @@
})();
</script>
-<script>
- (function() {
- "use strict";
- if (!ses.ok()) {
- return;
- }
-
- var src =
- 'function foo() { throw Error("Expand me to see stack"); }\n' +
- 'function foo2() { foo(); }\n' +
- 'function foo3() { foo2(); }\n' +
- 'foo3();\n';
-
- try {
- cajaVM.eval(src);
- } catch (err) {
- // The ses.logger installed by useHTMLLogger.js uses ses.getStack
- // to display the stack, if any, associated with the err argument.
- ses.logger.info('Expected error to test ses.getStack API: ', err);
- return;
- }
- ses.logger.error('Missing expected error');
- }());
-</script>
-
<script src="makeQ.js"></script>
-<script src="makeFarResourceMaker.js"></script>
-<script src="compileExprLater.js"></script>
<script>
var Q;
(function() {
"use strict";
-
if (!ses.ok()) { return; }
/* TODO(erights): Extract the reusable boilerplate below into a
@@ -202,6 +174,63 @@
}, millis);
}
Q = ses.makeQ(mySetTimeout);
+ })();
+</script>
+
+<script src="compileExprLater.js"></script>
+
+<script>
+ (function() {
+ "use strict";
+ if (!ses.ok()) { return; }
+
+ var exprSrc =
+ '(function(){\n' +
+ ' function foo(str) {\n' +
+ ' debugger;\n' +
+ ' throw Error(str);\n' +
+ ' }\n' +
+ ' function foo2(g) { g("Expand me to see stack"); }\n' +
+ ' function foo3(f) { f(foo); }\n' +
+ ' foo3(foo2);\n' +
+ '}())';
+
+ syncEval: {
+ try {
+ cajaVM.confine(exprSrc, {}, {
+ sourceUrl: 'http://example.com/fake1.js'
+ });
+ } catch (err) {
+ // The ses.logger installed by useHTMLLogger.js uses ses.getStack
+ // to display the stack, if any, associated with the err argument.
+ ses.logger.info('Expected error to test ses.getStack API: ', err);
+ break syncEval;
+ }
+ ses.logger.error('Missing expected error');
+ }
+
+ var compiledP = ses.compileExprLater(exprSrc, {
+ sourceUrl: 'http://example.com/fake2.js'
+ });
+ // TODO(erights): Should be 'done' once makeQ supports it.
+ Q(compiledP).fcall(cajaVM.sharedImports).then(
+ function(val) {
+ ses.logger.error('Missing expected error: ', val);
+ },
+ function(reason) {
+ // The ses.logger installed by useHTMLLogger.js uses ses.getStack
+ // to display the stack, if any, associated with the err argument.
+ ses.logger.info('Testing ses.getStack with compileExprLater: ',
reason);
+ });
+ }());
+</script>
+
+<script src="makeFarResourceMaker.js"></script>
+
+<script>
+ (function() {
+ "use strict";
+ if (!ses.ok()) { return; }
var makeTextResource = ses.makeFarResourceMaker();
@@ -223,7 +252,9 @@
var makeSimpleAMDLoaderP =
Q(makeSimpleAMDLoaderSrcP).then(function(src) {
var exprSrc = '(function() {' + src + '}).call(this)';
- var compiledExprP = ses.compileExprLater(exprSrc, void 0, url);
+ var compiledExprP = ses.compileExprLater(exprSrc, {
+ sourceUrl: url
+ });
return Q(compiledExprP).then(function(compiledExpr) {
compiledExpr(imports);
return imports.makeSimpleAMDLoader;
=======================================
--- /branches/es53/src/com/google/caja/ses/makeSimpleAMDLoader.js Sat Jul
13 15:35:24 2013 UTC
+++ /branches/es53/src/com/google/caja/ses/makeSimpleAMDLoader.js Tue Apr
8 17:39:52 2014 UTC
@@ -125,7 +125,10 @@
cajaVM.def(imports);
var compiledExprP = compileExprLater(
- '(function(){' + src + '})()', { sourceUrl: id });
+ '(function(){' + src + '\n}())',
+ // TODO(erights): Should expand fetch interface to obtain
+ // from fetch the url it expanded the id to.
+ { sourceUrl: id + '.js' });
return Q(compiledExprP).then(function(compiledExpr) {
compiledExpr(imports);
=======================================
--- /branches/es53/src/com/google/caja/ses/repair-framework.js Wed Feb 12
18:09:06 2014 UTC
+++ /branches/es53/src/com/google/caja/ses/repair-framework.js Tue Apr 8
17:39:52 2014 UTC
@@ -691,7 +691,7 @@
*
* <p>[TODO(kpreid): Rewrite the rest of this comment to better
* discuss repair-framework vs repairES5.]
- *
+ *
* <p>Although <code>repairES5.js</code> can be used standalone for
* partial ES5 repairs, its primary purpose is to repair as a first
* stage of <code>initSES.js</code> for purposes of supporting SES
=======================================
--- /branches/es53/src/com/google/caja/ses/startSES.js Tue Feb 4 17:26:19
2014 UTC
+++ /branches/es53/src/com/google/caja/ses/startSES.js Tue Apr 8 17:39:52
2014 UTC
@@ -22,7 +22,7 @@
* //requires ses.verifyStrictFunctionBody
* //optionally requires ses.mitigateSrcGotchas
* //provides ses.startSES ses.resolveOptions, ses.securableWrapperSrc
- * //provides ses.makeCompiledExpr
+ * //provides ses.makeCompiledExpr ses.prepareExpr
*
* @author Mark S. Miller,
* @author Jasvir Nagra
@@ -287,18 +287,10 @@
* mitigate. Passing no {@code opt_mitigateOpts} performs all the
* default mitigations. Returns a well behaved options record.
*
- * <p>See {@code compileExpr} for documentation of the mitigation
+ * <p>See {@code prepareExpr} for documentation of the mitigation
* options and their effects.
*/
function resolveOptions(opt_mitigateOpts) {
- if (typeof opt_mitigateOpts === 'string') {
- // TODO: transient deprecated adaptor only, since there used to
- // be an opt_sourceUrl parameter in many of the parameter
- // positions now accepting an opt_mitigateOpts. Once we are
- // confident that we no longer have live clients that count on
- // the old behavior, remove this kludge.
- opt_mitigateOpts = { sourceUrl: opt_mitigateOpts };
- }
function resolve(opt, defaultOption) {
return (opt_mitigateOpts && opt in opt_mitigateOpts) ?
opt_mitigateOpts[opt] : defaultOption;
@@ -339,7 +331,7 @@
* {@code options} are assumed to already be canonicalized by {@code
* resolveOptions} and says which mitigations to apply.
*/
- function mitigateSrcGotchas(funcBodySrc, options) {
+ function mitigateIfPossible(funcBodySrc, options) {
var safeError;
if ('function' === typeof ses.mitigateSrcGotchas) {
if (INCREMENT_IGNORES_FROZEN) {
@@ -756,6 +748,19 @@
}
ses.securableWrapperSrc = securableWrapperSrc;
+ /**
+ * See <a
href="http://www.ecma-international.org/ecma-262/5.1/#sec-7.3"
+ * >EcmaScript 5 Line Terminators</a>
+ */
+ var hasLineTerminator = /[\u000A\u000D\u2028\u2029]/;
+
+ function verifyOnOneLine(text) {
+ text = ''+text;
+ if (hasLineTerminator.test(text)) {
+ throw new TypeError("Unexpected line terminator: " + text);
+ }
+ return text;
+ }
/**
* Given a wrapper function, such as the result of evaluating the
@@ -780,6 +785,10 @@
}
ses.makeCompiledExpr = makeCompiledExpr;
+ // Maintain the list of mitigation options documented below in
+ // coordination with the list of mitigation options in
+ // html-emitter.js's evaluateUntrustedExternalScript.
+ // See https://code.google.com/p/google-caja/issues/detail?id=1893
/**
* Compiles {@code exprSrc} as a strict expression into a function
* of an {@code imports}, that when called evaluates {@code
@@ -832,13 +841,13 @@
* {@code with} together with RegExp matching to intercept free
* variable access without parsing.
*/
- function compileExpr(exprSrc, opt_mitigateOpts) {
+ function prepareExpr(exprSrc, opt_mitigateOpts) {
// Force exprSrc to be a string that can only parse (if at all) as
// an expression.
exprSrc = '(' + exprSrc + '\n)';
var options = resolveOptions(opt_mitigateOpts);
- exprSrc = mitigateSrcGotchas(exprSrc, options);
+ exprSrc = mitigateIfPossible(exprSrc, options);
// This is a workaround for a bug in the escodegen renderer that
// renders expressions as expression statements
@@ -846,9 +855,51 @@
exprSrc = exprSrc.substr(0, exprSrc.length - 1);
}
var wrapperSrc = securableWrapperSrc(exprSrc);
- var wrapper = unsafeEval(wrapperSrc);
var freeNames = atLeastFreeVarNames(exprSrc);
- var result = makeCompiledExpr(wrapper, freeNames, options);
+
+ var suffixSrc;
+ var sourceUrl = options.sourceUrl;
+ if (sourceUrl) {
+ sourceUrl = verifyOnOneLine(sourceUrl);
+ // Placing the sourceURL inside a line comment at the end of
+ // the evaled string, in this format, has emerged as a de
+ // facto convention for associating the source info with this
+ // evaluation. See
+ //
http://updates.html5rocks.com/2013/06/sourceMappingURL-and-sourceURL-syntax-changed
+ //
http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+ //
https://developers.google.com/chrome-developer-tools/docs/javascript-debugging#breakpoints-dynamic-javascript
+
+ // TODO(erights): Should validate that the sourceURL is a
+ // valid URL of a whitelisted protocol, where that whitelist
+ // does not include "javascript:". Not doing so at this time
+ // does not itself introduce a security vulnerability, as long
+ // as the sourceURL is all on one line, since the text only
+ // appears in a JavaScript line comment. Separate hazards may
+ // appear when the alleged URL reappears in a stack trace, but
+ // it is the responsibility of that code to handle those URLs
+ // safely.
+ suffixSrc = '\n//# sourceURL=' + sourceUrl + '\n';
+ } else {
+ suffixSrc = '';
+ }
+
+ return def({
+ options: options,
+ wrapperSrc: wrapperSrc,
+ suffixSrc: suffixSrc,
+ freeNames: freeNames
+ });
+ }
+ ses.prepareExpr = prepareExpr;
+
+ /**
+ *
+ */
+ function compileExpr(exprSrc, opt_mitigateOpts) {
+ var prep = prepareExpr(exprSrc, opt_mitigateOpts);
+
+ var wrapper = unsafeEval(prep.wrapperSrc + prep.suffixSrc);
+ var result = makeCompiledExpr(wrapper, prep.freeNames, prep.options);
return freeze(result);
}
@@ -916,7 +967,7 @@
* any valid Program.
*
* For documentation on {@code opt_mitigateOpts} see the
- * corresponding parameter in compileExpr.
+ * corresponding parameter in {@code prepareExpr}.
*
* <p>In addition, in case the module source happens to begin with
* a streotyped prelude of the CommonJS module system, the
@@ -951,7 +1002,7 @@
// modSrc from eliding the rest of the wrapper.
var exprSrc =
'(function() {' +
- mitigateSrcGotchas(modSrc, options) +
+ mitigateIfPossible(modSrc, options) +
'\n}).call(this)';
// Follow the pattern in compileExpr
var wrapperSrc = securableWrapperSrc(exprSrc);
=======================================
--- /branches/es53/src/com/google/caja/ses/useHTMLLogger.js Tue Feb 4
17:26:19 2014 UTC
+++ /branches/es53/src/com/google/caja/ses/useHTMLLogger.js Tue Apr 8
17:39:52 2014 UTC
@@ -111,6 +111,47 @@
}, false);
toggler.style.cursor = 'pointer';
}
+
+ /**
+ * Turn a Causeway stack into a DOM rendering of a v8-like stack
+ * traceback string. Based on ses.stackString in debug.js
+ */
+ function stackDom(preParent, cwStack) {
+ var calls = cwStack.calls;
+
+ calls.forEach(function(call) {
+ var spanString = call.span.map(function(subSpan) {
+ return subSpan.join(':');
+ }).join('::');
+
+ if (spanString) { spanString = ':' + spanString; }
+
+ appendText(preParent, ' at ' + call.name + ' (');
+ var url = call.source;
+ var urlText = call.source;
+
+ if (/^(?:http|https|file):\/\//.test(url)) {
+ var googlecodeRX =
(/^http:\/\/([^.]+)\.googlecode.com\/svn\/(.*)$/);
+ var urlGroups = googlecodeRX.exec(call.source);
+ if (urlGroups) {
+ url = 'https://code.google.com/p/' + urlGroups[1] +
+ '/source/browse/' + urlGroups[2];
+ var spanGroups = (/^:([0-9]+)(.*)$/).exec(spanString);
+ if (spanGroups) {
+ url += '#' + spanGroups[1];
+ urlText += ':' + spanGroups[1];
+ spanString = spanGroups[2];
+ }
+ }
+ var link = appendNew(preParent, 'a');
+ link.href = url;
+ link.textContent = urlText;
+ } else {
+ appendText(preParent, urlText);
+ }
+ appendText(preParent, spanString + ')\n');
+ });
+ };
/** modeled on textAdder */
function makeLogFunc(parent, style) {
@@ -119,27 +160,23 @@
var args = slice.call(arguments, 0);
// See debug.js
+ var getCWStack = ses.getCWStack;
var getStack = ses.getStack;
for (var i = 0, len = args.length; i < len; i++) {
- // The 'p' below used to be 'span', which is better use of HTML.
- // However, on Chrome Version 27.0.1428.0 canary we are seeing
- // a strange bug (TODO(erights) we have yet to isolate and
- // report) where the resulting HTMLSpanElement does not
- // inherit from the right prototype, and therefore fails to
- // implement appendChild. This may be related to
- // https://code.google.com/p/v8/issues/detail?id=2565
- // since it occurs after Object.prototype is frozen.
- var span = appendNew(p, 'p');
+ var span = appendNew(p, 'span');
appendText(span, '' + args[i]);
- if (getStack) {
- var stack = getStack(args[i]);
- if (stack) {
- var stackNode = appendNew(p, 'pre');
- appendText(stackNode, stack);
- deflate(span, [stackNode], '');
- }
+ var cwStack;
+ var stack;
+ if (getCWStack && ((cwStack = getCWStack(args[i])))) {
+ var stackNode = appendNew(p, 'pre');
+ stackDom(stackNode, cwStack);
+ deflate(span, [stackNode], '');
+ } else if (getStack && ((stack = getStack(args[i])))) {
+ var stackNode = appendNew(p, 'pre');
+ appendText(stackNode, stack);
+ deflate(span, [stackNode], '');
}
}
p.className = style;
=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-domado-dom-guest.html
Thu Aug 22 21:49:41 2013 UTC
+++ /branches/es53/tests/com/google/caja/plugin/test-domado-dom-guest.html
Tue Apr 8 17:39:52 2014 UTC
@@ -876,6 +876,7 @@
<div id="testQuerySelector" class="testcontainer">
<p class="testQuerySelector-ac" id="testQuerySelector-ai">a</p>
<p class="testQuerySelector-bc" id="testQuerySelector-bi">b</p>
+ <p testQuerySelector-attr="foo">c</p>
</div>
<script type="text/javascript">
// TODO(kpreid): Disable this test if host browser does not provide
@@ -901,14 +902,18 @@
assertEquals(selectors + ' (querySelector)',
len === 0 ? null : nodeList[0], single);
}
+
+ var paragraphCount = 3;
assertQueryResult('.testQuerySelector-ac', ['a']);
assertQueryResult('.testQuerySelector-bc', ['b']);
assertQueryResult('#testQuerySelector-ai', ['a']);
assertQueryResult('#testQuerySelector-ai, #testQuerySelector-bi',
['a', 'b']);
- assertQueryResult('#testQuerySelector p', ['a', 'b']);
- assertEquals('scoped query', 2,
container.querySelectorAll('p').length);
+ assertQueryResult('#testQuerySelector p', ['a', 'b', 'c']);
+ assertQueryResult('[testQuerySelector-attr]', ['c']);
+ assertEquals('scoped query', paragraphCount,
+ container.querySelectorAll('p').length);
expectFailure(function() {
document.querySelector(':unrecognized');
}, 'syntax error solo', function(e) {
@@ -929,9 +934,9 @@
// test non-liveness
var list = container.querySelectorAll('p');
- assertEquals('length before change', 2, list.length);
+ assertEquals('length before change', paragraphCount, list.length);
container.appendChild(document.createElement('p')).textContent
= 'added';
- assertEquals('length after change', 2, list.length);
+ assertEquals('length after change', paragraphCount, list.length);
pass('testQuerySelector');
});
--
---
You received this message because you are subscribed to the Google Groups "Google Caja Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.