diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java index 5d501541e..cdab91c84 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java @@ -1,15 +1,13 @@ package org.owasp.webgoat.plugins; import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import org.owasp.webgoat.classloader.PluginClassLoader; import org.owasp.webgoat.lessons.AbstractLesson; -import org.owasp.webgoat.util.LabelProvider; +import org.springframework.util.StringUtils; import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.HashMap; @@ -29,7 +27,6 @@ public class Plugin { private static final String NAME_LESSON_SOLUTION_DIRECTORY = "lessonSolutions"; private static final String NAME_LESSON_PLANS_DIRECTORY = "lessonPlans"; - private final Path pluginDirectory; private Class lesson; private Map solutionLanguageFiles = new HashMap<>(); @@ -37,36 +34,14 @@ public class Plugin { private List pluginFiles = Lists.newArrayList(); private File lessonSourceFile; - /** - *

Constructor for Plugin.

- * - * @param pluginDirectory a {@link java.nio.file.Path} object. - */ - public Plugin(Path pluginDirectory) { - Preconditions.checkNotNull(pluginDirectory, "plugin directory cannot be null"); - Preconditions.checkArgument(Files.exists(pluginDirectory), "directory %s does not exists", pluginDirectory); - this.pluginDirectory = pluginDirectory; - } - - /** - *

Constructor for Plugin.

- * - * @param pluginDirectory a {@link java.nio.file.Path} object. - * @param classes a {@link java.util.List} object. - */ - public Plugin(Path pluginDirectory, List classes) { - this(pluginDirectory); - findLesson(classes); - } - - private void findLesson(List classes) { + public void findLesson(List classes) { for (String clazzName : classes) { findLesson(clazzName); } } private void findLesson(String name) { - String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", ""); + String realClassName = StringUtils.trimLeadingCharacter(name, '/').replaceAll("/", ".").replaceAll(".class", ""); PluginClassLoader cl = (PluginClassLoader) Thread.currentThread().getContextClassLoader(); try { @@ -76,44 +51,23 @@ public class Plugin { this.lesson = clazz; } } catch (ClassNotFoundException ce) { - throw new PluginLoadingFailure("Class " + realClassName + " listed in jar but unable to load the class.", - ce); + throw new PluginLoadingFailure("Class " + realClassName + " listed in jar but unable to load the class.", ce); } } - /** - *

loadProperties.

- * - * @param properties a {@link java.util.List} object. - */ - public void loadProperties(List properties) { - for (Path propertyFile : properties) { - LabelProvider.updatePluginResources(propertyFile); - LabelProvider.refresh(); + public void loadFiles(Path file, boolean reload) { + if (fileEndsWith(file, ".html") && hasParentDirectoryWithName(file, NAME_LESSON_SOLUTION_DIRECTORY)) { + solutionLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); + } + if (fileEndsWith(file, ".html") && hasParentDirectoryWithName(file, NAME_LESSON_PLANS_DIRECTORY)) { + lessonPlansLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); + } + if (fileEndsWith(file, ".java")) { + lessonSourceFile = file.toFile(); } - } - /** - *

loadFiles.

- * - * @param files a {@link java.util.List} object. - * @param reload a boolean. - */ - public void loadFiles(List files, boolean reload) { - for (Path file : files) { - if (fileEndsWith(file, ".html") && hasParentDirectoryWithName(file, NAME_LESSON_SOLUTION_DIRECTORY)) { - solutionLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); - } - if (fileEndsWith(file, ".html") && hasParentDirectoryWithName(file, NAME_LESSON_PLANS_DIRECTORY)) { - lessonPlansLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); - } - if (fileEndsWith(file, ".java")) { - lessonSourceFile = file.toFile(); - } - - if (fileEndsWith(file, ".css", ".jsp", ".js")) { - pluginFiles.add(file.toFile()); - } + if (fileEndsWith(file, ".css", ".jsp", ".js")) { + pluginFiles.add(file.toFile()); } } @@ -148,8 +102,6 @@ public class Plugin { .format("%s/plugin/%s/images/", pluginTarget.getFileName().toString(), this.lesson.getSimpleName()); replaceInFiles(s, r, pluginFiles); replaceInFiles(s, r, Arrays.asList(lessonSourceFile)); - - } catch (IOException e) { throw new PluginLoadingFailure("Unable to rewrite the paths in the solutions", e); } @@ -208,4 +160,5 @@ public class Plugin { return this.lessonPlansLanguageFiles; } + } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java index 5b9c03c14..b55b9f41b 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java @@ -25,7 +25,7 @@ public class PluginBackgroundLoader implements ServletContextListener { String targetPath = event.getServletContext().getRealPath("plugin_extracted"); scheduler = Executors.newSingleThreadScheduledExecutor(); - scheduler.scheduleAtFixedRate(new PluginsLoader(Paths.get(pluginPath), Paths.get(targetPath)), 0, 5, TimeUnit.MINUTES); + scheduler.scheduleAtFixedRate(new PluginsLoader(Paths.get(pluginPath), Paths.get(targetPath)), 10, 5, TimeUnit.MINUTES); } /** {@inheritDoc} */ diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java index 0cd62840d..ae5052d76 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java @@ -1,26 +1,18 @@ package org.owasp.webgoat.plugins; import com.google.common.collect.Lists; +import com.google.common.io.Files; +import org.apache.commons.fileupload.util.Streams; +import java.io.File; +import java.io.FileOutputStream; 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.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Enumeration; import java.util.List; - -import static java.lang.String.format; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static org.owasp.webgoat.plugins.PluginFileUtils.createDirsIfNotExists; -import static org.owasp.webgoat.plugins.PluginFileUtils.fileEndsWith; -import static org.owasp.webgoat.plugins.PluginFileUtils.hasParentDirectoryWithName; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /** * Extract the jar file and place them in the system temp directory in the folder webgoat and collect the files @@ -30,49 +22,76 @@ import static org.owasp.webgoat.plugins.PluginFileUtils.hasParentDirectoryWithNa */ public class PluginExtractor { - private static final String NAME_LESSON_I18N_DIRECTORY = "i18n"; - private final Path pluginArchive; private final List classes = Lists.newArrayList(); private final List files = new ArrayList<>(); - private final List properties = new ArrayList<>(); - /** - *

Constructor for PluginExtractor.

- * - * @param pluginArchive a {@link java.nio.file.Path} object. - */ - public PluginExtractor(Path pluginArchive) { - this.pluginArchive = pluginArchive; - } + public Plugin extractJarFile(final File archive, final File targetDirectory) throws IOException { + ZipFile zipFile = new ZipFile(archive); + Plugin plugin = new Plugin(); + try { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + final ZipEntry zipEntry = entries.nextElement(); + if (shouldProcessFile(zipEntry)) { + boolean processed = processClassFile(zipEntry); - /** - *

extract.

- * + if (!processed) { * @param target a {@link java.nio.file.Path} object. */ public void extract(final Path target) { try (FileSystem zip = createZipFileSystem()) { - final Path root = zip.getPath("/"); - Files.walkFileTree(root, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (file.toString().endsWith(".class")) { - classes.add(file.toString()); + processed = processPropertyFile(zipFile, zipEntry, targetDirectory); } - if (fileEndsWith(file, ".properties") && hasParentDirectoryWithName(file, - NAME_LESSON_I18N_DIRECTORY)) { - properties.add(Files - .copy(file, createDirsIfNotExists(Paths.get(target.toString(), file.toString())), - REPLACE_EXISTING)); + if (!processed) { + processFile(plugin, zipFile, zipEntry, targetDirectory); } - files.add(Files.copy(file, createDirsIfNotExists(Paths.get(target.toString(), file.toString())), - REPLACE_EXISTING)); - return FileVisitResult.CONTINUE; } - }); - } catch (Exception e) { - new PluginLoadingFailure(format("Unable to extract: %s", pluginArchive.getFileName()), e); + } + } finally { + plugin.findLesson(this.classes); + if (plugin.getLesson().isPresent()) { + plugin.rewritePaths(targetDirectory.toPath()); + } + zipFile.close(); } + return plugin; + } + + private void processFile(Plugin plugin, ZipFile zipFile, ZipEntry zipEntry, File targetDirectory) + throws IOException { + final File targetFile = new File(targetDirectory, zipEntry.getName()); + copyFile(zipFile, zipEntry, targetFile, false); + plugin.loadFiles(targetFile.toPath(), true); + } + + private boolean processPropertyFile(ZipFile zipFile, ZipEntry zipEntry, File targetDirectory) + throws IOException { + if (zipEntry.getName().endsWith(".properties")) { + final File targetFile = new File(targetDirectory, zipEntry.getName()); + copyFile(zipFile, zipEntry, targetFile, true); + return true; + } + return false; + } + + private boolean processClassFile(ZipEntry zipEntry) { + if (zipEntry.getName().endsWith(".class")) { + classes.add(zipEntry.getName()); + return true; + } + return false; + } + + private boolean shouldProcessFile(ZipEntry zipEntry) { + return !zipEntry.isDirectory() && !zipEntry.getName().startsWith("META-INF"); + } + + private File copyFile(ZipFile zipFile, ZipEntry zipEntry, File targetFile, boolean append) throws IOException { + Files.createParentDirs(targetFile); + try (FileOutputStream fos = new FileOutputStream(targetFile, append)) { + Streams.copy(zipFile.getInputStream(zipEntry), fos, true); + } + return targetFile; } /** @@ -92,18 +111,4 @@ public class PluginExtractor { public List getFiles() { return this.files; } - - /** - *

Getter for the field properties.

- * - * @return a {@link java.util.List} object. - */ - public List getProperties() { - return this.properties; - } - - private FileSystem createZipFileSystem() throws Exception { - final URI uri = URI.create("jar:file:" + pluginArchive.toUri().getPath()); - return FileSystems.newFileSystem(uri, new HashMap()); - } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java index 798fd7af0..1d0ccdbc6 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java @@ -2,17 +2,15 @@ package org.owasp.webgoat.plugins; import com.google.common.base.Preconditions; +import org.apache.commons.io.IOUtils; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; -import java.nio.file.DirectoryStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.OpenOption; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** *

PluginFileUtils class.

@@ -79,24 +77,6 @@ public class PluginFileUtils { return p; } - /** - *

getFilesInDirectory.

- * - * @param directory a {@link java.nio.file.Path} object. - * @return a {@link java.util.List} object. - * @throws java.io.IOException if any. - */ - public static List getFilesInDirectory(Path directory) throws IOException { - List files = new ArrayList<>(); - DirectoryStream dirStream; - dirStream = Files.newDirectoryStream(directory); - for (Path entry : dirStream) { - files.add(entry); - } - dirStream.close(); - return files; - } - /** *

replaceInFiles.

* @@ -111,7 +91,7 @@ public class PluginFileUtils { Preconditions.checkNotNull(files); for (File file : files) { - replaceInFile(replace, with, Paths.get(file.toURI())); + replaceInFile(replace, with, file); } } @@ -123,31 +103,16 @@ public class PluginFileUtils { * @param file a {@link java.nio.file.Path} object. * @throws java.io.IOException if any. */ - public static void replaceInFile(String replace, String with, Path file) throws IOException { + public static void replaceInFile(String replace, String with, File file) throws IOException { Preconditions.checkNotNull(replace); Preconditions.checkNotNull(with); Preconditions.checkNotNull(file); - byte[] fileAsBytes = Files.readAllBytes(file); - String fileAsString = new String(fileAsBytes); - fileAsString = fileAsString.replaceAll(replace, with); - Files.write(file, fileAsString.getBytes()); - } - - /** - *

writeFile.

- * - * @param targetFile a {@link java.nio.file.Path} object. - * @param bytes an array of byte. - * @param options a {@link java.nio.file.OpenOption} object. - * @throws java.io.IOException if any. - */ - public static void writeFile(Path targetFile, byte[] bytes, OpenOption... options) throws IOException { - createDirsIfNotExists(targetFile.getParent()); - if (!Files.exists(targetFile)) { - Files.createFile(targetFile); + String fileAsString = ""; + try (FileInputStream fis = new FileInputStream(file);) { + fileAsString = IOUtils.toString(fis, StandardCharsets.UTF_8.name()); + fileAsString = fileAsString.replaceAll(replace, with); } - Files.write(targetFile, bytes, options); + Files.write(file.toPath(), fileAsString.getBytes()); } - } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java index 9f6519f58..bc1f1cf7b 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java @@ -6,13 +6,13 @@ import org.owasp.webgoat.classloader.PluginClassLoader; import org.owasp.webgoat.util.LabelProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.ResourceUtils; import java.io.IOException; import java.net.URL; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; @@ -88,39 +88,33 @@ public class PluginsLoader implements Runnable { private List processPlugins(List jars, boolean reload) throws Exception { final List plugins = Lists.newArrayList(); - final ExecutorService executorService = Executors.newFixedThreadPool(20); - final CompletionService completionService = new ExecutorCompletionService<>(executorService); - final List> callables = extractJars(jars); + final ExecutorService executorService = Executors.newFixedThreadPool(10); + final CompletionService completionService = new ExecutorCompletionService<>(executorService); + final List> callables = extractJars(jars); - for (Callable s : callables) { + for (Callable s : callables) { completionService.submit(s); } int n = callables.size(); for (int i = 0; i < n; i++) { - PluginExtractor extractor = completionService.take().get(); - Plugin plugin = new Plugin(pluginTarget, extractor.getClasses()); + Plugin plugin = completionService.take().get(); if (plugin.getLesson().isPresent()) { - PluginFileUtils.createDirsIfNotExists(pluginTarget); - plugin.loadFiles(extractor.getFiles(), reload); - plugin.loadProperties(extractor.getProperties()); - plugin.rewritePaths(pluginTarget); plugins.add(plugin); } } - LabelProvider.refresh(); + LabelProvider.updatePluginResources(pluginTarget.resolve("plugin/i18n/WebGoatLabels.properties")); return plugins; } - private List> extractJars(List jars) { - List> extractorCallables = Lists.newArrayList(); + private List> extractJars(List jars) { + List> extractorCallables = Lists.newArrayList(); for (final URL jar : jars) { - extractorCallables.add(new Callable() { + extractorCallables.add(new Callable() { @Override - public PluginExtractor call() throws Exception { - PluginExtractor extractor = new PluginExtractor(Paths.get(jar.toURI())); - extractor.extract(pluginTarget); - return extractor; + public Plugin call() throws Exception { + PluginExtractor extractor = new PluginExtractor(); + return extractor.extractJarFile(ResourceUtils.getFile(jar), pluginTarget.toFile()); } }); } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/util/LabelProvider.java b/webgoat-container/src/main/java/org/owasp/webgoat/util/LabelProvider.java index 7a9f11efa..8d5926f62 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/util/LabelProvider.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/util/LabelProvider.java @@ -6,7 +6,6 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.UrlResource; import org.springframework.stereotype.Component; -import org.springframework.util.DefaultPropertiesPersister; import javax.inject.Singleton; import java.net.MalformedURLException; @@ -67,9 +66,6 @@ public class LabelProvider { labels.setFallbackToSystemLocale(false); labels.setUseCodeAsDefaultMessage(true); pluginLabels.setParentMessageSource(labels); - pluginLabels.setPropertiesPersister(new DefaultPropertiesPersister() { - - }); } /** @@ -96,7 +92,6 @@ public class LabelProvider { return Thread.currentThread().getContextClassLoader(); } }); - } /** *

refresh.

diff --git a/webgoat-container/src/test/java/org/owasp/webgoat/plugins/WebGoatIT.java b/webgoat-container/src/test/java/org/owasp/webgoat/plugins/WebGoatIT.java index c5c62010b..391983806 100644 --- a/webgoat-container/src/test/java/org/owasp/webgoat/plugins/WebGoatIT.java +++ b/webgoat-container/src/test/java/org/owasp/webgoat/plugins/WebGoatIT.java @@ -232,7 +232,6 @@ public class WebGoatIT implements SauceOnDemandSessionIdProvider { @Test public void testStartMvc() { - driver.get(baseWebGoatUrl + "/start.mvc"); WebDriverWait wait = new WebDriverWait(driver, 15); // wait for a maximum of 15 seconds diff --git a/webgoat-container/src/test/java/org/owasp/webgoat/util/LabelProviderTest.java b/webgoat-container/src/test/java/org/owasp/webgoat/util/LabelProviderTest.java index 5d27873db..ab6ef5ec6 100644 --- a/webgoat-container/src/test/java/org/owasp/webgoat/util/LabelProviderTest.java +++ b/webgoat-container/src/test/java/org/owasp/webgoat/util/LabelProviderTest.java @@ -22,7 +22,6 @@ public class LabelProviderTest { public void loadingPluginLabels() throws IOException { LabelProvider labelProvider = new LabelProvider(); labelProvider.updatePluginResources(new ClassPathResource("log4j.properties").getFile().toPath()); - LabelProvider.refresh(); assertThat(labelProvider.get(Locale.ENGLISH, "LessonCompleted"), CoreMatchers.equalTo( "Congratulations. You have successfully completed this lesson.")); assertThat(labelProvider.get(Locale.ENGLISH, "log4j.appender.CONSOLE.Target"), CoreMatchers.equalTo(