From 720040d1f8de913afc60529026ffb971917949a4 Mon Sep 17 00:00:00 2001 From: nbaars Date: Mon, 29 Dec 2014 18:54:12 +0100 Subject: [PATCH] Loading classes from the plugin archive (cherry picked from commit 2adf04c) --- .../org/owasp/webgoat/plugins/Plugin.java | 61 +++++++++++++ .../webgoat/plugins/PluginClassLoader.java | 18 ++++ .../webgoat/plugins/PluginExtractor.java | 79 +++++++++++++++++ .../owasp/webgoat/plugins/PluginsLoader.java | 39 +++++++++ .../org/owasp/webgoat/session/Course.java | 86 ++++++++++++++----- 5 files changed, 260 insertions(+), 23 deletions(-) create mode 100644 src/main/java/org/owasp/webgoat/plugins/Plugin.java create mode 100644 src/main/java/org/owasp/webgoat/plugins/PluginClassLoader.java create mode 100644 src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java create mode 100644 src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java diff --git a/src/main/java/org/owasp/webgoat/plugins/Plugin.java b/src/main/java/org/owasp/webgoat/plugins/Plugin.java new file mode 100644 index 000000000..9d462ef6c --- /dev/null +++ b/src/main/java/org/owasp/webgoat/plugins/Plugin.java @@ -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 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 lesson, String lessonPlanHtml, String lessonSolutionHtml) { + this.lesson = lesson; + this.lessonPlanHtml = lessonPlanHtml; + this.lessonSolutionHtml = lessonSolutionHtml; + } + + + public String getLessonPlanHtml() { + return lessonPlanHtml; + } + + public Class getLesson() { + return lesson; + } + + public String getLessonSolutionHtml() { + return lessonSolutionHtml; + } +} diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginClassLoader.java b/src/main/java/org/owasp/webgoat/plugins/PluginClassLoader.java new file mode 100644 index 000000000..9b7201603 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/plugins/PluginClassLoader.java @@ -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); + } + + +} + diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java b/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java new file mode 100644 index 000000000..c3422b3fd --- /dev/null +++ b/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java @@ -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() { + @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()); + } + + private void closeZipFileSystem(FileSystem zip) { + if (zip != null) { + try { + zip.close(); + } catch (IOException e) { + //ignore + } + } + } + + +} diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java b/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java new file mode 100644 index 000000000..7cb1b9904 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java @@ -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 loadPlugins() { + final List plugins = new ArrayList(); + try { + Files.walkFileTree(path, new SimpleFileVisitor() { + + @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; + } + + +} diff --git a/src/main/java/org/owasp/webgoat/session/Course.java b/src/main/java/org/owasp/webgoat/session/Course.java index 9245db83c..3cbbe60d6 100644 --- a/src/main/java/org/owasp/webgoat/session/Course.java +++ b/src/main/java/org/owasp/webgoat/session/Course.java @@ -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; /** * ************************************************************************************************* - * - * + *

+ *

* This file is part of WebGoat, an Open Web Application Security Project * utility. For details, please see http://www.owasp.org/ - * + *

* Copyright (c) 2002 - 20014 Bruce Mayhew - * + *

* 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. - * + *

* 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. - * + *

* 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. - * + *

* Getting Source ============== - * + *

* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. - * + *

* For details, please see http://webgoat.github.io * * @author Bruce Mayhew WebGoat @@ -73,7 +84,7 @@ public class Course { /** * Take an absolute file and return the filename. - * + *

* Ex. /etc/password becomes password * * @param s @@ -95,7 +106,7 @@ public class Course { /** * Take a class name and return the equivalent file name - * + *

* 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 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 plugins = new PluginsLoader(Paths.get(path)).loadPlugins(); + for (Plugin plugin : plugins) { + try { + Class 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); } }