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 }