From 5d2019fb18fcec03c8dcca5368797818cbe4f2da Mon Sep 17 00:00:00 2001 From: nbaars Date: Thu, 15 Jan 2015 21:38:11 +0100 Subject: [PATCH] making the images work as well --- .../org/owasp/webgoat/plugins/Plugin.java | 16 +++ .../plugins/PluginBackgroundLoader.java | 3 +- .../webgoat/plugins/PluginExtractor.java | 17 +-- .../owasp/webgoat/plugins/PluginsLoader.java | 16 ++- .../org/owasp/webgoat/session/Course.java | 122 +----------------- .../owasp/webgoat/session/WebgoatContext.java | 10 -- .../org/owasp/webgoat/util/LabelProvider.java | 4 - .../plugin_lessons/SqlStringInjection-1.0.jar | Bin 442353 -> 442341 bytes 8 files changed, 38 insertions(+), 150 deletions(-) diff --git a/src/main/java/org/owasp/webgoat/plugins/Plugin.java b/src/main/java/org/owasp/webgoat/plugins/Plugin.java index e56c67e50..77827910f 100644 --- a/src/main/java/org/owasp/webgoat/plugins/Plugin.java +++ b/src/main/java/org/owasp/webgoat/plugins/Plugin.java @@ -10,6 +10,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.HashMap; import java.util.List; @@ -112,6 +113,21 @@ public class Plugin { } } + public void rewritePaths(Path pluginTarget) { + try { + for (Map.Entry html : solutionLanguageFiles.entrySet()) { + byte[] htmlFileAsBytes = Files.readAllBytes(Paths.get(html.getValue().toURI())); + String htmlFile = new String(htmlFileAsBytes); + htmlFile = htmlFile.replaceAll(this.lesson.getSimpleName() + "_files", pluginTarget.getFileName().toString() + "/lessons/plugin/SqlStringInjection/lessonSolutions/en/" + this.lesson.getSimpleName() + "_files"); + Files.write(Paths.get(html.getValue().toURI()), htmlFile.getBytes(), StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + } + } catch (IOException e) { + throw new PluginLoadingFailure("Unable to rewrite the paths in the solutions", e); + } + } + + public Class getLesson() { return lesson; } diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java b/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java index 6649fbcbb..e2d59002f 100644 --- a/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java +++ b/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java @@ -16,8 +16,9 @@ public class PluginBackgroundLoader implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent event) { String pluginPath = event.getServletContext().getRealPath("plugin_lessons"); + String targetPath = event.getServletContext().getRealPath("plugin_extracted"); scheduler = Executors.newSingleThreadScheduledExecutor(); - scheduler.scheduleAtFixedRate(new PluginsLoader(Paths.get(pluginPath)), 0, 5, TimeUnit.MINUTES); + scheduler.scheduleAtFixedRate(new PluginsLoader(Paths.get(pluginPath), Paths.get(targetPath)), 0, 5, TimeUnit.MINUTES); } @Override diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java b/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java index 36936fb24..a9a2f1043 100644 --- a/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java +++ b/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java @@ -26,22 +26,15 @@ import static org.owasp.webgoat.plugins.PluginFileUtils.createDirsIfNotExists; */ public class PluginExtractor { - private static final String DIRECTORY = "webgoat"; private final Path pluginArchive; - private final Map classes = new HashMap(); + private final Map classes = new HashMap<>(); private final List files = new ArrayList<>(); - private Path baseDirectory; public PluginExtractor(Path pluginArchive) { this.pluginArchive = pluginArchive; - try { - baseDirectory = createDirsIfNotExists(Paths.get(System.getProperty("java.io.tmpdir"), DIRECTORY)); - } catch (IOException io) { - new Plugin.PluginLoadingFailure(format("Unable to create base directory: {}", pluginArchive.getFileName()), io); - } } - public void extract() { + public void extract(final Path target) { try (FileSystem zip = createZipFileSystem()) { final Path root = zip.getPath("/"); Files.walkFileTree(root, new SimpleFileVisitor() { @@ -52,7 +45,7 @@ public class PluginExtractor { Files.copy(file, bos); classes.put(file.toString(), bos.toByteArray()); } - files.add(Files.copy(file, createDirsIfNotExists(Paths.get(baseDirectory.toString(), file.toString())), REPLACE_EXISTING)); + files.add(Files.copy(file, createDirsIfNotExists(Paths.get(target.toString(), file.toString())), REPLACE_EXISTING)); return FileVisitResult.CONTINUE; } }); @@ -69,10 +62,6 @@ public class PluginExtractor { return this.files; } - public Path getBaseDirectory() { - return this.baseDirectory; - } - private FileSystem createZipFileSystem() throws IOException { final URI uri = URI.create("jar:file:" + pluginArchive.toUri().getPath()); return FileSystems.newFileSystem(uri, new HashMap()); diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java b/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java index 9feceb4af..87ffc3669 100644 --- a/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java +++ b/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java @@ -15,25 +15,29 @@ import java.util.List; public class PluginsLoader implements Runnable { private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final Path path; + private final Path pluginSource; + private Path pluginTarget; - public PluginsLoader(Path path) { - this.path = path; + public PluginsLoader(Path pluginSource, Path pluginTarget) { + this.pluginSource = pluginSource; + this.pluginTarget = pluginTarget; } public List loadPlugins(final boolean reload) { final List plugins = new ArrayList(); try { - Files.walkFileTree(path, new SimpleFileVisitor() { + Files.walkFileTree(pluginSource, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { try { + PluginFileUtils.createDirsIfNotExists(pluginTarget); PluginExtractor extractor = new PluginExtractor(file); - extractor.extract(); - Plugin plugin = new Plugin(extractor.getBaseDirectory()); + extractor.extract(pluginTarget); + Plugin plugin = new Plugin(pluginTarget); plugin.loadClasses(extractor.getClasses()); plugin.loadFiles(extractor.getFiles(), reload); + plugin.rewritePaths(pluginTarget); plugins.add(plugin); } catch (Plugin.PluginLoadingFailure e) { logger.error("Unable to load plugin, continue loading others..."); diff --git a/src/main/java/org/owasp/webgoat/session/Course.java b/src/main/java/org/owasp/webgoat/session/Course.java index db60cb9d3..6f4abd4e5 100644 --- a/src/main/java/org/owasp/webgoat/session/Course.java +++ b/src/main/java/org/owasp/webgoat/session/Course.java @@ -281,42 +281,17 @@ public class Course { return null; } - /** - * Load all of the filenames into a temporary cache - * - * @param context - * @param path - */ - private void loadFiles(ServletContext context, String path) { - logger.debug("Loading files into cache, path: " + path); - Set resourcePaths = context.getResourcePaths(path); - if (resourcePaths == null) { - logger.error("Unable to load file cache for courses, this is probably a bug or configuration issue"); - return; - } - Iterator itr = resourcePaths.iterator(); - - while (itr.hasNext()) { - String file = (String) itr.next(); - - if (file.length() != 1 && file.endsWith("/")) { - loadFiles(context, file); - } else { - files.add(file); - } - } - } - private void loadLessionFromPlugin(ServletContext context) { + context.getContextPath(); logger.debug("Loading plugins into cache"); - String path = context.getRealPath("plugin_lessons"); - if (path == null) { - logger.error("Plugins directory {} not found", path); + String pluginPath = context.getRealPath("plugin_lessons"); + String targetPath = context.getRealPath("plugin_extracted"); + if (pluginPath == null) { + logger.error("Plugins directory {} not found", pluginPath); return; } - Path pluginDirectory = Paths.get(path); - webgoatContext.setPluginDirectory(pluginDirectory); - List plugins = new PluginsLoader(pluginDirectory).loadPlugins(false); + Path pluginDirectory = Paths.get(pluginPath); + List plugins = new PluginsLoader(Paths.get(pluginPath), Paths.get(targetPath)).loadPlugins(false); for (Plugin plugin : plugins) { try { Class c = plugin.getLesson(); @@ -341,85 +316,6 @@ public class Course { } } - /** - * Instantiate all the lesson objects into a cache - * - * @deprecated should be removed if everything is loaded with plugins - * @param path - */ - private void loadLessons(String path) { - for (String file : files) { - String className = getClassFile(file, path); - - if (className != null && !className.endsWith("_i")) { - try { - Class c = Class.forName(className); - Object o = c.newInstance(); - - if (o instanceof AbstractLesson) { - 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); - } - } - } - } - - private String getLanguageFromFileName(String first, String absoluteFile) { - int p1 = absoluteFile.indexOf("/", absoluteFile.indexOf(first) + 1); - int p2 = absoluteFile.indexOf("/", p1 + 1); - String langStr = absoluteFile.substring(p1 + 1, p2); - - return langStr; - } - - /** - * For each lesson, set the source file and lesson file - */ - private void loadResources() { - for (AbstractLesson lesson : lessons) { - logger.info("Loading resources for lesson -> " + lesson.getName()); - String className = lesson.getClass().getName(); - String classFile = getSourceFile(className); - logger.info("Lesson classname: " + className); - logger.info("Lesson java file: " + classFile); - - for (String absoluteFile : files) { - String fileName = getFileName(absoluteFile); - //logger.debug("Course: looking at file: " + absoluteFile); - - if (absoluteFile.endsWith(classFile)) { - logger.info("Set source file for " + classFile); - 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()); - 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()); - logger.info("fileName: " + fileName + " == className: " + className); - lesson.setLessonSolutionFileName(absoluteFile); - } - } - } - } - /** * Description of the Method * @@ -431,10 +327,6 @@ public class Course { logger.info("Loading courses: " + path); this.webgoatContext = webgoatContext; loadLessionFromPlugin(context); - loadFiles(context, path); - //loadLessons(path); - loadResources(); - } } diff --git a/src/main/java/org/owasp/webgoat/session/WebgoatContext.java b/src/main/java/org/owasp/webgoat/session/WebgoatContext.java index 9cb27494f..8c9ce4549 100644 --- a/src/main/java/org/owasp/webgoat/session/WebgoatContext.java +++ b/src/main/java/org/owasp/webgoat/session/WebgoatContext.java @@ -4,7 +4,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServlet; -import java.nio.file.Path; public class WebgoatContext { @@ -216,13 +215,4 @@ public class WebgoatContext { public String getDefaultLanguage() { return defaultLanguage; } - - public Path getPluginDirectory() { - return pluginDirectory; - } - - public void setPluginDirectory(Path pluginDirectory) { - this.pluginDirectory = pluginDirectory; - } - } diff --git a/src/main/java/org/owasp/webgoat/util/LabelProvider.java b/src/main/java/org/owasp/webgoat/util/LabelProvider.java index d788e1ede..e2861096d 100644 --- a/src/main/java/org/owasp/webgoat/util/LabelProvider.java +++ b/src/main/java/org/owasp/webgoat/util/LabelProvider.java @@ -55,10 +55,6 @@ public class LabelProvider return labels.get(locale).getString(strName); } - public void addLabels() { - - } - private class WebGoatResourceBundleController extends ResourceBundle.Control { private final Locale fallbackLocale = new Locale(DEFAULT_LANGUAGE); diff --git a/src/main/webapp/plugin_lessons/SqlStringInjection-1.0.jar b/src/main/webapp/plugin_lessons/SqlStringInjection-1.0.jar index 680e478ebb99396071a08bee7ef894aef0bf5da7..d032bb12c83105b20d667668f6eeee58ec384881 100644 GIT binary patch delta 6867 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