Hello Bear, hello all,
IMHO, the final continuous build machine should be anyway a root-only machine (OK, root and the-same-root-person-as-a-common-user). The developer machine is also pretty much a developer-only machine, so there is little concern about some other user fiddling with the files.
To be honest, my concern isn't another user fiddling with the files. That type of stuff is best handled administratively.
My concern is everyone you don't know about. As maven is used by more projects, it becomes a more attractive target. And that means you have to start worrying about more sophsticated attackers. Forcing them to get root access to do any damage raises the barrier a bit, and keeping everything in the original jar files (vs. unpacking them) helps a bit more.
If so, the problem is common to many other packages than only Maven.
Very few support plugin libraries to this extent. Plugins are always problematic - system libraries have presumably had some amount of testing, etc. But "plugins" are often lightweight, poorly tested code which is still run with high privilege. That's scary - it's not as bad as Outlook's willingness to run arbitrary code from anyone who bothers to send some its way, but it's still pretty scary.
Anyway... the attached file is a ClassLoader that scans the ToC of all of the jar files in a subdirectory, then loads the content on demand. The "main" routine shows how to use the standard interface to load a class, a ResourceBundle, or an arbitrary resource (e.g., a .png image).
Something it doesn't yet support, but easily could, is a separate SecurityManager for plugin classes. E.g., maybe the maven classes have net access, but the plugins don't. (Or only specific ones do.) Another possible idea is restricting the classes that can be loaded by the plugins, e.g., allowing them to load standard java.* and javax.* libraries, but not others.
The same thing applies to all downloaded code - to me it makes a lot of sense to be able to set up rules in the build environment that say an automatically downloaded package can't download another package, it can't access the net at all, perhaps can't read or write any files outside of a sandbox, etc. If you really need some of these packages, they should be deliberately bundled and installed in the regular classpath.
After this security manager is installed, you have some measure of protection even if somebody manages to corrupt an upstream package. Your production environment may still get hosed, but your development environment should be protected from a rogue jarfile scribbling into others.
/** * Not-so-quickie classloader that loads jar files from "plugins" * directory. The "main" method shows it loading and running (local) * class, loading a ResourceBundle, and loading an arbitary resource. * * Internally, it contains a reverse mapping of the contents of * the jar files. That is, if the jar file "foo.jar" contains * the file "bar.class," the "classes" map will map "bar.class" to * "foo.jar" * * A related "SecureClassLoader" class should be defined - one that * explicitly supports a security manager for plugins. * * Released to "Maven" group for use as they see fit. */ import java.io.*; import java.net.*; import java.util.*; import java.util.jar.*; import java.util.zip.*;
// only used for 'main' test code. import java.lang.reflect.*; public class MavenClassLoader extends ClassLoader { // protected Map classes = new HashMap(); // protected Map resources = new HashMap(); protected Map classes = new TreeMap(); // for debugging... protected Map resources = new TreeMap(); // for debugging... // // Constructor: reads all jar files in a directory and caches // the contents for later retrieval. // public MavenClassLoader (String pluginDir) throws IOException { super(); String[] files = new File(pluginDir).list(); for (int i = 0; i < files.length; i++) { if (!files[i].endsWith(".jar")) { continue; } String filename = pluginDir + File.separator + files[i]; cacheJar(new JarFile(filename), filename); } } // // Recursively cache contents of jar file. // private void cacheJar (JarFile jf, String path) throws IOException { ZipEntry ze; Enumeration e = jf.entries(); while (e.hasMoreElements()) { ze = (ZipEntry) e.nextElement(); if (ze.isDirectory()) { continue; } String name = ze.getName(); if (name.startsWith("META-INF/")) { continue; } // class file? if (name.endsWith(".class")) { classes.put(name, path); } // resource file? else if (name.startsWith("plugin-resources/")) { resources.put(name.substring(17), path); } // certain items with common names... else { // what to do here?... } } } public Class findClass (String name) throws ClassNotFoundException { byte[] b = loadClassData(name.replace('.', '/')); return defineClass(name, b, 0, b.length); } private byte[] loadClassData (String name) throws ClassNotFoundException { if (!classes.containsKey(name)) { throw new ClassNotFoundException(name); } String path = (String) classes.get(name); if (path == null) { throw new ClassNotFoundException(name + ": internal"); } try { return getBytes(path, name); } catch (IOException e) { throw new ClassNotFoundException (e.toString()); } } public InputStream getResourceAsStream (String name) { if (!resources.containsKey(name)) { return null; } String path = (String) resources.get(name); try { byte[] b = getBytes(path, "plugin-resources/" + name); if (b == null) { System.err.println("no data?"); return null; } return new ByteArrayInputStream(b); } catch (IOException e) { System.err.println(e.toString()); return null; } } // // Read a single file from a jar file. // private byte[] getBytes (String path, String name) throws IOException { JarFile jf; JarEntry je; jf = new JarFile(path); if (jf == null) { return null; } je = jf.getJarEntry(name); if (je == null) { return null; } int len = (int) je.getSize(); if (len < 1) { return null; } byte[] b = new byte[len]; int n; InputStream is = jf.getInputStream(je); for (int off = 0; off < len; off += n) { n = is.read(b, off, len-off); } is.close(); jf.close(); return b; } static public final void main (String[] args) throws Exception { MavenClassLoader cl = new MavenClassLoader("./plugins"); String spacer = "----------------------------------------"; // Iterator i = cl.classes.keySet().iterator(); // while (i.hasNext()) { // System.out.println((String) i.next()); // } // System.out.println(""); // System.out.println(spacer); // System.out.println(""); // // i = cl.resources.keySet().iterator(); // while (i.hasNext()) { // System.out.println((String) i.next()); // } // System.out.println(""); // System.out.println(spacer); // System.out.println(""); // demonstration: loading arbitrary class & running "main()". try { Class hello = Class.forName("hello", true, cl); Class[] params = new Class[1]; params[0] = Class.forName("[Ljava.lang.String;", false, null); Method m = hello.getDeclaredMethod("main", params); Object[] o = new Object[1]; o[0] = new String[0]; m.invoke(hello, o); } catch (ClassNotFoundException e) { System.err.println(e.toString()); } System.out.println(""); System.out.println(spacer); System.out.println(""); // demonstration: reading bundle try { ResourceBundle bundle = ResourceBundle.getBundle("src.conf.app", Locale.getDefault(), cl); Enumeration e = bundle.getKeys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); System.out.println(key + "=" + bundle.getString(key)); } } catch (MissingResourceException e) { System.err.println(e.toString()); } System.out.println(""); System.out.println(spacer); System.out.println(""); // demonstration: reading arbitary object byte[] b = new byte[512]; int n; InputStream is = cl.getResourceAsStream("templates/index.xml"); while ((n = is.read(b)) > 0) { System.out.write(b, 0, n); } is.close(); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]