Report a finding whenever honeypot class is loaded

Whenever external input leads to unrestricted class loading a RCE could
be possible. Report a finding every time and not only in specific
use-cases.
diff --git a/sanitizers/src/main/java/jaz/Zer.java b/sanitizers/src/main/java/jaz/Zer.java
index 0b27609..08ca3d2 100644
--- a/sanitizers/src/main/java/jaz/Zer.java
+++ b/sanitizers/src/main/java/jaz/Zer.java
@@ -15,93 +15,220 @@
 package jaz;
 
 import com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh;
-import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium;
 import com.code_intelligence.jazzer.api.Jazzer;
-import java.io.IOException;
-import java.io.ObjectInputStream;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.Serializable;
+import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.function.Function;
 
 /**
- * A honeypot class that reports an appropriate finding on any interaction with one of its methods
- * or initializers.
+ * A honeypot class that reports a finding on initialization.
  *
- * Note: This class must not be referenced in any way by the rest of the code, not even statically.
- * When referring to it, always use its hardcoded class name "jaz.Zer".
+ * Class loading based on externally controlled data could lead to RCE
+ * depending on available classes on the classpath. Even if no applicable
+ * gadget class is available, allowing input to control class loading is a bad
+ * idea and should be prevented. A finding is generated whenever the class
+ * is loaded and initialized, regardless of its further use.
+ * <p>
+ * This class needs to implement {@link Serializable} to be considered in
+ * deserialization scenarios. It also implements common constructors, getter
+ * and setter and common interfaces to increase chances of passing
+ * deserialization checks.
+ * <p>
+ * <b>Note</b>: Jackson provides a nice list of "nasty classes" at
+ * <a
+ * href=https://github.com/FasterXML/jackson-databind/blob/2.14/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/SubTypeValidator.java>SubTypeValidator</a>.
+ * <p>
+ * <b>Note</b>: This class must not be referenced in any way by the rest of the code, not even
+ * statically. When referring to it, always use its hardcoded class name {@code jaz.Zer}.
  */
