All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.shapesecurity.shift.validator.Validator Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/*
 * Copyright 2014 Shape Security, 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.shapesecurity.shift.validator;

import com.shapesecurity.functional.data.Either;
import com.shapesecurity.functional.data.ImmutableList;
import com.shapesecurity.functional.data.Maybe;
import com.shapesecurity.shift.ast.CatchClause;
import com.shapesecurity.shift.ast.FunctionBody;
import com.shapesecurity.shift.ast.Identifier;
import com.shapesecurity.shift.ast.Script;
import com.shapesecurity.shift.ast.VariableDeclarator;
import com.shapesecurity.shift.ast.expression.AssignmentExpression;
import com.shapesecurity.shift.ast.expression.FunctionExpression;
import com.shapesecurity.shift.ast.expression.IdentifierExpression;
import com.shapesecurity.shift.ast.expression.LiteralNumericExpression;
import com.shapesecurity.shift.ast.expression.ObjectExpression;
import com.shapesecurity.shift.ast.expression.PrefixExpression;
import com.shapesecurity.shift.ast.expression.StaticMemberExpression;
import com.shapesecurity.shift.ast.operators.PrefixOperator;
import com.shapesecurity.shift.ast.property.Getter;
import com.shapesecurity.shift.ast.property.ObjectProperty;
import com.shapesecurity.shift.ast.property.PropertyName;
import com.shapesecurity.shift.ast.property.Setter;
import com.shapesecurity.shift.ast.statement.BreakStatement;
import com.shapesecurity.shift.ast.statement.ContinueStatement;
import com.shapesecurity.shift.ast.statement.DoWhileStatement;
import com.shapesecurity.shift.ast.statement.ForInStatement;
import com.shapesecurity.shift.ast.statement.ForStatement;
import com.shapesecurity.shift.ast.statement.FunctionDeclaration;
import com.shapesecurity.shift.ast.statement.LabeledStatement;
import com.shapesecurity.shift.ast.statement.ReturnStatement;
import com.shapesecurity.shift.ast.statement.SwitchStatement;
import com.shapesecurity.shift.ast.statement.SwitchStatementWithDefault;
import com.shapesecurity.shift.ast.statement.WhileStatement;
import com.shapesecurity.shift.ast.statement.WithStatement;
import com.shapesecurity.shift.path.Branch;
import com.shapesecurity.shift.utils.Utils;
import com.shapesecurity.shift.visitor.MonoidalReducer;

import java.util.HashSet;

import org.jetbrains.annotations.NotNull;

public class Validator extends MonoidalReducer {
  public Validator() {
    super(ValidationContext.MONOID);
  }

  public static ImmutableList validate(Script node) {
    return node.reduce(new Validator()).errors.toList();
  }

  @NotNull
  @Override
  public ValidationContext reduceBreakStatement(
      @NotNull BreakStatement node,
      @NotNull ImmutableList path,
      @NotNull Maybe label) {
    ValidationContext v = super.reduceBreakStatement(node, path, label);
    return node.label.maybe(
        v.addFreeBreakStatement(
            new ValidationError(
                node,
                "break must be nested within switch or iteration statement")), v::addFreeJumpTarget);
  }

  @NotNull
  @Override
  public ValidationContext reduceAssignmentExpression(
      @NotNull AssignmentExpression node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext binding,
      @NotNull ValidationContext expression) {
    ValidationContext v = super.reduceAssignmentExpression(node, path, binding, expression);
    if (node.binding instanceof IdentifierExpression) {
      v = v.checkRestricted(((IdentifierExpression) node.binding).identifier);
    }
    return v;
  }

  @NotNull
  @Override
  public ValidationContext reduceCatchClause(
      @NotNull CatchClause node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext binding,
      @NotNull ValidationContext body) {
    ValidationContext v = super.reduceCatchClause(node, path, binding, body);
    return v.checkRestricted(node.binding);
  }

  @NotNull
  @Override
  public ValidationContext reduceContinueStatement(
      @NotNull ContinueStatement node,
      @NotNull ImmutableList path,
      @NotNull Maybe label) {
    final ValidationContext v = super.reduceContinueStatement(node, path, label).addFreeContinueStatement(node);
    return node.label.maybe(v, v::addFreeJumpTarget);
  }

  @NotNull
  @Override
  public ValidationContext reduceDoWhileStatement(
      @NotNull DoWhileStatement node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext body,
      @NotNull ValidationContext test) {
    return super.reduceDoWhileStatement(node, path, body, test).clearFreeContinueStatements()
        .clearFreeBreakStatements();
  }

  @NotNull
  @Override
  public ValidationContext reduceFunctionDeclaration(
      @NotNull FunctionDeclaration node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext name,
      @NotNull ImmutableList params,
      @NotNull ValidationContext programBody) {
    ValidationContext v = super.reduceFunctionDeclaration(node, path, name, params, programBody).clearUsedLabelNames()
        .clearReturnStatements().clearUsedLabelNames();
    if (!Utils.areUniqueNames(node.parameters)) {
      v = v.addStrictError(new ValidationError(node, "FunctionDeclaration must have unique parameter names"));
    }
    v = node.parameters.foldLeft(ValidationContext::checkRestricted, v.checkRestricted(node.name));
    if (node.body.isStrict()) {
      v = v.invalidateStrictErrors();
    }
    return v;
  }

  @NotNull
  @Override
  public ValidationContext reduceFunctionExpression(
      @NotNull FunctionExpression node,
      @NotNull ImmutableList path,
      @NotNull Maybe name,
      @NotNull ImmutableList parameters,
      @NotNull ValidationContext programBody) {
    ValidationContext v = super.reduceFunctionExpression(node, path, name, parameters, programBody)
        .clearReturnStatements();
    if (!Utils.areUniqueNames(node.parameters)) {
      v = v.addStrictError(new ValidationError(node, "FunctionExpression parameter names must be unique"));
    }
    v = node.parameters.foldLeft(ValidationContext::checkRestricted, node.name.map(v::checkRestricted).orJust(v));
    if (node.body.isStrict()) {
      v = v.invalidateStrictErrors();
    }
    return v;
  }

  @NotNull
  @Override
  public ValidationContext reduceIdentifier(@NotNull Identifier node, @NotNull ImmutableList path) {
    ValidationContext v = new ValidationContext();
    if (!Utils.isValidIdentifierName(node.name)) {
      v = v.addError(new ValidationError(node, "Identifier `name` must be a valid IdentifierName"));
    }
    return v;
  }

  @NotNull
  @Override
  public ValidationContext reduceIdentifierExpression(
      @NotNull IdentifierExpression node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext identifier) {
    return super.reduceIdentifierExpression(node, path, identifier).checkReserved(node.identifier);
  }

  @NotNull
  @Override
  public ValidationContext reduceForInStatement(
      @NotNull ForInStatement node,
      @NotNull ImmutableList path,
      @NotNull Either left,
      @NotNull ValidationContext right,
      @NotNull ValidationContext body) {
    ValidationContext v = super.reduceForInStatement(node, path, left, right, body).clearFreeBreakStatements()
        .clearFreeContinueStatements();
    if (node.left.isLeft() && !node.left.left().just().declarators.tail().isEmpty()) {
      v = v.addError(
          new ValidationError(
              node.left.left().just(),
              "VariableDeclarationStatement in ForInVarStatement contains more than one VariableDeclarator"));
    }
    return v;
  }

  @NotNull
  @Override
  public ValidationContext reduceForStatement(
      @NotNull ForStatement node,
      @NotNull ImmutableList path,
      @NotNull Maybe> init,
      @NotNull Maybe test,
      @NotNull Maybe update,
      @NotNull ValidationContext body) {
    return super.reduceForStatement(node, path, init, test, update, body).clearFreeBreakStatements()
        .clearFreeContinueStatements();
  }

  @NotNull
  @Override
  public ValidationContext reduceLabeledStatement(
      @NotNull LabeledStatement node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext label,
      @NotNull ValidationContext body) {
    return super.reduceLabeledStatement(node, path, label, body).observeLabelName(node.label);
  }

  @NotNull
  @Override
  public ValidationContext reduceLiteralNumericExpression(
      @NotNull LiteralNumericExpression node,
      @NotNull ImmutableList path) {
    ValidationContext v = new ValidationContext();
    if (node.value < 0 || node.value == 0 && 1 / node.value < 0) {
      v = v.addError(new ValidationError(node, "Numeric Literal node must be non-negative"));
    } else if (Double.isNaN(node.value)) {
      v = v.addError(new ValidationError(node, "Numeric Literal node must not be NaN"));
    } else if (Double.isInfinite(node.value)) {
      v = v.addError(new ValidationError(node, "Numeric Literal node must be finite"));
    }
    return v;
  }

  @NotNull
  @Override
  public ValidationContext reduceObjectExpression(
      @NotNull final ObjectExpression node,
      @NotNull ImmutableList path,
      @NotNull ImmutableList properties) {
    ValidationContext v = super.reduceObjectExpression(node, path, properties);
    final HashSet setKeys = new HashSet<>();
    final HashSet getKeys = new HashSet<>();
    final HashSet dataKeys = new HashSet<>();
    for (ObjectProperty p : node.properties) {
      String key = p.name.value;
      switch (p.getKind()) {
      case InitProperty:
        if (dataKeys.contains(key)) {
          v = v.addStrictError(
              new ValidationError(
                  node,
                  "ObjectExpression must not have more that one data property with the same name"));
        }
        if (getKeys.contains(key)) {
          v = v.addError(
              new ValidationError(
                  node,
                  "ObjectExpression must not have data and getter properties with same name"));
        }
        if (setKeys.contains(key)) {
          v = v.addError(
              new ValidationError(
                  node,
                  "ObjectExpression must not have data and setter properties with same name"));
        }
        dataKeys.add(key);
        break;
      case GetterProperty:
        if (getKeys.contains(key)) {
          v = v.addError(
              new ValidationError(
                  node,
                  "ObjectExpression must not have multiple getters with the same name"));
        }
        if (dataKeys.contains(key)) {
          v = v.addError(
              new ValidationError(
                  node,
                  "ObjectExpression must not have data and getter properties with the same name"));
        }
        getKeys.add(key);
        break;
      case SetterProperty:
        if (setKeys.contains(key)) {
          v = v.addError(
              new ValidationError(
                  node,
                  "ObjectExpression must not have multiple setters with the same name"));
        }
        if (dataKeys.contains(key)) {
          v = v.addError(
              new ValidationError(
                  node,
                  "ObjectExpression must not have data and setter properties with the same name"));
        }
        setKeys.add(key);
        break;
      default:
        break;
      }
    }
    return v;
  }

  @NotNull
  @Override
  public ValidationContext reducePrefixExpression(
      @NotNull PrefixExpression node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext operand) {
    ValidationContext v = super.reducePrefixExpression(node, path, operand);
    if (node.operator == PrefixOperator.Delete && node.operand instanceof IdentifierExpression) {
      return v.addStrictError(
          new ValidationError(
              node,
              "`delete` with unqualified identifier not allowed in strict mode"));
    }
    return v;
  }

  @NotNull
  @Override
  public ValidationContext reduceScript(
      @NotNull Script node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext body) {
    return super.reduceScript(node, path, body).invalidateFreeReturnErrors();
  }

  @NotNull
  @Override
  public ValidationContext reduceFunctionBody(
      @NotNull FunctionBody node,
      @NotNull ImmutableList path,
      @NotNull ImmutableList directives,
      @NotNull ImmutableList statements) {
    ValidationContext v = super.reduceFunctionBody(node, path, directives, statements).checkFreeJumpTargets();
    if (node.isStrict()) {
      v = v.invalidateStrictErrors();
    }
    return v.invalidateFreeContinueAndBreakErrors();
  }

  @NotNull
  @Override
  public ValidationContext reduceReturnStatement(
      @NotNull ReturnStatement node,
      @NotNull ImmutableList path,
      @NotNull Maybe expression) {
    return super.reduceReturnStatement(node, path, expression).addFreeReturnStatement(node);
  }

  @NotNull
  @Override
  public ValidationContext reduceSetter(
      @NotNull Setter node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext name,
      @NotNull ValidationContext param,
      @NotNull ValidationContext body) {
    return super.reduceSetter(node, path, name, param, body).checkRestricted(node.parameter).clearReturnStatements();
  }

  @NotNull
  @Override
  public ValidationContext reduceGetter(@NotNull Getter node, @NotNull ImmutableList path,
                                        @NotNull ValidationContext name, @NotNull ValidationContext body) {
    return super.reduceGetter(node, path, name, body).clearReturnStatements();
  }

  @NotNull
  @Override
  public ValidationContext reduceSwitchStatement(
      @NotNull SwitchStatement node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext discriminant,
      @NotNull ImmutableList cases) {
    return super.reduceSwitchStatement(node, path, discriminant, cases).clearFreeBreakStatements();
  }

  @NotNull
  @Override
  public ValidationContext reduceSwitchStatementWithDefault(
      @NotNull SwitchStatementWithDefault node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext discriminant,
      @NotNull ImmutableList preDefaultCases,
      @NotNull ValidationContext defaultCase,
      @NotNull ImmutableList postDefaultCases) {
    return super.reduceSwitchStatementWithDefault(
        node,
        path,
        discriminant,
        preDefaultCases,
        defaultCase,
        postDefaultCases)
        .clearFreeBreakStatements();
  }

  @NotNull
  @Override
  public ValidationContext reduceVariableDeclarator(
      @NotNull VariableDeclarator node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext binding,
      @NotNull Maybe init) {
    return super.reduceVariableDeclarator(node, path, binding, init).checkRestricted(node.binding);
  }

  @NotNull
  @Override
  public ValidationContext reduceWhileStatement(
      @NotNull WhileStatement node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext test,
      @NotNull ValidationContext body) {
    return super.reduceWhileStatement(node, path, test, body).clearFreeBreakStatements().clearFreeContinueStatements();
  }

  @NotNull
  @Override
  public ValidationContext reduceWithStatement(
      @NotNull WithStatement node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext object,
      @NotNull ValidationContext body) {
    return super.reduceWithStatement(node, path, object, body).addStrictError(
        new ValidationError(node, "WithStatement not allowed in strict mode"));
  }

  @NotNull
  @Override
  public ValidationContext reduceStaticMemberExpression(
      @NotNull StaticMemberExpression node,
      @NotNull ImmutableList path,
      @NotNull ValidationContext object,
      @NotNull ValidationContext property) {
    return super.reduceStaticMemberExpression(node, path, object, property.clearIdentifierNameError());
  }

  @NotNull
  @Override
  public ValidationContext reducePropertyName(@NotNull PropertyName node, @NotNull ImmutableList path) {
    ValidationContext v = super.reducePropertyName(node, path);
    switch (node.kind) {
    case Identifier:
      if (!Utils.isValidIdentifierName(node.value)) {
        return v.addError(
            new ValidationError(node, "PropertyName of kind 'identifier' must be valid identifier name."));
      }
      break;
    case Number:
      if (!Utils.isValidNumber(node.value)) {
        return v.addError(new ValidationError(node, "PropertyName of kind 'number' must be a valid number literal."));
      }
      break;
    }
    return v;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy