Search in sources :

Example 16 with SourceLocation

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;
}
Also used : TemplateNode(com.google.template.soy.soytree.TemplateNode) SourceLocation(com.google.template.soy.base.SourceLocation) SoyFileNode(com.google.template.soy.soytree.SoyFileNode) HashSet(java.util.HashSet)

Example 17 with SourceLocation

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));
        }
    }
}
Also used : TemplateNode(com.google.template.soy.soytree.TemplateNode) SourceLocation(com.google.template.soy.base.SourceLocation) VarRefNode(com.google.template.soy.exprtree.VarRefNode) TemplateParam(com.google.template.soy.soytree.defn.TemplateParam) HtmlOpenTagNode(com.google.template.soy.soytree.HtmlOpenTagNode)

Example 18 with SourceLocation

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;
    }
}
Also used : SourceLocation(com.google.template.soy.base.SourceLocation)

Example 19 with SourceLocation

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));
}
Also used : SourceLocation(com.google.template.soy.base.SourceLocation) Point(com.google.template.soy.base.SourceLocation.Point) Reason(com.google.template.soy.soytree.RawTextNode.SourceOffsets.Reason)

Example 20 with SourceLocation

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);
}
Also used : SourceLocation(com.google.template.soy.base.SourceLocation)

Aggregations

SourceLocation (com.google.template.soy.base.SourceLocation)23 Test (org.junit.Test)8 VarRefNode (com.google.template.soy.exprtree.VarRefNode)5 TemplateNode (com.google.template.soy.soytree.TemplateNode)4 IntegerNode (com.google.template.soy.exprtree.IntegerNode)3 Matcher (java.util.regex.Matcher)3 CharMatcher (com.google.common.base.CharMatcher)2 ExprNode (com.google.template.soy.exprtree.ExprNode)2 FunctionNode (com.google.template.soy.exprtree.FunctionNode)2 NullNode (com.google.template.soy.exprtree.NullNode)2 HtmlOpenTagNode (com.google.template.soy.soytree.HtmlOpenTagNode)2 PrintNode (com.google.template.soy.soytree.PrintNode)2 RawTextNode (com.google.template.soy.soytree.RawTextNode)2 TemplateParam (com.google.template.soy.soytree.defn.TemplateParam)2 ArrayList (java.util.ArrayList)2 HashSet (java.util.HashSet)2 ImmutableSet (com.google.common.collect.ImmutableSet)1 Point (com.google.template.soy.base.SourceLocation.Point)1 Identifier (com.google.template.soy.base.internal.Identifier)1 SanitizedContentKind (com.google.template.soy.base.internal.SanitizedContentKind)1