001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Portions Copyright Apache Software Foundation.
007: *
008: * The contents of this file are subject to the terms of either the GNU
009: * General Public License Version 2 only ("GPL") or the Common Development
010: * and Distribution License("CDDL") (collectively, the "License"). You
011: * may not use this file except in compliance with the License. You can obtain
012: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
013: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
014: * language governing permissions and limitations under the License.
015: *
016: * When distributing the software, include this License Header Notice in each
017: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
018: * Sun designates this particular file as subject to the "Classpath" exception
019: * as provided by Sun in the GPL Version 2 section of the License file that
020: * accompanied this code. If applicable, add the following below the License
021: * Header, with the fields enclosed by brackets [] replaced by your own
022: * identifying information: "Portions Copyrighted [year]
023: * [name of copyright owner]"
024: *
025: * Contributor(s):
026: *
027: * If you wish your version of this file to be governed by only the CDDL or
028: * only the GPL Version 2, indicate your decision by adding "[Contributor]
029: * elects to include this software in this distribution under the [CDDL or GPL
030: * Version 2] license." If you don't indicate a single choice of license, a
031: * recipient has the option to distribute your version of this file under
032: * either the CDDL, the GPL Version 2 or to extend the choice of license to
033: * its licensees as provided above. However, if you add GPL Version 2 code
034: * and therefore, elected the GPL Version 2 license, then the option applies
035: * only if the new code is made subject to such option by the copyright
036: * holder.
037: */
038:
039: package javax.servlet.jsp.jstl.tlv;
040:
041: import java.io.IOException;
042: import java.io.InputStream;
043: import java.util.Map;
044:
045: import javax.servlet.jsp.tagext.PageData;
046: import javax.servlet.jsp.tagext.TagLibraryValidator;
047: import javax.servlet.jsp.tagext.ValidationMessage;
048: import javax.xml.parsers.ParserConfigurationException;
049: import javax.xml.parsers.SAXParser;
050: import javax.xml.parsers.SAXParserFactory;
051:
052: import org.xml.sax.Attributes;
053: import org.xml.sax.SAXException;
054: import org.xml.sax.helpers.DefaultHandler;
055:
056: /**
057: * <p>A TagLibraryValidator for enforcing restrictions against
058: * the use of JSP scripting elements.</p>
059: * <p>This TLV supports four initialization parameters, for controlling
060: * which of the four types of scripting elements are allowed or prohibited:</p>
061: * <ul>
062: * <li><b>allowDeclarations</b>: if true, indicates that declaration elements
063: * are not prohibited.
064: * <li><b>allowScriptlets</b>: if true, indicates that scriptlets are not
065: * prohibited
066: * <li><b>allowExpressions</b>: if true, indicates that top-level expression
067: * elements (i.e., expressions not associated with request-time attribute
068: * values) are not prohibited.
069: * <li><b>allowRTExpressions</b>: if true, indicates that expression elements
070: * associated with request-time attribute values are not prohibited.
071: * </ul>
072: * <p>The default value for all for initialization parameters is false,
073: * indicating all forms of scripting elements are to be prohibited.</p>
074: *
075: * @author <a href="mailto:mak@taglib.com">Mark A. Kolb</a>
076: * @author Shawn Bayern (minor changes)
077: */
078: public class ScriptFreeTLV extends TagLibraryValidator {
079: private boolean allowDeclarations = false;
080: private boolean allowScriptlets = false;
081: private boolean allowExpressions = false;
082: private boolean allowRTExpressions = false;
083: private SAXParserFactory factory;
084:
085: /**
086: * Constructs a new validator instance.
087: * Initializes the parser factory to create non-validating, namespace-aware
088: * SAX parsers.
089: */
090: public ScriptFreeTLV() {
091: factory = SAXParserFactory.newInstance();
092: factory.setValidating(false);
093: factory.setNamespaceAware(true);
094: }
095:
096: /**
097: * Sets the values of the initialization parameters, as supplied in the TLD.
098: * @param initParms a mapping from the names of the initialization parameters
099: * to their values, as specified in the TLD.
100: */
101: public void setInitParameters(Map<String, Object> initParms) {
102: super .setInitParameters(initParms);
103: String declarationsParm = (String) initParms
104: .get("allowDeclarations");
105: String scriptletsParm = (String) initParms
106: .get("allowScriptlets");
107: String expressionsParm = (String) initParms
108: .get("allowExpressions");
109: String rtExpressionsParm = (String) initParms
110: .get("allowRTExpressions");
111:
112: allowDeclarations = "true".equalsIgnoreCase(declarationsParm);
113: allowScriptlets = "true".equalsIgnoreCase(scriptletsParm);
114: allowExpressions = "true".equalsIgnoreCase(expressionsParm);
115: allowRTExpressions = "true".equalsIgnoreCase(rtExpressionsParm);
116: }
117:
118: /**
119: * Validates a single JSP page.
120: * @param prefix the namespace prefix specified by the page for the
121: * custom tag library being validated.
122: * @param uri the URI specified by the page for the TLD of the
123: * custom tag library being validated.
124: * @param page a wrapper around the XML representation of the page
125: * being validated.
126: * @return null, if the page is valid; otherwise, a ValidationMessage[]
127: * containing one or more messages indicating why the page is not valid.
128: */
129: public ValidationMessage[] validate(String prefix, String uri,
130: PageData page) {
131: InputStream in = null;
132: SAXParser parser;
133: MyContentHandler handler = new MyContentHandler();
134: try {
135: synchronized (factory) {
136: parser = factory.newSAXParser();
137: }
138: in = page.getInputStream();
139: parser.parse(in, handler);
140: } catch (ParserConfigurationException e) {
141: return vmFromString(e.toString());
142: } catch (SAXException e) {
143: return vmFromString(e.toString());
144: } catch (IOException e) {
145: return vmFromString(e.toString());
146: } finally {
147: if (in != null)
148: try {
149: in.close();
150: } catch (IOException e) {
151: }
152: }
153: return handler.reportResults();
154: }
155:
156: /**
157: * Handler for SAX events.
158: * Four counters are provided as instance variables,
159: * for counting occurrences of prohibited scripting elements.
160: */
161: private class MyContentHandler extends DefaultHandler {
162: private int declarationCount = 0;
163: private int scriptletCount = 0;
164: private int expressionCount = 0;
165: private int rtExpressionCount = 0;
166:
167: /**
168: * This event is received whenever a new element is encountered.
169: * The qualified name of each such element is compared against
170: * the names of any prohibited scripting elements. When found, the
171: * corresponding counter is incremented.
172: * If expressions representing request-time attribute values are
173: * prohibited, it is also necessary to check the values of all
174: * attributes specified by the element. (Trying to figure out
175: * which attributes actually support request-time attribute values
176: * and checking only those is far more trouble than it's worth.)
177: */
178: public void startElement(String namespaceUri, String localName,
179: String qualifiedName, Attributes atts) {
180: if ((!allowDeclarations)
181: && qualifiedName.equals("jsp:declaration"))
182: ++declarationCount;
183: else if ((!allowScriptlets)
184: && qualifiedName.equals("jsp:scriptlet"))
185: ++scriptletCount;
186: else if ((!allowExpressions)
187: && qualifiedName.equals("jsp:expression"))
188: ++expressionCount;
189: if (!allowRTExpressions)
190: countRTExpressions(atts);
191: }
192:
193: /**
194: * Auxiliary method for checking attribute values to see if
195: * are specified via request-time attribute values.
196: * Expressions representing request-time attribute values are
197: * recognized by their "%=" and "%" delimiters. When found, the
198: * corresponding counter is incremented.
199: */
200: private void countRTExpressions(Attributes atts) {
201: int stop = atts.getLength();
202: for (int i = 0; i < stop; ++i) {
203: String attval = atts.getValue(i);
204: if (attval.startsWith("%=") && attval.endsWith("%"))
205: ++rtExpressionCount;
206: }
207: }
208:
209: /**
210: * Constructs a String reporting the number(s) of prohibited
211: * scripting elements that were detected, if any.
212: * Returns null if no violations were found, making the result
213: * of this method suitable for the return value of the
214: * TagLibraryValidator.validate() method.
215: *
216: * TODO: The update from 7/13/2001 merely makes this validator
217: * compliant with the new TLV API, but does not fully take advantage
218: * of this API. In the future, we should do so... but because
219: * of the possibility that anti-script checking will be incorporated
220: * into the base TLV, I've held off for now and just changed this
221: * class to use the new API. -- SB.
222: */
223: public ValidationMessage[] reportResults() {
224: if (declarationCount + scriptletCount + expressionCount
225: + rtExpressionCount > 0) {
226: StringBuffer results = new StringBuffer(
227: "JSP page contains ");
228: boolean first = true;
229: if (declarationCount > 0) {
230: results.append(Integer.toString(declarationCount));
231: results.append(" declaration");
232: if (declarationCount > 1)
233: results.append('s');
234: first = false;
235: }
236: if (scriptletCount > 0) {
237: if (!first)
238: results.append(", ");
239: results.append(Integer.toString(scriptletCount));
240: results.append(" scriptlet");
241: if (scriptletCount > 1)
242: results.append('s');
243: first = false;
244: }
245: if (expressionCount > 0) {
246: if (!first)
247: results.append(", ");
248: results.append(Integer.toString(expressionCount));
249: results.append(" expression");
250: if (expressionCount > 1)
251: results.append('s');
252: first = false;
253: }
254: if (rtExpressionCount > 0) {
255: if (!first)
256: results.append(", ");
257: results.append(Integer.toString(rtExpressionCount));
258: results.append(" request-time attribute value");
259: if (rtExpressionCount > 1)
260: results.append('s');
261: first = false;
262: }
263: results.append(".");
264: return vmFromString(results.toString());
265: } else {
266: return null;
267: }
268: }
269: }
270:
271: // constructs a ValidationMessage[] from a single String and no ID
272: private static ValidationMessage[] vmFromString(String message) {
273: return new ValidationMessage[] { new ValidationMessage(null,
274: message) };
275: }
276:
277: }
|