001 // Copyright (c) 2011, Mike Samuel
002 // All rights reserved.
003 //
004 // Redistribution and use in source and binary forms, with or without
005 // modification, are permitted provided that the following conditions
006 // are met:
007 //
008 // Redistributions of source code must retain the above copyright
009 // notice, this list of conditions and the following disclaimer.
010 // Redistributions in binary form must reproduce the above copyright
011 // notice, this list of conditions and the following disclaimer in the
012 // documentation and/or other materials provided with the distribution.
013 // Neither the name of the OWASP nor the names of its contributors may
014 // be used to endorse or promote products derived from this software
015 // without specific prior written permission.
016 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
017 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
018 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
019 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
020 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
022 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
024 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
025 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
026 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
027 // POSSIBILITY OF SUCH DAMAGE.
028
029 package org.owasp.html;
030
031 import java.util.List;
032
033 import javax.annotation.Nullable;
034 import javax.annotation.concurrent.Immutable;
035
036 /**
037 * A policy that can be applied to an element to decide whether or not to
038 * allow it in the output, possibly after transforming attributes.
039 * <p>
040 * Element policies are applied <strong>after</strong>
041 * {@link AttributePolicy attribute policies} so
042 * they can be used to add extra attributes.
043 *
044 * @author Mike Samuel <mikesamuel@gmail.com>
045 * @see HtmlPolicyBuilder#allowElements(ElementPolicy, String...)
046 */
047 @TCB public interface ElementPolicy {
048 /**
049 * @param elementName the lower-case element name.
050 * @param attrs a list of alternating attribute names and values.
051 * The list may be added to or removed from. When removing, be
052 * careful to remove both the name and its associated value.
053 *
054 * @return {@code null} to disallow the element, or the adjusted element name.
055 */
056 public @Nullable String apply(String elementName, List<String> attrs);
057
058
059 /** Utilities for working with element policies. */
060 public static final class Util {
061 private Util() { /* uninstantiable */ }
062
063 /**
064 * Given zero or more element policies, returns an element policy equivalent
065 * to applying them in order failing early if any of them fails.
066 */
067 public static final ElementPolicy join(ElementPolicy... policies) {
068
069 class PolicyJoiner {
070 ElementPolicy last = null;
071 ElementPolicy out = null;
072
073 void join(ElementPolicy p) {
074 if (p == REJECT_ALL_ELEMENT_POLICY) {
075 out = p;
076 } else if (out != REJECT_ALL_ELEMENT_POLICY) {
077 if (p instanceof JoinedElementPolicy) {
078 JoinedElementPolicy jep = (JoinedElementPolicy) p;
079 join(jep.first);
080 join(jep.second);
081 } else if (p != last) {
082 last = p;
083 if (out == null || out == IDENTITY_ELEMENT_POLICY) {
084 out = p;
085 } else if (p != IDENTITY_ELEMENT_POLICY) {
086 out = new JoinedElementPolicy(out, p);
087 }
088 }
089 }
090 }
091 }
092
093 PolicyJoiner pu = new PolicyJoiner();
094 for (ElementPolicy policy : policies) {
095 if (policy == null) { continue; }
096 pu.join(policy);
097 }
098 return pu.out != null ? pu.out : IDENTITY_ELEMENT_POLICY;
099 }
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
120 final 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 }