use of com.google.template.soy.base.SourceLocation in project closure-templates by google.
the class ContextualAutoescaper method rewrite.
/**
* Rewrites the given Soy files so that dynamic output is properly escaped according to the
* context in which it appears.
*
* @param fileSet Modified in place.
* @return Extra templates which were derived from templates under fileSet and which must be
* compiled with fileSet to produce a correct output. See {@link DerivedTemplateUtils} for an
* explanation of these.
*/
public List<TemplateNode> rewrite(SoyFileSetNode fileSet, ErrorReporter errorReporter) {
// Defensively copy so our loops below hold.
List<SoyFileNode> files = ImmutableList.copyOf(fileSet.getChildren());
ImmutableListMultimap<String, TemplateNode> templatesByName = findTemplates(files);
// Inferences collects all the typing decisions we make, templates we derive, and escaping modes
// we choose.
Inferences inferences = new Inferences(fileSet.getNodeIdGenerator(), templatesByName);
Collection<TemplateNode> allTemplates = inferences.getAllTemplates();
TemplateCallGraph callGraph = new TemplateCallGraph(templatesByName);
// Generate a call graph, creating a dummy root that calls all non-private template in
// Context.PCDATA, and then type the minimal ancestor set needed to reach all contextual
// templates whether private or not.
// This should have the effect of being a NOP when there are no contextual templates, will type
// all contextual templates, and will not barf on private templates that might be declared
// autoescape="false" because they do funky things that are provably safe by human reason but
// not by this algorithm.
Collection<TemplateNode> thatRequireInference = Collections2.filter(allTemplates, REQUIRES_INFERENCE);
Set<TemplateNode> templateNodesToType = callGraph.callersOf(thatRequireInference);
templateNodesToType.addAll(thatRequireInference);
Set<SourceLocation> errorLocations = new HashSet<>();
for (TemplateNode templateNode : templateNodesToType) {
try {
// In strict mode, the author specifies the kind of SanitizedContent to produce, and thus
// the context in which to escape.
Context startContext = (templateNode.getContentKind() != null) ? Context.getStartContextForContentKind(templateNode.getContentKind()) : Context.HTML_PCDATA;
InferenceEngine.inferTemplateEndContext(templateNode, startContext, inferences, errorReporter);
} catch (SoyAutoescapeException e) {
reportError(errorReporter, errorLocations, e);
}
}
if (!errorLocations.isEmpty()) {
// Bail out early, since future passes won't succeed and may throw precondition errors.
return ImmutableList.<TemplateNode>of();
}
// Store inferences so that after processing, clients can access the output contexts for
// templates.
this.inferences = inferences;
runVisitorOnAllTemplatesIncludingNewOnes(inferences, new NonContextualTypedRenderUnitNodesVisitor(errorReporter));
// Now that we know we don't fail with exceptions, apply the changes to the given files.
List<TemplateNode> extraTemplates = new Rewriter(inferences, printDirectives).rewrite(fileSet);
runVisitorOnAllTemplatesIncludingNewOnes(inferences, new PerformDeprecatedNonContextualAutoescapeVisitor(fileSet.getNodeIdGenerator()));
return extraTemplates;
}
use of com.google.template.soy.base.SourceLocation in project closure-templates by google.
the class ContentSecurityPolicyNonceInjectionPass method run.
@Override
public void run(SoyFileNode file, IdGenerator nodeIdGen) {
// * $ij references
for (TemplateNode template : file.getChildren()) {
for (TemplateParam param : template.getAllParams()) {
if (param.isInjected() && param.name().equals(CSP_NONCE_VARIABLE_NAME)) {
errorReporter.report(param.nameLocation(), IJ_CSP_NONCE_REFERENCE);
}
}
}
for (VarRefNode var : SoyTreeUtils.getAllNodesOfType(file, VarRefNode.class)) {
// of isInjected
if (var.isDollarSignIjParameter() && var.getName().equals(CSP_NONCE_VARIABLE_NAME)) {
errorReporter.report(var.getSourceLocation(), IJ_CSP_NONCE_REFERENCE);
}
}
for (HtmlOpenTagNode openTag : SoyTreeUtils.getAllNodesOfType(file, HtmlOpenTagNode.class)) {
if (isTagNonceable(openTag)) {
// this should point to the character immediately before the '>' or '/>' at the end of the
// open tag
SourceLocation insertionLocation = openTag.getSourceLocation().getEndPoint().offset(0, openTag.isSelfClosing() ? -2 : -1).asLocation(openTag.getSourceLocation().getFilePath());
openTag.addChild(createCspInjection(insertionLocation, nodeIdGen));
}
}
}
use of com.google.template.soy.base.SourceLocation in project closure-templates by google.
the class HtmlTagEntry method matchOrError.
/**
* A helper method that compare two {@code HtmlTagEntry}s.
*/
public static boolean matchOrError(@Nullable HtmlTagEntry openTag, @Nullable HtmlTagEntry closeTag, ErrorReporter errorReporter) {
if (openTag == null && closeTag == null) {
return true;
}
if (openTag == null && closeTag != null) {
errorReporter.report(closeTag.getSourceLocation(), UNEXPECTED_CLOSE_TAG);
return false;
}
if (openTag != null && closeTag == null) {
errorReporter.report(openTag.getSourceLocation(), OPEN_TAG_NOT_CLOSED);
return false;
}
if (openTag.hasTagName() != closeTag.hasTagName()) {
return tryMatchCommonPrefix(openTag, closeTag, errorReporter);
}
// Now both tags should be either tag names or conditional branches.
if (openTag.hasTagName()) {
return matchOrError(openTag.getTagName(), closeTag.getTagName(), errorReporter);
} else {
List<ConditionalBranches.ConditionalBranch> openBranches = openTag.getBranches().getBranches();
List<ConditionalBranches.ConditionalBranch> closeBranches = closeTag.getBranches().getBranches();
// For the following cases, we report error at the source location for the first tag in
// the conditional branch.
SourceLocation location = closeTag.getSourceLocation();
if (openBranches.size() != closeBranches.size()) {
errorReporter.report(location, MISMATCH_TAG);
return false;
}
for (int i = 0; i < openBranches.size(); ++i) {
ConditionalBranches.ConditionalBranch openBranch = openBranches.get(i);
ConditionalBranches.ConditionalBranch closeBranch = closeBranches.get(i);
if (!openBranch.condition().equals(closeBranch.condition())) {
errorReporter.report(location, MISMATCH_TAG);
return false;
}
ArrayDeque<HtmlTagEntry> openStack = openBranch.deque();
ArrayDeque<HtmlTagEntry> closeQueue = closeBranch.deque();
if (!matchOrError(openStack, closeQueue, errorReporter)) {
return false;
}
}
return true;
}
}
use of com.google.template.soy.base.SourceLocation in project closure-templates by google.
the class RawTextNode method concat.
/**
* Concatenates the non-empty set of RawTextNodes, preserving source location information.
*/
public static RawTextNode concat(List<RawTextNode> nodes) {
checkArgument(!nodes.isEmpty());
if (nodes.size() == 1) {
return nodes.get(0);
}
int id = nodes.get(0).getId();
int numChars = 0;
int numOffsets = 0;
for (RawTextNode node : nodes) {
numChars += node.getRawText().length();
// offsets are most often null when SourceLocation.UNKNOWN is in use
if (numOffsets != -1 && node.offsets != null) {
// since the last index in the array refers to a pseudo end location, we drop it when
// joining the arrays
// we don't preserve offsets from empty nodes.
numOffsets += node.isEmpty() ? 0 : node.offsets.indexes.length - 1;
} else {
numOffsets = -1;
}
}
char[] chars = new char[numChars];
int charsIndex = 0;
int[] indexes = null;
int[] lines = null;
int[] columns = null;
Reason[] reasons = null;
// the current index into the offsets arrays
int offsetsIndex = 0;
if (numOffsets > 0) {
// +1 because we want to preserve the end location of the last node.
indexes = new int[numOffsets + 1];
lines = new int[numOffsets + 1];
columns = new int[numOffsets + 1];
reasons = new Reason[numOffsets + 1];
}
for (int i = 0; i < nodes.size(); i++) {
RawTextNode node = nodes.get(i);
String text = node.getRawText();
text.getChars(0, text.length(), chars, charsIndex);
if (indexes != null && !text.isEmpty()) {
// To concatenate we need to approximately just cat the arrays
// since the last slot in the array corresponds to a pseudo location (the end of the string)
// we should drop it when splicing.
// we also need to modify all the indexes in other to be offset by the length of this
// offset's string.
SourceOffsets offsets = node.offsets;
int amountToCopy = offsets.indexes.length;
if (i < nodes.size() - 1) {
amountToCopy--;
}
System.arraycopy(offsets.lines, 0, lines, offsetsIndex, amountToCopy);
System.arraycopy(offsets.columns, 0, columns, offsetsIndex, amountToCopy);
System.arraycopy(offsets.reasons, 0, reasons, offsetsIndex, amountToCopy);
// If the new begin reason is NONE, then use the old end reason.
if (offsets.reasons[0] == Reason.NONE && i > 0) {
Reason[] prevReasons = nodes.get(i - 1).offsets.reasons;
reasons[offsetsIndex] = prevReasons[prevReasons.length - 1];
}
// manually copy the indexes over so we can apply the current character index
for (int indexIndex = 0; indexIndex < amountToCopy; indexIndex++) {
indexes[indexIndex + offsetsIndex] = offsets.indexes[indexIndex] + charsIndex;
}
offsetsIndex += amountToCopy;
}
charsIndex += text.length();
}
String text = new String(chars);
SourceLocation location = nodes.get(0).getSourceLocation().extend(Iterables.getLast(nodes).getSourceLocation());
return new RawTextNode(id, text, location, indexes == null ? null : new SourceOffsets(indexes, lines, columns, reasons));
}
use of com.google.template.soy.base.SourceLocation in project closure-templates by google.
the class RawTextNode method substring.
/**
* Returns a new RawTextNode that represents the given {@link String#substring(int, int)} of this
* raw text node.
*
* <p>Unlike {@link String#substring(int, int)} the range must be non-empty
*
* @param newId the new node id to use
* @param start the start location
* @param end the end location
*/
public RawTextNode substring(int newId, int start, int end) {
checkArgument(start >= 0, "start (%s) should be greater than or equal to 0", start);
checkArgument(start < end, "start (%s) should be less that end (%s)", start, end);
checkArgument(end <= rawText.length(), "end (%s) should be less than or equal to the length (%s)", end, rawText.length());
if (start == 0 && end == rawText.length()) {
return this;
}
String newText = rawText.substring(start, end);
SourceOffsets newOffsets = null;
SourceLocation newLocation = getSourceLocation();
if (offsets != null) {
newOffsets = offsets.substring(start, end, rawText);
newLocation = newOffsets.getSourceLocation(getSourceLocation().getFilePath());
}
return new RawTextNode(newId, newText, newLocation, newOffsets);
}
Aggregations