-@SuppressWarnings("unused")
-public class Zer implements java.io.Serializable {
+@SuppressWarnings({"rawtypes", "unused"})
+public class Zer
+    implements Serializable, Cloneable, Comparable<Zer>, Comparator, Closeable, Flushable, Iterable,
+               Iterator, Runnable, Callable, Function, Collection, List {
   static final long serialVersionUID = 42L;
 
-  private static final Throwable staticInitializerCause;
-
   static {
-    staticInitializerCause = new FuzzerSecurityIssueMedium("finalize call on arbitrary object");
-  }
-
-  public Zer() {
-    Jazzer.reportFindingFromHook(
-        new FuzzerSecurityIssueMedium("default constructor call on arbitrary object"));
-  }
-
-  public Zer(String arg1) {
-    Jazzer.reportFindingFromHook(
-        new FuzzerSecurityIssueMedium("String constructor call on arbitrary object"));
-  }
-
-  public Zer(String arg1, Throwable arg2) {
-    Jazzer.reportFindingFromHook(
-        new FuzzerSecurityIssueMedium("(String, Throwable) constructor call on arbitrary object"));
-  }
-
-  private String jaz;
-
-  public String getJaz() {
-    Jazzer.reportFindingFromHook(new FuzzerSecurityIssueMedium("getter call on arbitrary object"));
-    return jaz;
-  }
-
-  public void setJaz(String jaz) {
-    Jazzer.reportFindingFromHook(new FuzzerSecurityIssueMedium("setter call on arbitrary object"));
-    this.jaz = jaz;
-  }
-
-  @Override
-  public int hashCode() {
-    Jazzer.reportFindingFromHook(
-        new FuzzerSecurityIssueMedium("hashCode call on arbitrary object"));
-    return super.hashCode();
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    Jazzer.reportFindingFromHook(new FuzzerSecurityIssueMedium("equals call on arbitrary object"));
-    return super.equals(obj);
-  }
-
-  @Override
-  protected Object clone() throws CloneNotSupportedException {
-    Jazzer.reportFindingFromHook(new FuzzerSecurityIssueMedium("clone call on arbitrary object"));
-    return super.clone();
-  }
-
-  @Override
-  public String toString() {
-    Jazzer.reportFindingFromHook(
-        new FuzzerSecurityIssueMedium("toString call on arbitrary object"));
-    return super.toString();
-  }
-
-  @Override
-  protected void finalize() throws Throwable {
-    // finalize is invoked automatically by the GC with an uninformative stack trace. We use the
-    // stack trace prerecorded in the static initializer.
-    Jazzer.reportFindingFromHook(staticInitializerCause);
-    super.finalize();
-  }
-
-  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
     Jazzer.reportFindingFromHook(new FuzzerSecurityIssueHigh("Remote Code Execution\n"
-        + "  Deserialization of arbitrary classes with custom readObject may allow remote\n"
-        + "  code execution depending on the classpath."));
-    in.defaultReadObject();
+        + "Unrestricted class loading based on externally controlled data may allow\n"
+        + "remote code execution depending on available classes on the classpath."));
+  }
+
+  // Common constructors
+
+  public Zer() {}
+
+  public Zer(String arg1) {}
+
+  public Zer(String arg1, Throwable arg2) {}
+
+  // Getter/Setter
+
+  public Object getJaz() {
+    return this;
+  }
+
+  public void setJaz(String jaz) {}
+
+  // Common interface stubs
+
+  @Override
+  public void close() {}
+
+  @Override
+  public void flush() {}
+
+  @Override
+  public int compareTo(Zer o) {
+    return 0;
+  }
+
+  @Override
+  public int compare(Object o1, Object o2) {
+    return 0;
+  }
+
+  @Override
+  public int size() {
+    return 0;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return false;
+  }
+
+  @Override
+  public boolean contains(Object o) {
+    return false;
+  }
+
+  @Override
+  public Object[] toArray() {
+    return new Object[0];
+  }
+
+  @Override
+  public boolean add(Object o) {
+    return false;
+  }
+
+  @Override
+  public boolean remove(Object o) {
+    return false;
+  }
+
+  @Override
+  public boolean addAll(Collection c) {
+    return false;
+  }
+
+  @Override
+  public boolean addAll(int index, Collection c) {
+    return false;
+  }
+
+  @Override
+  public void clear() {}
+
+  @Override
+  public Object get(int index) {
+    return this;
+  }
+
+  @Override
+  public Object set(int index, Object element) {
+    return this;
+  }
+
+  @Override
+  public void add(int index, Object element) {}
+
+  @Override
+  public Object remove(int index) {
+    return this;
+  }
+
+  @Override
+  public int indexOf(Object o) {
+    return 0;
+  }
+
+  @Override
+  public int lastIndexOf(Object o) {
+    return 0;
+  }
+
+  @Override
+  @SuppressWarnings("ConstantConditions")
+  public ListIterator listIterator() {
+    return null;
+  }
+
+  @Override
+  @SuppressWarnings("ConstantConditions")
+  public ListIterator listIterator(int index) {
+    return null;
+  }
+
+  @Override
+  public List subList(int fromIndex, int toIndex) {
+    return this;
+  }
+
+  @Override
+  public boolean retainAll(Collection c) {
+    return false;
+  }
+
+  @Override
+  public boolean removeAll(Collection c) {
+    return false;
+  }
+
+  @Override
+  public boolean containsAll(Collection c) {
+    return false;
+  }
+
+  @Override
+  public Object[] toArray(Object[] a) {
+    return new Object[0];
+  }
+
+  @Override
+  public Iterator iterator() {
+    return this;
+  }
+
+  @Override
+  public void run() {}
+
+  @Override
+  public boolean hasNext() {
+    return false;
+  }
+
+  @Override
+  public Object next() {
+    return this;
+  }
+
+  @Override
+  public Object call() throws Exception {
+    return this;
+  }
+
+  @Override
+  public Object apply(Object o) {
+    return this;
+  }
+
+  @Override
+  @SuppressWarnings("MethodDoesntCallSuperMethod")
+  public Object clone() {
+    return this;
   }
 }