1// Copyright (c) 2011, Mike Samuel
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions
6// are met:
7//
8// Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// Redistributions in binary form must reproduce the above copyright
11// notice, this list of conditions and the following disclaimer in the
12// documentation and/or other materials provided with the distribution.
13// Neither the name of the OWASP nor the names of its contributors may
14// be used to endorse or promote products derived from this software
15// without specific prior written permission.
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28
29package org.owasp.html;
30
31import java.util.List;
32
33import javax.annotation.Nullable;
34import javax.annotation.concurrent.Immutable;
35
36/**
37 * A policy that can be applied to an element to decide whether or not to
38 * allow it in the output, possibly after transforming attributes.
39 * <p>
40 * Element policies are applied <strong>after</strong>
41 * {@link AttributePolicy attribute policies} so
42 * they can be used to add extra attributes.
43 *
44 * @author Mike Samuel <mikesamuel@gmail.com>
45 * @see HtmlPolicyBuilder#allowElements(ElementPolicy, String...)
46 */
47@TCB public interface ElementPolicy {
48  /**
49   * @param elementName the lower-case element name.
50   * @param attrs a list of alternating attribute names and values.
51   *    The list may be added to or removed from.  When removing, be
52   *    careful to remove both the name and its associated value.
53   *
54   * @return {@code null} to disallow the element, or the adjusted element name.
55   */
56  public @Nullable String apply(String elementName, List<String> attrs);
57
58
59  /** Utilities for working with element policies. */
60  public static final class Util {
61    private Util() { /* uninstantiable */ }
62
63    /**
64     * Given zero or more element policies, returns an element policy equivalent
65     * to applying them in order failing early if any of them fails.
66     */
67    public static final ElementPolicy join(ElementPolicy... policies) {
68
69      class PolicyJoiner {
70        ElementPolicy last = null;
71        ElementPolicy out = null;
72
73        void join(ElementPolicy p) {
74          if (p == REJECT_ALL_ELEMENT_POLICY) {
75            out = p;
76          } else if (out != REJECT_ALL_ELEMENT_POLICY) {
77            if (p instanceof JoinedElementPolicy) {
78              JoinedElementPolicy jep = (JoinedElementPolicy) p;
79              join(jep.first);
80              join(jep.second);
81            } else if (p != last) {
82              last = p;
83              if (out == null || out == IDENTITY_ELEMENT_POLICY) {
84                out = p;
85              } else if (p != IDENTITY_ELEMENT_POLICY) {
86                out = new JoinedElementPolicy(out, p);
87              }
88            }
89          }
90        }
91      }
92
93      PolicyJoiner pu = new PolicyJoiner();
94      for (ElementPolicy policy : policies) {
95        if (policy == null) { continue; }
96        pu.join(policy);
97      }
98      return pu.out != null ? pu.out : IDENTITY_ELEMENT_POLICY;
99    }
100
101  }
102
103  public static final ElementPolicy IDENTITY_ELEMENT_POLICY
104      = new ElementPolicy() {
105    public String apply(String elementName, List<String> attrs) {
106      return elementName;
107    }
108  };
109
110  public static final ElementPolicy REJECT_ALL_ELEMENT_POLICY
111      = new ElementPolicy() {
112    public @Nullable String apply(String elementName, List<String> attrs) {
113      return null;
114    }
115  };
116
117}
118
119@Immutable
120final class JoinedElementPolicy implements ElementPolicy {
121  final ElementPolicy first, second;
122
123  JoinedElementPolicy(ElementPolicy first, ElementPolicy second) {
124    this.first = first;
125    this.second = second;
126  }
127
128  public @Nullable String apply(String elementName, List<String> attrs) {
129    elementName = first.apply(elementName, attrs);
130    return elementName != null ? second.apply(elementName, attrs) : null;
131  }
132}
133