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 javax.annotation.Nullable; 032 import javax.annotation.concurrent.Immutable; 033 034 /** 035 * A policy that can be applied to an HTML attribute to decide whether or not to 036 * allow it in the output, possibly after transforming its value. 037 * 038 * @author Mike Samuel <mikesamuel@gmail.com> 039 * @see HtmlPolicyBuilder.AttributeBuilder#matching(AttributePolicy) 040 */ 041 @TCB public interface AttributePolicy { 042 043 /** 044 * @param elementName the lower-case element name. 045 * @param attributeName the lower-case attribute name. 046 * @param value the attribute value without quotes and with HTML entities 047 * decoded. 048 * 049 * @return {@code null} to disallow the attribute or the adjusted value if 050 * allowed. 051 */ 052 public @Nullable String apply( 053 String elementName, String attributeName, String value); 054 055 056 /** Utilities for working with attribute policies. */ 057 public static final class Util { 058 059 /** 060 * An attribute policy equivalent to applying all the given policies in 061 * order, failing early if any of them fails. 062 */ 063 public static final AttributePolicy join(AttributePolicy... policies) { 064 065 class PolicyJoiner { 066 AttributePolicy last = null; 067 AttributePolicy out = null; 068 069 void join(AttributePolicy p) { 070 if (REJECT_ALL_ATTRIBUTE_POLICY.equals(p)) { 071 out = p; 072 } else if (!REJECT_ALL_ATTRIBUTE_POLICY.equals(out)) { 073 if (p instanceof JoinedAttributePolicy) { 074 JoinedAttributePolicy jap = (JoinedAttributePolicy) p; 075 join(jap.first); 076 join(jap.second); 077 } else if (p != last) { 078 last = p; 079 if (out == null || IDENTITY_ATTRIBUTE_POLICY.equals(out)) { 080 out = p; 081 } else if (!IDENTITY_ATTRIBUTE_POLICY.equals(p)) { 082 out = new JoinedAttributePolicy(out, p); 083 } 084 } 085 } 086 } 087 } 088 089 PolicyJoiner pu = new PolicyJoiner(); 090 for (AttributePolicy policy : policies) { 091 if (policy == null) { continue; } 092 pu.join(policy); 093 } 094 return pu.out != null ? pu.out : IDENTITY_ATTRIBUTE_POLICY; 095 } 096 } 097 098 099 public static final AttributePolicy IDENTITY_ATTRIBUTE_POLICY 100 = new AttributePolicy() { 101 public String apply( 102 String elementName, String attributeName, String value) { 103 return value; 104 } 105 }; 106 107 public static final AttributePolicy REJECT_ALL_ATTRIBUTE_POLICY 108 = new AttributePolicy() { 109 public @Nullable String apply( 110 String elementName, String attributeName, String value) { 111 return null; 112 } 113 }; 114 115 } 116 117 @Immutable 118 final class JoinedAttributePolicy implements AttributePolicy { 119 final AttributePolicy first, second; 120 121 JoinedAttributePolicy(AttributePolicy first, AttributePolicy second) { 122 this.first = first; 123 this.second = second; 124 } 125 126 public @Nullable String apply( 127 String elementName, String attributeName, String value) { 128 value = first.apply(elementName, attributeName, value); 129 return value != null 130 ? second.apply(elementName, attributeName, value) : null; 131 } 132 }