vhardy 2004/02/24 06:04:37 Modified: test-sources/org/apache/batik/bridge EcmaNoLoadTest.java ScriptSelfTest.java test-sources/org/apache/batik/test/svg SVGOnLoadExceptionTest.java sources/org/apache/batik/bridge BaseScriptingEnvironment.java BridgeContext.java sources/org/apache/batik/script InterpreterPool.java sources/org/apache/batik/script/rhino BatikSecurityController.java RhinoInterpreter.java Log: Improved error reporting when failing to load interpreter. Now throw a SecurityException if Scripts() are used. Added code that exercises the Batik API from restricted permission code Revision Changes Path 1.2 +26 -6 xml-batik/test-sources/org/apache/batik/bridge/EcmaNoLoadTest.java Index: EcmaNoLoadTest.java =================================================================== RCS file: /home/cvs/xml-batik/test-sources/org/apache/batik/bridge/EcmaNoLoadTest.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- EcmaNoLoadTest.java 23 Feb 2004 15:24:59 -0000 1.1 +++ EcmaNoLoadTest.java 24 Feb 2004 14:04:37 -0000 1.2 @@ -83,19 +83,36 @@ SVGOnLoadExceptionTest t = buildTest(scripts, scriptSource[i], scriptOrigin[k], - secure[j]); + secure[j], + false); addTest(t); } } } // + // If script run in restricted mode, then there should be + // a security exception, no matter what the other settings are + // (if we are running code under a security manager, that is, + // i.e., secure is true). + scripts = "text/ecmascript"; + for (int i=0; i<scriptSource.length; i++) { + for (int k=0; k<scriptOrigin.length; k++) { + SVGOnLoadExceptionTest t = buildTest(scripts, + scriptSource[i], + scriptOrigin[k], + true, + true); + addTest(t); + } + } + + // // If "applicatin/ecmascript" is allowed, but the accepted // script origin is lower than the candidate script, then // the script should not be loaded (e.g., if scriptOrigin // is embeded and trying to load an external script). // - scripts = "text/ecmascript"; for (int j=0; j<scriptOrigin.length; j++) { int max = j; if (j == scriptOrigin.length - 1) { @@ -105,25 +122,28 @@ for (int k=0; k<secure.length; k++) { SVGOnLoadExceptionTest t= buildTest(scripts, scriptSource[i], scriptOrigin[j], - secure[k]); + secure[k], + false); addTest(t); } } } } - SVGOnLoadExceptionTest buildTest(String scripts, String id, String origin, boolean secure) { + SVGOnLoadExceptionTest buildTest(String scripts, String id, String origin, boolean secure, boolean restricted) { SVGOnLoadExceptionTest t = new SVGOnLoadExceptionTest(); String desc = "(scripts=" + scripts + ")(scriptOrigin=" + origin + - ")(secure=" + secure + ")"; + ")(secure=" + secure + + ")(restricted=" + restricted + ")"; t.setId(id + desc); t.setScriptOrigin(origin); t.setSecure(secure); t.setScripts(scripts); t.setExpectedExceptionClass("java.lang.SecurityException"); + t.setRestricted(restricted); return t; } 1.7 +11 -43 xml-batik/test-sources/org/apache/batik/bridge/ScriptSelfTest.java Index: ScriptSelfTest.java =================================================================== RCS file: /home/cvs/xml-batik/test-sources/org/apache/batik/bridge/ScriptSelfTest.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- ScriptSelfTest.java 23 Feb 2004 15:24:59 -0000 1.6 +++ ScriptSelfTest.java 24 Feb 2004 14:04:37 -0000 1.7 @@ -57,13 +57,21 @@ import java.security.AccessController; import java.security.AccessControlContext; +import java.security.CodeSource; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; import java.security.ProtectionDomain; +import java.security.Permission; +import java.security.PermissionCollection; import java.security.Permissions; +import java.security.Policy; import java.io.FilePermission; +import java.io.File; +import java.net.URL; + +import java.util.Enumeration; /** * Helper class to simplify writing the unitTesting.xml file for * the bridge. @@ -76,7 +84,6 @@ String scripts = "text/ecmascript, application/java-archive"; boolean secure = true; String scriptOrigin = "any"; - boolean restricted = false; String fileName; TestUserAgent userAgent = new TestUserAgent(); @@ -110,14 +117,6 @@ this.scriptOrigin = scriptOrigin; } - public boolean getRestricted() { - return restricted; - } - - public void setRestricted(boolean restricted) { - this.restricted = restricted; - } - public void setScripts(String scripts){ this.scripts = scripts; } @@ -136,37 +135,6 @@ } try { - if (!restricted) { - return superRunImpl(); - } else { - // Emulate calling from restricted code. We create a - // calling context with only the permission to read - // the file. - FilePermission permission - = new FilePermission(fileName, "read"); - Permissions permissions = new Permissions(); - permissions.add(permission); - ProtectionDomain domain = new ProtectionDomain(null, permissions); - AccessControlContext ctx = new AccessControlContext - (new ProtectionDomain[] {domain}); - - try { - return (TestReport)AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - return superRunImpl(); - } - }, ctx); - } catch (PrivilegedActionException pae) { - throw pae.getException(); - } - } - } finally { - ase.enforceSecurity(false); - } - } - - protected TestReport superRunImpl() throws Exception { - try { return super.runImpl(); } catch (ExceptionInInitializerError e) { e.printStackTrace(); @@ -174,6 +142,8 @@ } catch (NoClassDefFoundError e) { // e.printStackTrace(); throw new Exception(e.getMessage()); + } finally { + ase.enforceSecurity(false); } } @@ -205,8 +175,6 @@ } } - System.err.println(">>>>>>>>>>>>> using script security: " + scriptSecurity + - " for " + scriptPURL + " referenced from " + docPURL); return scriptSecurity; } } 1.6 +150 -53 xml-batik/test-sources/org/apache/batik/test/svg/SVGOnLoadExceptionTest.java Index: SVGOnLoadExceptionTest.java =================================================================== RCS file: /home/cvs/xml-batik/test-sources/org/apache/batik/test/svg/SVGOnLoadExceptionTest.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- SVGOnLoadExceptionTest.java 23 Feb 2004 15:24:59 -0000 1.5 +++ SVGOnLoadExceptionTest.java 24 Feb 2004 14:04:37 -0000 1.6 @@ -79,6 +79,21 @@ import org.apache.batik.util.XMLResourceDescriptor; import org.apache.batik.util.ApplicationSecurityEnforcer; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.CodeSource; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import java.security.ProtectionDomain; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; + +import java.io.FilePermission; + +import java.util.Enumeration; + /** * This test takes an SVG file as an input. It processes the input SVG * (meaning it turns it into a GVT tree) and then dispatches the 'onload' @@ -199,6 +214,25 @@ */ protected Boolean validate = new Boolean(false); + /** + * The name of the test file + */ + protected String fileName; + + /** + * Controls whether on not the document should be processed from + * a 'restricted' context, one with no createClassLoader permission. + */ + protected boolean restricted = false; + + public boolean getRestricted() { + return restricted; + } + + public void setRestricted(boolean restricted) { + this.restricted = restricted; + } + public void setScripts(String scripts){ this.scripts = scripts; } @@ -272,7 +306,7 @@ if (i != -1) { id = id.substring(0, i); } - String fileName = "test-resources/org/apache/batik/" + id + ".svg"; + fileName = "test-resources/org/apache/batik/" + id + ".svg"; svgURL = resolveURL(fileName); } } @@ -324,70 +358,119 @@ } try { - // - // First step: - // - // Load the input SVG into a Document object - // - String parserClassName = XMLResourceDescriptor.getXMLParserClassName(); - SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parserClassName); - f.setValidating(validate.booleanValue()); - Document doc = null; - - try { - doc = f.createDocument(svgURL); - } catch(Exception e){ - return handleException(e); - } - - // - // Second step: - // - // Now that the SVG file has been loaded, build - // a GVT Tree from it - // - TestUserAgent userAgent = buildUserAgent(); - GVTBuilder builder = new GVTBuilder(); - BridgeContext ctx = new BridgeContext(userAgent); - ctx.setDynamic(true); - Exception e = null; - try { - builder.build(ctx, doc); - BaseScriptingEnvironment scriptEnvironment - = new BaseScriptingEnvironment(ctx); - scriptEnvironment.loadScripts(); - scriptEnvironment.dispatchSVGLoadEvent(); - } catch (Exception ex){ - e = ex; - } finally { - if (e == null && userAgent.e != null) { - e = userAgent.e; + if (!restricted) { + return testImpl(); + } else { + // Emulate calling from restricted code. We create a + // calling context with only the permission to read + // the file. + Policy policy = Policy.getPolicy(); + URL classesURL = (new File("classes")).toURL(); + CodeSource cs = new CodeSource(classesURL, null); + PermissionCollection permissionsOrig + = policy.getPermissions(cs); + Permissions permissions = new Permissions(); + Enumeration iter = permissionsOrig.elements(); + while (iter.hasMoreElements()) { + Permission p = (Permission)iter.nextElement(); + if (!(p instanceof RuntimePermission)) { + if (!(p instanceof java.security.AllPermission)) { + permissions.add(p); + } + } else { + if (!"createClassLoader".equals(p.getName())) { + permissions.add(p); + } + } } - if (e != null) { - return handleException(e); + permissions.add(new FilePermission(fileName, "read")); + permissions.add(new RuntimePermission("accessDeclaredMembers")); + + ProtectionDomain domain = new ProtectionDomain(null, permissions); + AccessControlContext ctx = new AccessControlContext + (new ProtectionDomain[] {domain}); + + try { + return (TestReport)AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + return testImpl(); + } + }, ctx); + } catch (PrivilegedActionException pae) { + throw pae.getException(); } - } - - // - // If we got here, it means that the expected exception did not - // happen. Report an error - // - TestReport report = reportError(ERROR_EXCEPTION_DID_NOT_OCCUR); - report.addDescriptionEntry(ENTRY_KEY_EXPECTED_EXCEPTION, - expectedExceptionClass); - return report; + } } finally { ase.enforceSecurity(false); } } + /** + * Implementation helper + */ + protected TestReport testImpl() { + // + // First step: + // + // Load the input SVG into a Document object + // + String parserClassName = XMLResourceDescriptor.getXMLParserClassName(); + SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parserClassName); + f.setValidating(validate.booleanValue()); + Document doc = null; + + try { + doc = f.createDocument(svgURL); + } catch(Exception e){ + return handleException(e); + } + + // + // Second step: + // + // Now that the SVG file has been loaded, build + // a GVT Tree from it + // + TestUserAgent userAgent = buildUserAgent(); + GVTBuilder builder = new GVTBuilder(); + BridgeContext ctx = new BridgeContext(userAgent); + ctx.setDynamic(true); + Exception e = null; + try { + builder.build(ctx, doc); + BaseScriptingEnvironment scriptEnvironment + = new BaseScriptingEnvironment(ctx); + scriptEnvironment.loadScripts(); + scriptEnvironment.dispatchSVGLoadEvent(); + } catch (Exception ex){ + e = ex; + } finally { + if (e == null && userAgent.e != null) { + e = userAgent.e; + } + + if (e != null) { + return handleException(e); + } + } + + // + // If we got here, it means that the expected exception did not + // happen. Report an error + // + TestReport report = reportError(ERROR_EXCEPTION_DID_NOT_OCCUR); + report.addDescriptionEntry(ENTRY_KEY_EXPECTED_EXCEPTION, + expectedExceptionClass); + return report; + } + /** * Compares the input exception with the expected exception * If they match, then the test passes. Otherwise, the test fails */ protected TestReport handleException(Exception e) { - if (!e.getClass().getName().equals(expectedExceptionClass)) { + if (!isMatch(e.getClass(), expectedExceptionClass)) { TestReport report = reportError(ERROR_UNEXPECTED_EXCEPTION); report.addDescriptionEntry(ENTRY_KEY_UNEXPECTED_EXCEPTION, e.getClass().getName()); @@ -407,6 +490,20 @@ } } return reportSuccess(); + } + } + + /** + * Check if the input class' name (or one of its base classes) matches + * the input name. + */ + protected boolean isMatch(final Class cl, final String name) { + if (cl == null) { + return false; + } else if (cl.getName().equals(name)) { + return true; + } else { + return isMatch(cl.getSuperclass(), name); } } 1.29 +3 -6 xml-batik/sources/org/apache/batik/bridge/BaseScriptingEnvironment.java Index: BaseScriptingEnvironment.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/bridge/BaseScriptingEnvironment.java,v retrieving revision 1.28 retrieving revision 1.29 diff -u -r1.28 -r1.29 --- BaseScriptingEnvironment.java 19 Feb 2004 20:32:07 -0000 1.28 +++ BaseScriptingEnvironment.java 24 Feb 2004 14:04:37 -0000 1.29 @@ -294,14 +294,11 @@ public Interpreter getInterpreter(String lang) { interpreter = bridgeContext.getInterpreter(lang); if (interpreter == null) { - if (languages.contains(lang)) + if (languages.contains(lang)) { // Already issued warning so just return null; return null; - - UserAgent ua = bridgeContext.getUserAgent(); - if (ua != null) { - ua.displayError(new Exception("Unknown language: " + lang)); } + // So we know we have processed this interpreter. languages.add(lang); return null; 1.75 +17 -3 xml-batik/sources/org/apache/batik/bridge/BridgeContext.java Index: BridgeContext.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/bridge/BridgeContext.java,v retrieving revision 1.74 retrieving revision 1.75 diff -u -r1.74 -r1.75 --- BridgeContext.java 14 Dec 2003 15:14:57 -0000 1.74 +++ BridgeContext.java 24 Feb 2004 14:04:37 -0000 1.75 @@ -472,9 +472,23 @@ } Interpreter interpreter = (Interpreter)interpreterMap.get(language); if (interpreter == null) { - interpreter = interpreterPool.createInterpreter(document, language); - interpreterMap.put(language, interpreter); + try { + interpreter = interpreterPool.createInterpreter(document, language); + interpreterMap.put(language, interpreter); + } catch (Exception e) { + if (userAgent != null) { + userAgent.displayError(e); + return null; + } + } } + + if (interpreter == null) { + if (userAgent != null) { + userAgent.displayError(new Exception("Unknown language: " + language)); + } + } + return interpreter; } 1.17 +7 -12 xml-batik/sources/org/apache/batik/script/InterpreterPool.java Index: InterpreterPool.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/script/InterpreterPool.java,v retrieving revision 1.16 retrieving revision 1.17 diff -u -r1.16 -r1.17 --- InterpreterPool.java 16 Sep 2003 01:12:52 -0000 1.16 +++ InterpreterPool.java 24 Feb 2004 14:04:37 -0000 1.17 @@ -149,17 +149,12 @@ InterpreterFactory factory = (InterpreterFactory)factories.get(language); Interpreter interpreter = null; if (factory != null) - try { - interpreter = factory.createInterpreter - (((SVGOMDocument)document).getURLObject()); - if (document != null) { - interpreter.bindObject(BIND_NAME_DOCUMENT, document); - } - } catch (Exception t) { - // may happen if the batik interpreters class is here but - // not the scripting engine jar - t.printStackTrace(); - } + interpreter = factory.createInterpreter + (((SVGOMDocument)document).getURLObject()); + if (document != null) { + interpreter.bindObject(BIND_NAME_DOCUMENT, document); + } + return interpreter; } 1.5 +5 -2 xml-batik/sources/org/apache/batik/script/rhino/BatikSecurityController.java Index: BatikSecurityController.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/script/rhino/BatikSecurityController.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- BatikSecurityController.java 20 Feb 2004 16:34:52 -0000 1.4 +++ BatikSecurityController.java 24 Feb 2004 14:04:37 -0000 1.5 @@ -79,13 +79,14 @@ */ public GeneratedClassLoader createClassLoader (final ClassLoader parentLoader, Object securityDomain) { + if (securityDomain instanceof RhinoClassLoader) { return (RhinoClassLoader)securityDomain; } // FIXX: This should be supported by intersecting perms. // Calling var script = Script(source); script(); is not supported - throw new RuntimeException("NOT SUPPORTED"); + throw new SecurityException("Script() objects are not supported"); } /** @@ -95,6 +96,7 @@ * allowed by the current stack. */ public Object getDynamicSecurityDomain(Object securityDomain) { + ClassLoader loader = (RhinoClassLoader)securityDomain; // Already have a rhino loader in place no need to // do anything (normally you would want to union the @@ -130,6 +132,7 @@ } try { + // acc = new AccessController(acc, acc.getDomainCombiner()); return AccessController.doPrivileged (new PrivilegedExceptionAction() { public Object run() throws JavaScriptException { 1.38 +2 -2 xml-batik/sources/org/apache/batik/script/rhino/RhinoInterpreter.java Index: RhinoInterpreter.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/script/rhino/RhinoInterpreter.java,v retrieving revision 1.37 retrieving revision 1.38 diff -u -r1.37 -r1.38 --- RhinoInterpreter.java 20 Feb 2004 16:34:52 -0000 1.37 +++ RhinoInterpreter.java 24 Feb 2004 14:04:37 -0000 1.38 @@ -190,7 +190,7 @@ for (int i = 0; i < TO_BE_IMPORTED.length; i++) { p[i] = new NativeJavaPackage(TO_BE_IMPORTED[i], rhinoClassLoader); } try { - ScriptableObject.callMethod(globalObject, "importPackage", p); + ScriptableObject.callMethod(globalObject, "importPackage", p); } catch (JavaScriptException e) { // cannot happen as we know the method is there and // the parameters are ok
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]