ElementPolicy.java revision 4e867904c8295537803c1c8a076e130df5674b58
14e867904c8295537803c1c8a076e130df5674b58mikesamuelpackage org.owasp.html;
24e867904c8295537803c1c8a076e130df5674b58mikesamuel
34e867904c8295537803c1c8a076e130df5674b58mikesamuelimport java.util.List;
44e867904c8295537803c1c8a076e130df5674b58mikesamuel
54e867904c8295537803c1c8a076e130df5674b58mikesamuelimport javax.annotation.Nullable;
64e867904c8295537803c1c8a076e130df5674b58mikesamuelimport javax.annotation.concurrent.Immutable;
74e867904c8295537803c1c8a076e130df5674b58mikesamuel
84e867904c8295537803c1c8a076e130df5674b58mikesamuel/**
94e867904c8295537803c1c8a076e130df5674b58mikesamuel * A policy that can be applied to an element to decide whether or not to
104e867904c8295537803c1c8a076e130df5674b58mikesamuel * allow it in the output, possibly after transforming attributes.
114e867904c8295537803c1c8a076e130df5674b58mikesamuel * <p>
124e867904c8295537803c1c8a076e130df5674b58mikesamuel * Element policies are applied <strong>after</strong>
134e867904c8295537803c1c8a076e130df5674b58mikesamuel * {@link AttributePolicy attribute policies} so
144e867904c8295537803c1c8a076e130df5674b58mikesamuel * they can be used to add extra attributes.
154e867904c8295537803c1c8a076e130df5674b58mikesamuel *
164e867904c8295537803c1c8a076e130df5674b58mikesamuel * @author Mike Samuel
174e867904c8295537803c1c8a076e130df5674b58mikesamuel * @see HtmlPolicyBuilder#allowElements(ElementPolicy, String...)
184e867904c8295537803c1c8a076e130df5674b58mikesamuel */
194e867904c8295537803c1c8a076e130df5674b58mikesamuel@TCB public interface ElementPolicy {
204e867904c8295537803c1c8a076e130df5674b58mikesamuel  /**
214e867904c8295537803c1c8a076e130df5674b58mikesamuel   * @param elementName the lower-case element name.
224e867904c8295537803c1c8a076e130df5674b58mikesamuel   * @param attrs a list of alternating attribute names and values.
234e867904c8295537803c1c8a076e130df5674b58mikesamuel   *    The list may be added to or removed from.  When removing, be
244e867904c8295537803c1c8a076e130df5674b58mikesamuel   *    careful to remove both the name and its associated value.
254e867904c8295537803c1c8a076e130df5674b58mikesamuel   *
264e867904c8295537803c1c8a076e130df5674b58mikesamuel   * @return {@code null} to disallow the element, or the adjusted element name.
274e867904c8295537803c1c8a076e130df5674b58mikesamuel   */
284e867904c8295537803c1c8a076e130df5674b58mikesamuel  public @Nullable String apply(String elementName, List<String> attrs);
294e867904c8295537803c1c8a076e130df5674b58mikesamuel
304e867904c8295537803c1c8a076e130df5674b58mikesamuel
314e867904c8295537803c1c8a076e130df5674b58mikesamuel  /** Utilities for working with element policies. */
324e867904c8295537803c1c8a076e130df5674b58mikesamuel  public static final class Util {
334e867904c8295537803c1c8a076e130df5674b58mikesamuel    private Util() { /* uninstantiable */ }
344e867904c8295537803c1c8a076e130df5674b58mikesamuel
354e867904c8295537803c1c8a076e130df5674b58mikesamuel    /**
364e867904c8295537803c1c8a076e130df5674b58mikesamuel     * Given zero or more element policies, returns an element policy equivalent
374e867904c8295537803c1c8a076e130df5674b58mikesamuel     * to applying them in order failing early if any of them fails.
384e867904c8295537803c1c8a076e130df5674b58mikesamuel     */
394e867904c8295537803c1c8a076e130df5674b58mikesamuel    public static final ElementPolicy join(ElementPolicy... policies) {
404e867904c8295537803c1c8a076e130df5674b58mikesamuel
414e867904c8295537803c1c8a076e130df5674b58mikesamuel      class PolicyJoiner {
424e867904c8295537803c1c8a076e130df5674b58mikesamuel        ElementPolicy last = null;
434e867904c8295537803c1c8a076e130df5674b58mikesamuel        ElementPolicy out = null;
444e867904c8295537803c1c8a076e130df5674b58mikesamuel
454e867904c8295537803c1c8a076e130df5674b58mikesamuel        void join(ElementPolicy p) {
464e867904c8295537803c1c8a076e130df5674b58mikesamuel          if (p == REJECT_ALL_ELEMENT_POLICY) {
474e867904c8295537803c1c8a076e130df5674b58mikesamuel            out = p;
484e867904c8295537803c1c8a076e130df5674b58mikesamuel          } else if (out != REJECT_ALL_ELEMENT_POLICY) {
494e867904c8295537803c1c8a076e130df5674b58mikesamuel            if (p instanceof JoinedElementPolicy) {
504e867904c8295537803c1c8a076e130df5674b58mikesamuel              JoinedElementPolicy jep = (JoinedElementPolicy) p;
514e867904c8295537803c1c8a076e130df5674b58mikesamuel              join(jep.first);
524e867904c8295537803c1c8a076e130df5674b58mikesamuel              join(jep.second);
534e867904c8295537803c1c8a076e130df5674b58mikesamuel            } else if (p != last) {
544e867904c8295537803c1c8a076e130df5674b58mikesamuel              last = p;
554e867904c8295537803c1c8a076e130df5674b58mikesamuel              if (out == null || out == IDENTITY_ELEMENT_POLICY) {
564e867904c8295537803c1c8a076e130df5674b58mikesamuel                out = p;
574e867904c8295537803c1c8a076e130df5674b58mikesamuel              } else if (p != IDENTITY_ELEMENT_POLICY) {
584e867904c8295537803c1c8a076e130df5674b58mikesamuel                out = new JoinedElementPolicy(out, p);
594e867904c8295537803c1c8a076e130df5674b58mikesamuel              }
604e867904c8295537803c1c8a076e130df5674b58mikesamuel            }
614e867904c8295537803c1c8a076e130df5674b58mikesamuel          }
624e867904c8295537803c1c8a076e130df5674b58mikesamuel        }
634e867904c8295537803c1c8a076e130df5674b58mikesamuel      }
644e867904c8295537803c1c8a076e130df5674b58mikesamuel
654e867904c8295537803c1c8a076e130df5674b58mikesamuel      PolicyJoiner pu = new PolicyJoiner();
664e867904c8295537803c1c8a076e130df5674b58mikesamuel      for (ElementPolicy policy : policies) {
674e867904c8295537803c1c8a076e130df5674b58mikesamuel        if (policy == null) { continue; }
684e867904c8295537803c1c8a076e130df5674b58mikesamuel        pu.join(policy);
694e867904c8295537803c1c8a076e130df5674b58mikesamuel      }
704e867904c8295537803c1c8a076e130df5674b58mikesamuel      return pu.out != null ? pu.out : IDENTITY_ELEMENT_POLICY;
714e867904c8295537803c1c8a076e130df5674b58mikesamuel    }
724e867904c8295537803c1c8a076e130df5674b58mikesamuel
734e867904c8295537803c1c8a076e130df5674b58mikesamuel  }
744e867904c8295537803c1c8a076e130df5674b58mikesamuel
754e867904c8295537803c1c8a076e130df5674b58mikesamuel  public static final ElementPolicy IDENTITY_ELEMENT_POLICY
764e867904c8295537803c1c8a076e130df5674b58mikesamuel      = new ElementPolicy() {
774e867904c8295537803c1c8a076e130df5674b58mikesamuel    public String apply(String elementName, List<String> attrs) {
784e867904c8295537803c1c8a076e130df5674b58mikesamuel      return elementName;
794e867904c8295537803c1c8a076e130df5674b58mikesamuel    }
804e867904c8295537803c1c8a076e130df5674b58mikesamuel  };
814e867904c8295537803c1c8a076e130df5674b58mikesamuel
824e867904c8295537803c1c8a076e130df5674b58mikesamuel  public static final ElementPolicy REJECT_ALL_ELEMENT_POLICY
834e867904c8295537803c1c8a076e130df5674b58mikesamuel      = new ElementPolicy() {
844e867904c8295537803c1c8a076e130df5674b58mikesamuel    public @Nullable String apply(String elementName, List<String> attrs) {
854e867904c8295537803c1c8a076e130df5674b58mikesamuel      return null;
864e867904c8295537803c1c8a076e130df5674b58mikesamuel    }
874e867904c8295537803c1c8a076e130df5674b58mikesamuel  };
884e867904c8295537803c1c8a076e130df5674b58mikesamuel
894e867904c8295537803c1c8a076e130df5674b58mikesamuel}
904e867904c8295537803c1c8a076e130df5674b58mikesamuel
914e867904c8295537803c1c8a076e130df5674b58mikesamuel@Immutable
924e867904c8295537803c1c8a076e130df5674b58mikesamuelfinal class JoinedElementPolicy implements ElementPolicy {
934e867904c8295537803c1c8a076e130df5674b58mikesamuel  final ElementPolicy first, second;
944e867904c8295537803c1c8a076e130df5674b58mikesamuel
954e867904c8295537803c1c8a076e130df5674b58mikesamuel  JoinedElementPolicy(ElementPolicy first, ElementPolicy second) {
964e867904c8295537803c1c8a076e130df5674b58mikesamuel    this.first = first;
974e867904c8295537803c1c8a076e130df5674b58mikesamuel    this.second = second;
984e867904c8295537803c1c8a076e130df5674b58mikesamuel  }
994e867904c8295537803c1c8a076e130df5674b58mikesamuel
1004e867904c8295537803c1c8a076e130df5674b58mikesamuel  public @Nullable String apply(String elementName, List<String> attrs) {
1014e867904c8295537803c1c8a076e130df5674b58mikesamuel    elementName = first.apply(elementName, attrs);
1024e867904c8295537803c1c8a076e130df5674b58mikesamuel    return elementName != null ? second.apply(elementName, attrs) : null;
1034e867904c8295537803c1c8a076e130df5674b58mikesamuel  }
1044e867904c8295537803c1c8a076e130df5674b58mikesamuel}
105