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 }