18403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Copyright (c) 2011, Mike Samuel
28403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// All rights reserved.
38403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel//
48403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Redistribution and use in source and binary forms, with or without
58403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// modification, are permitted provided that the following conditions
68403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// are met:
78403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel//
88403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Redistributions of source code must retain the above copyright
98403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// notice, this list of conditions and the following disclaimer.
108403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Redistributions in binary form must reproduce the above copyright
118403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// notice, this list of conditions and the following disclaimer in the
128403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// documentation and/or other materials provided with the distribution.
138403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// Neither the name of the OWASP nor the names of its contributors may
148403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// be used to endorse or promote products derived from this software
158403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// without specific prior written permission.
168403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
178403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
188403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
198403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
208403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
218403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
228403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
238403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
248403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
258403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
268403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
278403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel// POSSIBILITY OF SUCH DAMAGE.
288403881c365ab36b721ccc4500af1b3a5bd25870mikesamuel
294e867904c8295537803c1c8a076e130df5674b58mikesamuelpackage org.owasp.html;
304e867904c8295537803c1c8a076e130df5674b58mikesamuel
314e867904c8295537803c1c8a076e130df5674b58mikesamuelimport java.util.List;
324e867904c8295537803c1c8a076e130df5674b58mikesamuel
334e867904c8295537803c1c8a076e130df5674b58mikesamuelimport javax.annotation.Nullable;
344e867904c8295537803c1c8a076e130df5674b58mikesamuelimport javax.annotation.concurrent.Immutable;
354e867904c8295537803c1c8a076e130df5674b58mikesamuel
364e867904c8295537803c1c8a076e130df5674b58mikesamuel/**
374e867904c8295537803c1c8a076e130df5674b58mikesamuel * A policy that can be applied to an element to decide whether or not to
384e867904c8295537803c1c8a076e130df5674b58mikesamuel * allow it in the output, possibly after transforming attributes.
394e867904c8295537803c1c8a076e130df5674b58mikesamuel * <p>
404e867904c8295537803c1c8a076e130df5674b58mikesamuel * Element policies are applied <strong>after</strong>
414e867904c8295537803c1c8a076e130df5674b58mikesamuel * {@link AttributePolicy attribute policies} so
424e867904c8295537803c1c8a076e130df5674b58mikesamuel * they can be used to add extra attributes.
434e867904c8295537803c1c8a076e130df5674b58mikesamuel *
446d8c2e9241d042a3e0bff40dac4c388966ad060cmikesamuel * @author Mike Samuel <mikesamuel@gmail.com>
454e867904c8295537803c1c8a076e130df5674b58mikesamuel * @see HtmlPolicyBuilder#allowElements(ElementPolicy, String...)
464e867904c8295537803c1c8a076e130df5674b58mikesamuel */
474e867904c8295537803c1c8a076e130df5674b58mikesamuel@TCB public interface ElementPolicy {
484e867904c8295537803c1c8a076e130df5674b58mikesamuel  /**
494e867904c8295537803c1c8a076e130df5674b58mikesamuel   * @param elementName the lower-case element name.
504e867904c8295537803c1c8a076e130df5674b58mikesamuel   * @param attrs a list of alternating attribute names and values.
514e867904c8295537803c1c8a076e130df5674b58mikesamuel   *    The list may be added to or removed from.  When removing, be
524e867904c8295537803c1c8a076e130df5674b58mikesamuel   *    careful to remove both the name and its associated value.
534e867904c8295537803c1c8a076e130df5674b58mikesamuel   *
544e867904c8295537803c1c8a076e130df5674b58mikesamuel   * @return {@code null} to disallow the element, or the adjusted element name.
554e867904c8295537803c1c8a076e130df5674b58mikesamuel   */
564e867904c8295537803c1c8a076e130df5674b58mikesamuel  public @Nullable String apply(String elementName, List<String> attrs);
574e867904c8295537803c1c8a076e130df5674b58mikesamuel
584e867904c8295537803c1c8a076e130df5674b58mikesamuel
594e867904c8295537803c1c8a076e130df5674b58mikesamuel  /** Utilities for working with element policies. */
604e867904c8295537803c1c8a076e130df5674b58mikesamuel  public static final class Util {
614e867904c8295537803c1c8a076e130df5674b58mikesamuel    private Util() { /* uninstantiable */ }
624e867904c8295537803c1c8a076e130df5674b58mikesamuel
634e867904c8295537803c1c8a076e130df5674b58mikesamuel    /**
644e867904c8295537803c1c8a076e130df5674b58mikesamuel     * Given zero or more element policies, returns an element policy equivalent
654e867904c8295537803c1c8a076e130df5674b58mikesamuel     * to applying them in order failing early if any of them fails.
664e867904c8295537803c1c8a076e130df5674b58mikesamuel     */
674e867904c8295537803c1c8a076e130df5674b58mikesamuel    public static final ElementPolicy join(ElementPolicy... policies) {
684e867904c8295537803c1c8a076e130df5674b58mikesamuel
694e867904c8295537803c1c8a076e130df5674b58mikesamuel      class PolicyJoiner {
704e867904c8295537803c1c8a076e130df5674b58mikesamuel        ElementPolicy last = null;
714e867904c8295537803c1c8a076e130df5674b58mikesamuel        ElementPolicy out = null;
724e867904c8295537803c1c8a076e130df5674b58mikesamuel
734e867904c8295537803c1c8a076e130df5674b58mikesamuel        void join(ElementPolicy p) {
744e867904c8295537803c1c8a076e130df5674b58mikesamuel          if (p == REJECT_ALL_ELEMENT_POLICY) {
754e867904c8295537803c1c8a076e130df5674b58mikesamuel            out = p;
764e867904c8295537803c1c8a076e130df5674b58mikesamuel          } else if (out != REJECT_ALL_ELEMENT_POLICY) {
774e867904c8295537803c1c8a076e130df5674b58mikesamuel            if (p instanceof JoinedElementPolicy) {
784e867904c8295537803c1c8a076e130df5674b58mikesamuel              JoinedElementPolicy jep = (JoinedElementPolicy) p;
794e867904c8295537803c1c8a076e130df5674b58mikesamuel              join(jep.first);
804e867904c8295537803c1c8a076e130df5674b58mikesamuel              join(jep.second);
814e867904c8295537803c1c8a076e130df5674b58mikesamuel            } else if (p != last) {
824e867904c8295537803c1c8a076e130df5674b58mikesamuel              last = p;
834e867904c8295537803c1c8a076e130df5674b58mikesamuel              if (out == null || out == IDENTITY_ELEMENT_POLICY) {
844e867904c8295537803c1c8a076e130df5674b58mikesamuel                out = p;
854e867904c8295537803c1c8a076e130df5674b58mikesamuel              } else if (p != IDENTITY_ELEMENT_POLICY) {
864e867904c8295537803c1c8a076e130df5674b58mikesamuel                out = new JoinedElementPolicy(out, p);
874e867904c8295537803c1c8a076e130df5674b58mikesamuel              }
884e867904c8295537803c1c8a076e130df5674b58mikesamuel            }
894e867904c8295537803c1c8a076e130df5674b58mikesamuel          }
904e867904c8295537803c1c8a076e130df5674b58mikesamuel        }
914e867904c8295537803c1c8a076e130df5674b58mikesamuel      }
924e867904c8295537803c1c8a076e130df5674b58mikesamuel
934e867904c8295537803c1c8a076e130df5674b58mikesamuel      PolicyJoiner pu = new PolicyJoiner();
944e867904c8295537803c1c8a076e130df5674b58mikesamuel      for (ElementPolicy policy : policies) {
954e867904c8295537803c1c8a076e130df5674b58mikesamuel        if (policy == null) { continue; }
964e867904c8295537803c1c8a076e130df5674b58mikesamuel        pu.join(policy);
974e867904c8295537803c1c8a076e130df5674b58mikesamuel      }
984e867904c8295537803c1c8a076e130df5674b58mikesamuel      return pu.out != null ? pu.out : IDENTITY_ELEMENT_POLICY;
994e867904c8295537803c1c8a076e130df5674b58mikesamuel    }
1004e867904c8295537803c1c8a076e130df5674b58mikesamuel
1014e867904c8295537803c1c8a076e130df5674b58mikesamuel  }
1024e867904c8295537803c1c8a076e130df5674b58mikesamuel
1034e867904c8295537803c1c8a076e130df5674b58mikesamuel  public static final ElementPolicy IDENTITY_ELEMENT_POLICY
1044e867904c8295537803c1c8a076e130df5674b58mikesamuel      = new ElementPolicy() {
1054e867904c8295537803c1c8a076e130df5674b58mikesamuel    public String apply(String elementName, List<String> attrs) {
1064e867904c8295537803c1c8a076e130df5674b58mikesamuel      return elementName;
1074e867904c8295537803c1c8a076e130df5674b58mikesamuel    }
1084e867904c8295537803c1c8a076e130df5674b58mikesamuel  };
1094e867904c8295537803c1c8a076e130df5674b58mikesamuel
1104e867904c8295537803c1c8a076e130df5674b58mikesamuel  public static final ElementPolicy REJECT_ALL_ELEMENT_POLICY
1114e867904c8295537803c1c8a076e130df5674b58mikesamuel      = new ElementPolicy() {
1124e867904c8295537803c1c8a076e130df5674b58mikesamuel    public @Nullable String apply(String elementName, List<String> attrs) {
1134e867904c8295537803c1c8a076e130df5674b58mikesamuel      return null;
1144e867904c8295537803c1c8a076e130df5674b58mikesamuel    }
1154e867904c8295537803c1c8a076e130df5674b58mikesamuel  };
1164e867904c8295537803c1c8a076e130df5674b58mikesamuel
1174e867904c8295537803c1c8a076e130df5674b58mikesamuel}
1184e867904c8295537803c1c8a076e130df5674b58mikesamuel
1194e867904c8295537803c1c8a076e130df5674b58mikesamuel@Immutable
1204e867904c8295537803c1c8a076e130df5674b58mikesamuelfinal class JoinedElementPolicy implements ElementPolicy {
1214e867904c8295537803c1c8a076e130df5674b58mikesamuel  final ElementPolicy first, second;
1224e867904c8295537803c1c8a076e130df5674b58mikesamuel
1234e867904c8295537803c1c8a076e130df5674b58mikesamuel  JoinedElementPolicy(ElementPolicy first, ElementPolicy second) {
1244e867904c8295537803c1c8a076e130df5674b58mikesamuel    this.first = first;
1254e867904c8295537803c1c8a076e130df5674b58mikesamuel    this.second = second;
1264e867904c8295537803c1c8a076e130df5674b58mikesamuel  }
1274e867904c8295537803c1c8a076e130df5674b58mikesamuel
1284e867904c8295537803c1c8a076e130df5674b58mikesamuel  public @Nullable String apply(String elementName, List<String> attrs) {
1294e867904c8295537803c1c8a076e130df5674b58mikesamuel    elementName = first.apply(elementName, attrs);
1304e867904c8295537803c1c8a076e130df5674b58mikesamuel    return elementName != null ? second.apply(elementName, attrs) : null;
1314e867904c8295537803c1c8a076e130df5674b58mikesamuel  }
1324e867904c8295537803c1c8a076e130df5674b58mikesamuel}
133