SLING-6008 - The display context cannot be overwritten for HTML attribute expressions
* allowed display context overrides for attributes by checking if the expression was already
transformed into an XSS RuntimeCall
* added extra test based on the TCK that checks the encoding of an attribute value
generated by a Sightly expression
* updated TCK
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1757548 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/html/dom/MarkupHandler.java b/src/main/java/org/apache/sling/scripting/sightly/impl/html/dom/MarkupHandler.java
index 09e765f..11e83e6 100644
--- a/src/main/java/org/apache/sling/scripting/sightly/impl/html/dom/MarkupHandler.java
+++ b/src/main/java/org/apache/sling/scripting/sightly/impl/html/dom/MarkupHandler.java
@@ -24,6 +24,7 @@
import java.util.Stack;
import org.apache.commons.lang.StringUtils;
+import org.apache.sling.scripting.sightly.compiler.RuntimeFunction;
import org.apache.sling.scripting.sightly.compiler.commands.Conditional;
import org.apache.sling.scripting.sightly.compiler.commands.OutText;
import org.apache.sling.scripting.sightly.compiler.commands.OutputVariable;
@@ -34,6 +35,7 @@
import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperator;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.BooleanConstant;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.Identifier;
+import org.apache.sling.scripting.sightly.compiler.expression.nodes.RuntimeCall;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.StringConstant;
import org.apache.sling.scripting.sightly.impl.compiler.Patterns;
import org.apache.sling.scripting.sightly.impl.compiler.PushStream;
@@ -203,36 +205,64 @@
String isTrueVar = symbolGenerator.next("isTrueAttr"); // holds the comparison (attrValue == true)
String shouldDisplayAttr = symbolGenerator.next("shouldDisplayAttr");
MarkupContext markupContext = getAttributeMarkupContext(name);
- Expression contentExpression = valueExpression.withNode(new Identifier(attrValue));
+ boolean alreadyEscaped = false;
+ if (valueExpression.getRoot() instanceof RuntimeCall) {
+ RuntimeCall rc = (RuntimeCall) valueExpression.getRoot();
+ if (RuntimeFunction.XSS.equals(rc.getFunctionName())) {
+ alreadyEscaped = true;
+ }
+ }
ExpressionNode node = valueExpression.getRoot();
stream.write(new VariableBinding.Start(attrValue, node)); //attrContent = <expr>
- stream.write(new VariableBinding.Start(attrContent, expressionWrapper.adjustToContext(contentExpression, markupContext,
- ExpressionContext.ATTRIBUTE).getRoot()));
- stream.write(
- new VariableBinding.Start(
- shouldDisplayAttr,
- new BinaryOperation(
- BinaryOperator.OR,
- new Identifier(attrContent),
- new BinaryOperation(BinaryOperator.EQ, new StringConstant("false"), new Identifier(attrValue))
- )
- )
- );
+ if (!alreadyEscaped) {
+ Expression contentExpression = valueExpression.withNode(new Identifier(attrValue));
+ stream.write(new VariableBinding.Start(attrContent, adjustContext(compilerContext, contentExpression, markupContext,
+ ExpressionContext.ATTRIBUTE).getRoot()));
+ stream.write(
+ new VariableBinding.Start(
+ shouldDisplayAttr,
+ new BinaryOperation(
+ BinaryOperator.OR,
+ new Identifier(attrContent),
+ new BinaryOperation(BinaryOperator.EQ, new StringConstant("false"), new Identifier(attrValue))
+ )
+ )
+ );
+
+ } else {
+ stream.write(
+ new VariableBinding.Start(
+ shouldDisplayAttr,
+ new BinaryOperation(
+ BinaryOperator.OR,
+ new Identifier(attrValue),
+ new BinaryOperation(BinaryOperator.EQ, new StringConstant("false"), new Identifier(attrValue))
+ )
+ )
+ );
+ }
stream.write(new Conditional.Start(shouldDisplayAttr, true)); // if (attrContent)
+
emitAttributeStart(name); //write("attrName");
invoke.beforeAttributeValue(stream, name, node);
stream.write(new VariableBinding.Start(isTrueVar, //isTrueAttr = (attrValue == true)
new BinaryOperation(BinaryOperator.EQ, new Identifier(attrValue), BooleanConstant.TRUE)));
stream.write(new Conditional.Start(isTrueVar, false)); //if (!isTrueAttr)
emitAttributeValueStart(quoteChar); // write("='");
- stream.write(new OutputVariable(attrContent)); //write(attrContent)
+ if (!alreadyEscaped) {
+ stream.write(new OutputVariable(attrContent)); //write(attrContent)
+ } else {
+ stream.write(new OutputVariable(attrValue)); // write(attrValue)
+ }
emitAttributeEnd(quoteChar); //write("'");
stream.write(Conditional.END); //end if isTrueAttr
stream.write(VariableBinding.END); //end scope for isTrueAttr
invoke.afterAttributeValue(stream, name);
stream.write(Conditional.END); //end if attrContent
- stream.write(VariableBinding.END);
stream.write(VariableBinding.END); //end scope for attrContent
+ if (!alreadyEscaped) {
+ stream.write(VariableBinding.END);
+ }
stream.write(VariableBinding.END); //end scope for attrValue
}
@@ -427,4 +457,16 @@
return parentElementName != null &&
("script".equals(parentElementName) || "style".equals(parentElementName));
}
+
+ private Expression adjustContext(CompilerContext compilerContext, Expression expression, MarkupContext markupContext,
+ ExpressionContext expressionContext) {
+ ExpressionNode root = expression.getRoot();
+ if (root instanceof RuntimeCall) {
+ RuntimeCall runtimeCall = (RuntimeCall) root;
+ if (runtimeCall.getFunctionName().equals(RuntimeFunction.XSS)) {
+ return expression;
+ }
+ }
+ return compilerContext.adjustToContext(expression, markupContext, expressionContext);
+ }
}