/*
 * Copyright 2009 Google Inc.
 *
 * Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.template.soy.passes;

import static com.google.template.soy.parsepasses.contextautoesc.ContentSecurityPolicyPass.CSP_NONCE_VARIABLE_NAME;

import com.google.template.soy.basetree.Node;
import com.google.template.soy.basetree.NodeVisitor;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
import com.google.template.soy.exprtree.VarDefn;
import com.google.template.soy.exprtree.VarDefn.Kind;
import com.google.template.soy.exprtree.VarRefNode;
import com.google.template.soy.soytree.SoyNode;
import com.google.template.soy.soytree.SoytreeUtils;
import com.google.template.soy.soytree.TemplateNode;
import com.google.template.soy.soytree.defn.TemplateParam;

/**
 * A visitor that checks for certain misuses of template parameters:
 *
 * <ul>
 *     <li>There should be no explicit use of injected parameters called {@code csp_nonce}.  These
 *     should all be inserted by the soy compiler as part of the content security pass.
 * </ul>
 *
 * <p>TODO(lukes): add a check for unresolved globals that match params.
 */
final class CheckInvalidParamsVisitor {
  private static final SoyErrorKind IJ_CSP_NONCE_REFERENCE =
      SoyErrorKind.of(
          "Found a use of the injected parameter ''csp_nonce''. This parameter is reserved "
              + "by the Soy compiler for Content Security Policy support.");

  private final ErrorReporter errorReporter;

  CheckInvalidParamsVisitor(ErrorReporter errorReporter) {
    this.errorReporter = errorReporter;
  }

  void exec(SoyNode node) {
    // Search for injected params named 'csp_nonce'.  This includes:
    // * @inject params
    // * $ij references
    SoytreeUtils.visitAllNodes(
        node,
        new NodeVisitor<Node, Boolean>() {
          @Override
          public Boolean exec(Node node) {
            if (node instanceof TemplateNode) {
              TemplateNode template = (TemplateNode) node;
              for (TemplateParam param : template.getAllParams()) {
                if (param.isInjected() && param.name().equals(CSP_NONCE_VARIABLE_NAME)) {
                  errorReporter.report(node.getSourceLocation(), IJ_CSP_NONCE_REFERENCE);
                }
              }
            }
            if (node instanceof VarRefNode) {
              VarDefn defn = ((VarRefNode) node).getDefnDecl();
              if (defn.kind() == Kind.IJ_PARAM && defn.name().equals(CSP_NONCE_VARIABLE_NAME)) {
                errorReporter.report(node.getSourceLocation(), IJ_CSP_NONCE_REFERENCE);
              }
            }
            return true; // keep going
          }
        });
  }
}
