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 29 package org.owasp.html; 30 31 import java.util.List; 32 33 import javax.annotation.Nullable; 34 import 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 */ apply(String elementName, List<String> attrs)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 { 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 */ join(ElementPolicy... policies)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 120 final class JoinedElementPolicy implements ElementPolicy { 121 final ElementPolicy first, second; 122 JoinedElementPolicy(ElementPolicy first, ElementPolicy second)123 JoinedElementPolicy(ElementPolicy first, ElementPolicy second) { 124 this.first = first; 125 this.second = second; 126 } 127 apply(String elementName, List<String> attrs)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