Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/batik/bridge/ExternalResourcesTest.java


1   /*
2   
3      Copyright 2002-2003  The Apache Software Foundation 
4   
5      Licensed under the Apache License, Version 2.0 (the "License");
6      you may not use this file except in compliance with the License.
7      You may obtain a copy of the License at
8   
9          http://www.apache.org/licenses/LICENSE-2.0
10  
11     Unless required by applicable law or agreed to in writing, software
12     distributed under the License is distributed on an "AS IS" BASIS,
13     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14     See the License for the specific language governing permissions and
15     limitations under the License.
16  
17   */
18  package org.apache.batik.bridge;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.net.MalformedURLException;
23  import java.net.URL;
24  import java.util.StringTokenizer;
25  import java.util.Vector;
26  
27  import org.w3c.dom.Document;
28  import org.w3c.dom.Element;
29  import org.w3c.dom.Node;
30  
31  import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
32  import org.apache.batik.gvt.GraphicsNode;
33  import org.apache.batik.test.AbstractTest;
34  import org.apache.batik.test.DefaultTestReport;
35  import org.apache.batik.test.TestReport;
36  import org.apache.batik.util.ParsedURL;
37  import org.apache.batik.util.XMLResourceDescriptor;
38  
39  /**
40   * This test validates that SecurityExceptions are generated when 
41   * the user is trying the access external resources and the UserAgent
42   * disallows that.
43   *
44   * In the following, 'unsecure' means an external resource coming from
45   * a different location than the file referencing it.
46   *
47   * This test works with an SVG file containing an unsecure stylesheet
48   * and a set of unsecure elements of all kinds, such as <image>
49   * <use> or <feImage>. All these elements are defined
50   * in a defs section. The test tries to load the document and validates
51   * that a SecurityException is thrown (because of the unsecure 
52   * stylesheet). Then, the test iterates over the various unsecure
53   * elements, inserting them into the document outside the defs
54   * section, which should result in a SecurityException in each case.
55   * 
56   * There is a property (secure) to have the test work the opposite
57   * way and check that no SecurityException is thrown if access
58   * to external resources is allowed.
59   *
60   * @author <a href="mailto:vhardy@apache.org">Vincent Hardy</a>
61   * @version $Id: ExternalResourcesTest.java,v 1.6 2004/08/18 07:16:38 vhardy Exp $
62   */
63  
64  public class ExternalResourcesTest extends AbstractTest 
65      implements ErrorConstants {
66      /**
67       * Error when the input file cannot be loaded into a
68       * Document object
69       * {0} = IOException message
70       */
71      public static final String ERROR_CANNOT_LOAD_SVG_DOCUMENT
72          = "ExternalResourcesTest.error.cannot.load.svg.document";
73  
74      /**
75       * Error while processing the document
76       * {0} = BridgeException message
77       */
78      public static final String ERROR_WHILE_PROCESSING_SVG_DOCUMENT
79          = "ExternalResourcesTest.error.while.processing.svg.document";
80  
81      /**
82       * Error: an expected exception was not thrown
83       * {0} = List of ids for which the exception was not thrown
84       */
85      public static final String ERROR_UNTHROWN_SECURITY_EXCEPTIONS
86          = "ExternalResourcesTest.error.unthrown.security.exceptions";
87  
88      /**
89       * Error: an unexpected exception was thrown
90       * {0} = List of ids for which an exception was thrown
91       */
92      public static final String ERROR_THROWN_SECURITY_EXCEPTIONS
93          = "ExternalResourcesTest.error.thrown.security.exceptions";
94  
95      /**
96       * Error when the insertion point cannot be found in the 
97       * test document
98       * {0} = insertion point id
99       */
100     public static final String ERROR_NO_INSERTION_POINT_IN_DOCUMENT
101         = "ExternalResourceTest.error.no.insertion.point.in.document";
102 
103     /**
104      * Error when the test could not find a list of ids for testing
105      */
106     public static final String ERROR_NO_ID_LIST
107         = "ExternalResourceTest.error.no.id.list";
108 
109     /**
110      * Error when one of the target id cannot be found
111      * {0} = id which was not found
112      */
113     public static final String ERROR_TARGET_ID_NOT_FOUND
114         = "ExternalResourcesTest.error.target.id.not.found";
115 
116     /**
117      * Entry describing the error
118      */
119     public static final String ENTRY_KEY_ERROR_DESCRIPTION 
120         = "ExternalResourcesTest.entry.key.error.description";
121 
122     public static final String ENTRY_KEY_INSERTION_POINT_ID
123         = "ExternalResourcesTest.entry.key.insertion.point.id";
124 
125     public static final String ENTRY_KEY_TARGET_ID
126         = "ExternalResourcesTest.entry.target.id";
127 
128     public static final String ENTRY_KEY_EXPECTED_EXCEPTION_ON
129         = "ExternalResourcesTest.entry.key.expected.exception.on";
130 
131     public static final String ENTRY_KEY_UNEXPECTED_EXCEPTION_ON
132         = "ExternalResourcesTest.entry.key.unexpected.exception.on";
133 
134     /**
135      * Pseudo id for the external stylesheet test
136      */
137     public static final String EXTERNAL_STYLESHEET_ID 
138         = "external-stylesheet";
139 
140     /**
141      * Test Namespace
142      */
143     public static final String testNS = "http://xml.apache.org/batik/test";
144 
145     /**
146      * Id of the element where unsecure content is inserted
147      */
148     public static final String INSERTION_POINT_ID = "insertionPoint";
149 
150     /**
151      * Location of test files in filesystem.
152      */
153     public static final String FILE_DIR = 
154         "test-resources/org/apache/batik/bridge/";
155     /**
156      * Controls whether the test works in secure mode or not
157      */
158     protected boolean secure = true;
159 
160     String svgURL;
161 
162     public void setId(String id){
163         super.setId(id);
164         String file = id;
165         int idx = file.indexOf('.');
166         if (idx != -1) {
167             file = file.substring(0,idx);
168         }
169         svgURL = resolveURL(FILE_DIR + file + ".svg");
170     }
171 
172     public Boolean getSecure(){
173         return new Boolean(secure);
174     }
175 
176     public void setSecure(Boolean secure) {
177         this.secure = secure.booleanValue();
178     }
179 
180     /**
181      * Resolves the input string as follows.
182      * + First, the string is interpreted as a file description.
183      *   If the file exists, then the file name is turned into
184      *   a URL.
185      * + Otherwise, the string is supposed to be a URL. If it
186      *   is an invalid URL, an IllegalArgumentException is thrown.
187      */
188     protected String resolveURL(String url){
189         // Is url a file?
190         File f = (new File(url)).getAbsoluteFile();
191         if(f.getParentFile().exists()){
192             try{
193                 return f.toURL().toString();
194             }catch(MalformedURLException e){
195                 throw new IllegalArgumentException();
196             }
197         }
198         
199         // url is not a file. It must be a regular URL...
200         try{
201             return (new URL(url)).toString();
202         }catch(MalformedURLException e){
203             throw new IllegalArgumentException(url);
204         }
205     }
206 
207     /**
208      * This test uses a list of ids found in the test document. These ids reference
209      * elements found in a defs section. For each such element, the test will 
210      * attempt to insert the target id at a given insertion point. That insertion
211      * should cause a SecurityException. If so, the test passes. Otherwise, the test
212      * will fail
213      */
214     public TestReport runImpl() throws Exception{
215         DefaultTestReport report 
216             = new DefaultTestReport(this);
217 
218         //
219         // First step:
220         //
221         // Load the input SVG into a Document object
222         //
223         String parserClassName = XMLResourceDescriptor.getXMLParserClassName();
224         SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parserClassName);
225         Document doc = null;
226 
227         try {
228             doc = f.createDocument(svgURL);
229         } catch(IOException e){
230             report.setErrorCode(ERROR_CANNOT_LOAD_SVG_DOCUMENT);
231             report.addDescriptionEntry(ENTRY_KEY_ERROR_DESCRIPTION,
232                                        e.getMessage());
233             report.setPassed(false);
234             return report;
235         } catch(Exception e){
236             report.setErrorCode(ERROR_CANNOT_LOAD_SVG_DOCUMENT);
237             report.addDescriptionEntry(ENTRY_KEY_ERROR_DESCRIPTION,
238                                        e.getMessage());
239             report.setPassed(false);
240             return report;
241         }
242 
243         Vector failures = new Vector();
244 
245         //
246         // Do an initial processing to validate that the external 
247         // stylesheet causes a SecurityException
248         //
249         MyUserAgent userAgent = buildUserAgent();
250         GVTBuilder builder = new GVTBuilder();
251         BridgeContext ctx = new BridgeContext(userAgent);
252         ctx.setDynamic(true);
253 
254         // We expect either a SecurityException or a BridgeException 
255         // with ERR_URI_UNSECURE.
256         Throwable th = null;
257         try {
258             GraphicsNode gn = builder.build(ctx, doc);
259             gn.getBounds();
260             th = userAgent.getDisplayError();
261         } catch (BridgeException e){
262             th = e;
263         } catch (SecurityException e) {
264             th = e;
265         } catch (Throwable t) {
266             th = t;
267         }
268         if (th == null) {
269             if (secure)
270                 failures.addElement(EXTERNAL_STYLESHEET_ID);
271         } else if (th instanceof SecurityException) {
272             if (!secure)
273                 failures.addElement(EXTERNAL_STYLESHEET_ID);
274         } else if (th instanceof BridgeException) {
275             BridgeException be = (BridgeException)th;
276             if (!secure  ||
277                 (secure && !ERR_URI_UNSECURE.equals(be.getCode()))) {
278                 report.setErrorCode(ERROR_WHILE_PROCESSING_SVG_DOCUMENT);
279                 report.addDescriptionEntry(ENTRY_KEY_ERROR_DESCRIPTION,
280                                            be.getMessage());
281                 report.setPassed(false);
282                 return report;
283             }
284         }
285         
286         //
287         // Remove the stylesheet from the document
288         //
289         Node child = doc.getFirstChild();
290         Node next = null;
291         while (child != null) {
292             next = child.getNextSibling();
293             if (child.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
294                 doc.removeChild(child);
295             }
296             child = next;
297         }
298 
299         //
300         // Now, get the list of ids to be checked
301         //
302         Element root = doc.getDocumentElement();
303         String idList = root.getAttributeNS(testNS, "targetids");
304         if (idList == null || "".equals(idList)) {
305             report.setErrorCode(ERROR_NO_ID_LIST);
306             report.setPassed(false);
307             return report;
308         }
309 
310         StringTokenizer st = new StringTokenizer(idList, ",");
311         String[] ids = new String[st.countTokens()];
312         for (int i=0; i<ids.length; i++) {
313             ids[i] = st.nextToken().toString().trim();
314         }
315 
316         for (int i=0; i<ids.length; i++) {
317             String id = ids[i];
318             userAgent = buildUserAgent();
319             builder = new GVTBuilder();
320             ctx = new BridgeContext(userAgent);
321             ctx.setDynamic(true);
322 
323             Document cloneDoc = (Document)doc.cloneNode(true);
324             Element insertionPoint = cloneDoc.getElementById(INSERTION_POINT_ID);
325             
326             if (insertionPoint == null) {
327                 report.setErrorCode(ERROR_NO_INSERTION_POINT_IN_DOCUMENT);
328                 report.addDescriptionEntry(ENTRY_KEY_INSERTION_POINT_ID, 
329                                            INSERTION_POINT_ID);
330                 report.setPassed(false);
331                 return report;
332             }
333 
334             Element target = cloneDoc.getElementById(id);
335 
336             if (target == null) {
337                 report.setErrorCode(ERROR_TARGET_ID_NOT_FOUND);
338                 report.addDescriptionEntry(ENTRY_KEY_TARGET_ID,
339                                            id);
340                 report.setPassed(false);
341                 return report;
342             }
343 
344             insertionPoint.appendChild(target);
345             th = null;
346             try {
347                 GraphicsNode gn = builder.build(ctx, cloneDoc);
348                 gn.getBounds();
349                 th = userAgent.getDisplayError();
350             } catch (BridgeException e){
351                 th = e;
352             } catch (SecurityException e) {
353                 th = e;
354             } catch (Throwable t) {
355                 th = t;
356             }
357             if (th == null) {
358                 if (secure)
359                     failures.addElement(id);
360             } else if (th instanceof SecurityException) {
361                 if (!secure)
362                     failures.addElement(id);
363             } else if (th instanceof BridgeException) {
364                 BridgeException be = (BridgeException)th;
365                 if (!secure  ||
366                     (secure && !ERR_URI_UNSECURE.equals(be.getCode()))) {
367                     report.setErrorCode(ERROR_WHILE_PROCESSING_SVG_DOCUMENT);
368                     report.addDescriptionEntry(ENTRY_KEY_ERROR_DESCRIPTION,
369                                                be.getMessage());
370                     report.setPassed(false);
371                     return report;
372                 }
373             } else {
374                 // Some unknown exception was displayed...
375                 report.setErrorCode(ERROR_WHILE_PROCESSING_SVG_DOCUMENT);
376                 report.addDescriptionEntry(ENTRY_KEY_ERROR_DESCRIPTION,
377                                            th.getMessage());
378                 report.setPassed(false);
379                 return report;
380             }
381 
382         }
383 
384         if (failures.size() == 0) {
385             return reportSuccess();
386         }
387 
388         if (secure) {
389             report.setErrorCode(ERROR_UNTHROWN_SECURITY_EXCEPTIONS);
390             for (int i=0; i<failures.size(); i++) {
391                 report.addDescriptionEntry(ENTRY_KEY_EXPECTED_EXCEPTION_ON,
392                                            failures.elementAt(i));
393             }
394         } else {
395             report.setErrorCode(ERROR_THROWN_SECURITY_EXCEPTIONS);
396             for (int i=0; i<failures.size(); i++) {
397                 report.addDescriptionEntry(ENTRY_KEY_UNEXPECTED_EXCEPTION_ON,
398                                            failures.elementAt(i));
399             }
400         }
401 
402         report.setPassed(false);
403         return report;
404     }
405 
406     protected interface MyUserAgent extends UserAgent {
407         public Exception getDisplayError();
408     }
409 
410     protected MyUserAgent buildUserAgent(){
411         if (secure) {
412             return new SecureUserAgent();
413         } else {
414             return new RelaxedUserAgent();
415         }
416     }
417     
418     class MyUserAgentAdapter extends UserAgentAdapter implements MyUserAgent {
419         Exception ex = null;
420         public void displayError(Exception ex) {
421             this.ex = ex;
422             super.displayError(ex);
423         }
424 
425         public Exception getDisplayError() { return ex; }
426     }
427     
428     class SecureUserAgent extends MyUserAgentAdapter {
429         public ExternalResourceSecurity 
430             getExternalResourceSecurity(ParsedURL resourcePURL,
431                                         ParsedURL docPURL){
432             return new NoLoadExternalResourceSecurity();
433             
434         }
435     }
436 
437     class RelaxedUserAgent extends MyUserAgentAdapter {
438         public ExternalResourceSecurity 
439             getExternalResourceSecurity(ParsedURL resourcePURL,
440                                         ParsedURL docPURL){
441             return new RelaxedExternalResourceSecurity(resourcePURL,
442                                                        docPURL);
443             
444         }
445     }
446 
447 }