Loading classes from the plugin archive
(cherry picked from commit 2adf04c)
This commit is contained in:
		
							
								
								
									
										61
									
								
								src/main/java/org/owasp/webgoat/plugins/Plugin.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/java/org/owasp/webgoat/plugins/Plugin.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| package org.owasp.webgoat.plugins; | ||||
|  | ||||
| import org.owasp.webgoat.lessons.AbstractLesson; | ||||
|  | ||||
| import java.nio.file.Path; | ||||
|  | ||||
| public class Plugin { | ||||
|  | ||||
|     private final Class<AbstractLesson> lesson; | ||||
|     private final String lessonPlanHtml; | ||||
|     private final String lessonSolutionHtml; | ||||
|  | ||||
|     public static class Builder { | ||||
|  | ||||
|         private Path pluginDirectory; | ||||
|         private Class lesson; | ||||
|  | ||||
|         public Builder loadClass(String name, byte[] classFile) { | ||||
|             ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); | ||||
|             PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader, classFile); | ||||
|             try { | ||||
|                 String realClassName = name.replace("/lesson_plans/", "").replaceAll("/", ".").replaceAll(".class", ""); | ||||
|                 Class clazz = pluginClassLoader.loadClass(realClassName); | ||||
|                 if (AbstractLesson.class.isAssignableFrom(clazz)) { | ||||
|                     this.lesson = clazz; | ||||
|                 } | ||||
|             } catch (ClassNotFoundException e) { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder setBaseDirectory(Path pluginDirectory) { | ||||
|             this.pluginDirectory = pluginDirectory; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Plugin build() { | ||||
|             return new Plugin(this.lesson, null, null); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Plugin(Class<AbstractLesson> lesson, String lessonPlanHtml, String lessonSolutionHtml) { | ||||
|         this.lesson = lesson; | ||||
|         this.lessonPlanHtml = lessonPlanHtml; | ||||
|         this.lessonSolutionHtml = lessonSolutionHtml; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public String getLessonPlanHtml() { | ||||
|         return lessonPlanHtml; | ||||
|     } | ||||
|  | ||||
|     public Class<AbstractLesson> getLesson() { | ||||
|         return lesson; | ||||
|     } | ||||
|  | ||||
|     public String getLessonSolutionHtml() { | ||||
|         return lessonSolutionHtml; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| package org.owasp.webgoat.plugins; | ||||
|  | ||||
| public class PluginClassLoader extends ClassLoader { | ||||
|  | ||||
|     private final byte[] classFile; | ||||
|  | ||||
|     public PluginClassLoader(ClassLoader parent, byte[] classFile) { | ||||
|         super(parent); | ||||
|         this.classFile = classFile; | ||||
|     } | ||||
|  | ||||
|     public Class findClass(String name) { | ||||
|         return defineClass(name, classFile, 0, classFile.length); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
							
								
								
									
										79
									
								
								src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| package org.owasp.webgoat.plugins; | ||||
|  | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.net.URI; | ||||
| import java.nio.file.FileSystem; | ||||
| import java.nio.file.FileSystems; | ||||
| import java.nio.file.FileVisitResult; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.SimpleFileVisitor; | ||||
| import java.nio.file.StandardCopyOption; | ||||
| import java.nio.file.attribute.BasicFileAttributes; | ||||
| import java.util.HashMap; | ||||
|  | ||||
| /** | ||||
|  * Extract the zip file and place the files in a temp directory | ||||
|  */ | ||||
| public class PluginExtractor { | ||||
|  | ||||
|     private static final String DIRECTORY = "plugins"; | ||||
|     private final Path pluginArchive; | ||||
|     private final Logger logger = LoggerFactory.getLogger(getClass()); | ||||
|  | ||||
|     public PluginExtractor(Path pluginArchive) { | ||||
|         this.pluginArchive = pluginArchive; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public Plugin extract() { | ||||
|         final Plugin.Builder pluginBuilder = new Plugin.Builder(); | ||||
|         FileSystem zip = null; | ||||
|         try { | ||||
|             zip = createZipFileSystem(); | ||||
|             final Path root = zip.getPath("/"); | ||||
|             final Path tempDirectory = Files.createTempDirectory(DIRECTORY); | ||||
|             pluginBuilder.setBaseDirectory(tempDirectory); | ||||
|             Files.walkFileTree(root, new SimpleFileVisitor<Path>() { | ||||
|                 @Override | ||||
|                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||||
|                     if (file.toString().endsWith(".class")) { | ||||
|                         ByteArrayOutputStream bos = new ByteArrayOutputStream(); | ||||
|                         Files.copy(file, bos); | ||||
|                         pluginBuilder.loadClass(file.toString(), bos.toByteArray()); | ||||
|                     } | ||||
|                     Files.copy(file, tempDirectory, StandardCopyOption.REPLACE_EXISTING); | ||||
|                     return FileVisitResult.CONTINUE; | ||||
|                 } | ||||
|             }); | ||||
|             return pluginBuilder.build(); | ||||
|         } catch (IOException io) { | ||||
|             logger.error(String.format("Unable to extract: %s", pluginArchive.getFileName()), io); | ||||
|         } finally { | ||||
|             closeZipFileSystem(zip); | ||||
|         } | ||||
|  | ||||
|         return pluginBuilder.build(); | ||||
|     } | ||||
|  | ||||
|     private FileSystem createZipFileSystem() throws IOException { | ||||
|         final URI uri = URI.create("jar:file:" + pluginArchive.toUri().getPath()); | ||||
|         return FileSystems.newFileSystem(uri, new HashMap<String, Object>()); | ||||
|     } | ||||
|  | ||||
|     private void closeZipFileSystem(FileSystem zip) { | ||||
|         if (zip != null) { | ||||
|             try { | ||||
|                 zip.close(); | ||||
|             } catch (IOException e) { | ||||
|                 //ignore | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										39
									
								
								src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| package org.owasp.webgoat.plugins; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.nio.file.FileVisitResult; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.SimpleFileVisitor; | ||||
| import java.nio.file.attribute.BasicFileAttributes; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class PluginsLoader { | ||||
|  | ||||
|     private final Path path; | ||||
|  | ||||
|     public PluginsLoader(Path path) { | ||||
|         this.path = path; | ||||
|     } | ||||
|  | ||||
|     public List<Plugin> loadPlugins() { | ||||
|         final List<Plugin> plugins = new ArrayList<Plugin>(); | ||||
|         try { | ||||
|             Files.walkFileTree(path, new SimpleFileVisitor<Path>() { | ||||
|  | ||||
|                 @Override | ||||
|                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||||
|                     plugins.add(new PluginExtractor(file).extract()); | ||||
|                     return FileVisitResult.CONTINUE; | ||||
|                 } | ||||
|  | ||||
|             }); | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         return plugins; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @ -2,6 +2,13 @@ package org.owasp.webgoat.session; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.net.URI; | ||||
| import java.nio.file.FileVisitResult; | ||||
| import java.nio.file.FileVisitor; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.nio.file.attribute.BasicFileAttributes; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.Iterator; | ||||
| @ -9,41 +16,45 @@ import java.util.List; | ||||
| import java.util.Set; | ||||
| import java.util.LinkedList; | ||||
| import javax.servlet.ServletContext; | ||||
|  | ||||
| import org.owasp.webgoat.HammerHead; | ||||
| import org.owasp.webgoat.lessons.AbstractLesson; | ||||
| import org.owasp.webgoat.lessons.Category; | ||||
| import org.owasp.webgoat.plugins.Plugin; | ||||
| import org.owasp.webgoat.plugins.PluginExtractor; | ||||
| import org.owasp.webgoat.plugins.PluginsLoader; | ||||
| import org.owasp.webgoat.util.WebGoatI18N; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| /** | ||||
|  * ************************************************************************************************* | ||||
|  * | ||||
|  * | ||||
|  * <p/> | ||||
|  * <p/> | ||||
|  * This file is part of WebGoat, an Open Web Application Security Project | ||||
|  * utility. For details, please see http://www.owasp.org/ | ||||
|  * | ||||
|  * <p/> | ||||
|  * Copyright (c) 2002 - 20014 Bruce Mayhew | ||||
|  * | ||||
|  * <p/> | ||||
|  * This program is free software; you can redistribute it and/or modify it under | ||||
|  * the terms of the GNU General Public License as published by the Free Software | ||||
|  * Foundation; either version 2 of the License, or (at your option) any later | ||||
|  * version. | ||||
|  * | ||||
|  * <p/> | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||||
|  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||||
|  * details. | ||||
|  * | ||||
|  * <p/> | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||||
|  * Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
|  * | ||||
|  * <p/> | ||||
|  * Getting Source ============== | ||||
|  * | ||||
|  * <p/> | ||||
|  * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository | ||||
|  * for free software projects. | ||||
|  * | ||||
|  * <p/> | ||||
|  * For details, please see http://webgoat.github.io | ||||
|  * | ||||
|  * @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a> | ||||
| @ -73,7 +84,7 @@ public class Course { | ||||
|  | ||||
|     /** | ||||
|      * Take an absolute file and return the filename. | ||||
|      * | ||||
|      * <p/> | ||||
|      * Ex. /etc/password becomes password | ||||
|      * | ||||
|      * @param s | ||||
| @ -95,7 +106,7 @@ public class Course { | ||||
|  | ||||
|     /** | ||||
|      * Take a class name and return the equivalent file name | ||||
|      * | ||||
|      * <p/> | ||||
|      * Ex. org.owasp.webgoat becomes org/owasp/webgoat.java | ||||
|      * | ||||
|      * @param className | ||||
| @ -114,7 +125,7 @@ public class Course { | ||||
|      * Takes a file name and builds the class file name | ||||
|      * | ||||
|      * @param fileName Description of the Parameter | ||||
|      * @param path Description of the Parameter | ||||
|      * @param path     Description of the Parameter | ||||
|      * @return Description of the Return Value | ||||
|      */ | ||||
|     private static String getClassFile(String fileName, String path) { | ||||
| @ -237,7 +248,7 @@ public class Course { | ||||
|      * Gets the lessons attribute of the Course object | ||||
|      * | ||||
|      * @param category Description of the Parameter | ||||
|      * @param role Description of the Parameter | ||||
|      * @param role     Description of the Parameter | ||||
|      * @return The lessons value | ||||
|      */ | ||||
|     private List<AbstractLesson> getLessons(Category category, List roles) { | ||||
| @ -302,6 +313,34 @@ public class Course { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void loadLessionFromPlugin(ServletContext context) { | ||||
|         logger.debug("Loading plugins into cache"); | ||||
|         String path = context.getRealPath("plugin_lessons"); | ||||
|         if (path == null) { | ||||
|             logger.error("Plugins directory {} not found", path); | ||||
|             return; | ||||
|         } | ||||
|         List<Plugin> plugins = new PluginsLoader(Paths.get(path)).loadPlugins(); | ||||
|         for (Plugin plugin : plugins) { | ||||
|             try { | ||||
|                 Class<AbstractLesson> c = plugin.getLesson(); | ||||
|                 Object o = c.newInstance(); | ||||
|  | ||||
|                 AbstractLesson lesson = (AbstractLesson) o; | ||||
|                 lesson.setWebgoatContext(webgoatContext); | ||||
|  | ||||
|                 lesson.update(properties); | ||||
|  | ||||
|                 if (lesson.getHidden() == false) { | ||||
|                     lessons.add(lesson); | ||||
|                 } | ||||
|  | ||||
|             } catch (Exception e) { | ||||
|                 logger.error("Error in loadLessons: ", e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Instantiate all the lesson objects into a cache | ||||
|      * | ||||
| @ -361,18 +400,18 @@ public class Course { | ||||
|                     lesson.setSourceFileName(absoluteFile); | ||||
|                 } | ||||
|  | ||||
|                 if (absoluteFile.startsWith("/lesson_plans") && absoluteFile.endsWith(".html") | ||||
|                         && className.endsWith(fileName)) { | ||||
|                     logger.info("setting lesson plan file " + absoluteFile + " for lesson " | ||||
|                             + lesson.getClass().getName()); | ||||
|                 if (absoluteFile.startsWith("/lesson_plans") && absoluteFile.endsWith(".html") && className | ||||
|                     .endsWith(fileName)) { | ||||
|                     logger.info( | ||||
|                         "setting lesson plan file " + absoluteFile + " for lesson " + lesson.getClass().getName()); | ||||
|                     logger.info("fileName: " + fileName + " == className: " + className); | ||||
|                     String language = getLanguageFromFileName("/lesson_plans", absoluteFile); | ||||
|                     lesson.setLessonPlanFileName(language, absoluteFile); | ||||
|                 } | ||||
|                 if (absoluteFile.startsWith("/lesson_solutions") && absoluteFile.endsWith(".html") | ||||
|                         && className.endsWith(fileName)) { | ||||
|                     logger.info("setting lesson solution file " + absoluteFile + " for lesson " | ||||
|                             + lesson.getClass().getName()); | ||||
|                 if (absoluteFile.startsWith("/lesson_solutions") && absoluteFile.endsWith(".html") && className | ||||
|                     .endsWith(fileName)) { | ||||
|                     logger.info( | ||||
|                         "setting lesson solution file " + absoluteFile + " for lesson " + lesson.getClass().getName()); | ||||
|                     logger.info("fileName: " + fileName + " == className: " + className); | ||||
|                     lesson.setLessonSolutionFileName(absoluteFile); | ||||
|                 } | ||||
| @ -384,8 +423,8 @@ public class Course { | ||||
|      * Description of the Method | ||||
|      * | ||||
|      * @param webgoatContext | ||||
|      * @param path Description of the Parameter | ||||
|      * @param context Description of the Parameter | ||||
|      * @param path           Description of the Parameter | ||||
|      * @param context        Description of the Parameter | ||||
|      */ | ||||
|     public void loadCourses(WebgoatContext webgoatContext, ServletContext context, String path) { | ||||
|         logger.info("Loading courses: " + path); | ||||
| @ -393,5 +432,6 @@ public class Course { | ||||
|         loadFiles(context, path); | ||||
|         loadLessons(path); | ||||
|         loadResources(); | ||||
|         loadLessionFromPlugin(context); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user