/*
 * Copyright 2015 The Error Prone Authors.
 *
 * 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.errorprone.bugpatterns;

import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.method.MethodMatchers.constructor;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;

import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.NewClassTree;

/** @author avenet@google.com (Arnaud J. Venet) */
@BugPattern(
    name = "InsecureCryptoUsage",
    altNames = {"InsecureCipherMode"},
    summary =
        "Usage of the Java Cryptography API is insecure because it is prone to vulnerabilities.",
    documentSuppression = false,
    severity = ERROR)
public class InsecureCipherMode extends BugChecker
    implements MethodInvocationTreeMatcher, NewClassTreeMatcher {
  private static final Matcher<ExpressionTree> BLOCKED_CRYPTO_EXPRESSIONS =
      anyOf(
          staticMethod().onClass("javax.crypto.Cipher").named("getInstance"),
          staticMethod().onClass("javax.crypto.KeyAgreement").named("getInstance"),
          staticMethod().onClass("javax.crypto.Mac").named("getInstance"),
          staticMethod().onClass("java.security.KeyPairGenerator").named("getInstance"),
          staticMethod().onClass("java.security.MessageDigest").named("getInstance"),
          staticMethod().onClass("java.security.Signature").named("getInstance"));

  private static final Matcher<NewClassTree> BLOCKED_CRYPTO_CLASSES =
      anyOf(
          constructor().forClass("javax.crypto.CipherInputStream"),
          constructor().forClass("javax.crypto.CipherOutputStream"));


  @Override
  public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
    Description description = checkMethodInvocation(tree, state);


    return description;
  }

  Description checkMethodInvocation(MethodInvocationTree tree, VisitorState state) {
    if (BLOCKED_CRYPTO_EXPRESSIONS.matches(tree, state)) {
      String message = "Use of these APIs is considered insecure";
      state.reportMatch(buildDescription(tree).setMessage(message).build());
    }

    return Description.NO_MATCH;
  }

  @Override
  public Description matchNewClass(NewClassTree tree, VisitorState state) {
    Description description = checkClassInvocation(tree, state);


    return description;
  }

  Description checkClassInvocation(NewClassTree tree, VisitorState state) {
    if (BLOCKED_CRYPTO_CLASSES.matches(tree, state)) {
      String message = "Use of these APIs is considered insecure";
      state.reportMatch(buildDescription(tree).setMessage(message).build());
    }

    return Description.NO_MATCH;
  }
}

@@ -937,16 +937,64 @@
 ethod;%0A%0A
+import com.google.common.collect.ImmutableList;%0A
 import c
@@ -2784,32 +2784,46 @@
 te state) %7B%0A    
+ImmutableList%3C
 Description desc
@@ -2809,32 +2809,33 @@
 List%3CDescription
+%3E
  description = c
@@ -2822,32 +2822,36 @@
 ion%3E description
+List
  = checkMethodIn
@@ -2867,32 +2867,130 @@
 tree, state);%0A%0A%0A
+    for (Description description : descriptionList) %7B%0A      state.reportMatch(description);%0A    %7D%0A
     return descr
@@ -2976,33 +2976,33 @@
    %7D%0A    return 
-d
+D
 escription;%0A  %7D%0A
@@ -2991,33 +2991,56 @@
  Description
-;%0A  %7D%0A%0A  
+.NO_MATCH;%0A  %7D%0A%0A  ImmutableList%3C
 Description 
@@ -3034,24 +3034,25 @@
 %3CDescription
+%3E
  checkMethod
@@ -3103,32 +3103,114 @@
 rState state) %7B%0A
+    ImmutableList.Builder%3CDescription%3E descriptionList = ImmutableList.builder();%0A
     if (BLOCKED_
@@ -3325,33 +3325,35 @@
 ;%0A      
-state.reportMatch
+descriptionList.add
 (buildDe
@@ -3408,33 +3408,33 @@
   %7D%0A%0A    return 
-D
+d
 escription.NO_MA
@@ -3423,33 +3423,36 @@
  description
-.NO_MATCH
+List.build()
 ;%0A  %7D%0A%0A  @Ov
@@ -3834,34 +3834,23 @@
 ;%0A      
-state.reportMatch(
+return 
 buildDes
@@ -3883,33 +3883,32 @@
 message).build()
-)
 ;%0A    %7D%0A%0A    ret

Return matches in checkMethodInvocation and checkClassInvocation so they can be checked against the exemption list.