diff --git a/pom.xml b/pom.xml
index 3718d9be3..02f8f6df3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
org.owasp.webgoat
WebGoat-Container
- jar
+ war
6.1.0
@@ -38,15 +38,17 @@
org.apache.maven.plugins
maven-compiler-plugin
- 1.6
- 1.6
+ 1.7
+ 1.7
ISO-8859-1
org.apache.maven.plugins
maven-war-plugin
+
+ true
true
@@ -59,10 +61,28 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ create-jar
+ compile
+
+ jar
+
+
+
+
org.apache.tomcat.maven
tomcat7-maven-plugin
2.1
+
+ http://localhost:8080/manager
+ /WebGoat
+ exec
+
tomcat-run
@@ -70,11 +90,6 @@
exec-war-only
package
-
- http://localhost:8080/manager
- /WebGoat
- exec
-
diff --git a/src/main/java/org/owasp/webgoat/lessons/AbstractLesson.java b/src/main/java/org/owasp/webgoat/lessons/AbstractLesson.java
index e689f7d1f..8f3ed38e6 100644
--- a/src/main/java/org/owasp/webgoat/lessons/AbstractLesson.java
+++ b/src/main/java/org/owasp/webgoat/lessons/AbstractLesson.java
@@ -385,7 +385,7 @@ public abstract class AbstractLesson extends Screen implements Comparable"
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..77827910f
--- /dev/null
+++ b/src/main/java/org/owasp/webgoat/plugins/Plugin.java
@@ -0,0 +1,146 @@
+package org.owasp.webgoat.plugins;
+
+import org.owasp.webgoat.lessons.AbstractLesson;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+import java.io.ByteArrayOutputStream;
+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;
+import java.util.Map;
+
+import static org.owasp.webgoat.plugins.PluginFileUtils.fileEndsWith;
+import static org.owasp.webgoat.plugins.PluginFileUtils.hasParentDirectoryWithName;
+
+public class Plugin {
+
+ private static final String NAME_LESSON_SOLUTION_DIRECTORY = "lessonSolutions";
+ private static final String NAME_LESSON_PLANS_DIRECTORY = "lessonPlans";
+ private static final String NAME_LESSON_I18N_DIRECTORY = "i18n";
+ private final Logger logger = LoggerFactory.getLogger(Plugin.class);
+ private final Path pluginDirectory;
+
+ private Class lesson;
+ private Map solutionLanguageFiles = new HashMap<>();
+ private Map lessonPlansLanguageFiles = new HashMap<>();
+ private File lessonSourceFile;
+
+ public static class PluginLoadingFailure extends RuntimeException {
+
+ public PluginLoadingFailure(String message) {
+ super(message);
+ }
+
+ public PluginLoadingFailure(String message, Exception e) {
+ super(message, e);
+ }
+ }
+
+ public Plugin(Path pluginDirectory) {
+ this.pluginDirectory = pluginDirectory;
+ }
+
+ public void loadClasses(Map classes) {
+ for (Map.Entry clazz : classes.entrySet()) {
+ loadClass(clazz.getKey(), clazz.getValue());
+ }
+ if (lesson == null) {
+ throw new PluginLoadingFailure(String
+ .format("Lesson class not found, following classes were detected in the plugin: %s",
+ StringUtils.collectionToCommaDelimitedString(classes.keySet())));
+ }
+ }
+
+ private void loadClass(String name, byte[] classFile) {
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader, classFile);
+ try {
+ String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", "");
+ Class clazz = pluginClassLoader.loadClass(realClassName);
+ if (AbstractLesson.class.isAssignableFrom(clazz)) {
+ this.lesson = clazz;
+ }
+ } catch (ClassNotFoundException e) {
+ logger.error("Unable to load class {}", name);
+ }
+ }
+
+ 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, ".properties") && hasParentDirectoryWithName(file, NAME_LESSON_I18N_DIRECTORY)) {
+ copyProperties(reload, file);
+ }
+ }
+ }
+
+ private void copyProperties(boolean reload, Path file) {
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ Files.copy(file, bos);
+ Path propertiesPath = createPropertiesDirectory();
+ ResourceBundleClassLoader.setPropertiesPath(propertiesPath);
+ if ( reload ) {
+ Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
+ } else {
+ Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ }
+ } catch (IOException io) {
+ throw new PluginLoadingFailure("Property file detected, but unable to copy the properties", io);
+ }
+ }
+
+ private Path createPropertiesDirectory() throws IOException {
+ if (Files.exists(pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY))) {
+ return pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY);
+ } else {
+ return Files.createDirectory(pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY));
+ }
+ }
+
+ 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;
+ }
+
+ public Map getLessonSolutions() {
+ return this.solutionLanguageFiles;
+ }
+
+ public File getLessonSource() {
+ return lessonSourceFile;
+ }
+
+ public Map getLessonPlans() {
+ return this.lessonPlansLanguageFiles;
+ }
+}
diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java b/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java
new file mode 100644
index 000000000..e2d59002f
--- /dev/null
+++ b/src/main/java/org/owasp/webgoat/plugins/PluginBackgroundLoader.java
@@ -0,0 +1,28 @@
+package org.owasp.webgoat.plugins;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+import java.nio.file.Paths;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@WebListener
+public class PluginBackgroundLoader implements ServletContextListener {
+
+ private ScheduledExecutorService scheduler;
+
+ @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), Paths.get(targetPath)), 0, 5, TimeUnit.MINUTES);
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent event) {
+ scheduler.shutdownNow();
+ }
+}
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..98b101b8b
--- /dev/null
+++ b/src/main/java/org/owasp/webgoat/plugins/PluginClassLoader.java
@@ -0,0 +1,17 @@
+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..a9a2f1043
--- /dev/null
+++ b/src/main/java/org/owasp/webgoat/plugins/PluginExtractor.java
@@ -0,0 +1,71 @@
+package org.owasp.webgoat.plugins;
+
+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.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.lang.String.format;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.owasp.webgoat.plugins.PluginFileUtils.createDirsIfNotExists;
+
+/**
+ * Extract the wpf file and place them in the system temp directory in the folder webgoat and collect the files
+ * and classes.
+ */
+public class PluginExtractor {
+
+ private final Path pluginArchive;
+ private final Map classes = new HashMap<>();
+ private final List files = new ArrayList<>();
+
+ public PluginExtractor(Path pluginArchive) {
+ this.pluginArchive = pluginArchive;
+ }
+
+ 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")) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ Files.copy(file, bos);
+ classes.put(file.toString(), bos.toByteArray());
+ }
+ files.add(Files.copy(file, createDirsIfNotExists(Paths.get(target.toString(), file.toString())), REPLACE_EXISTING));
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException io) {
+ new Plugin.PluginLoadingFailure(format("Unable to extract: %s", pluginArchive.getFileName()), io);
+ }
+ }
+
+ public Map getClasses() {
+ return this.classes;
+ }
+
+ public List getFiles() {
+ return this.files;
+ }
+
+ 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/PluginFileUtils.java b/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java
new file mode 100644
index 000000000..c0390539c
--- /dev/null
+++ b/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java
@@ -0,0 +1,31 @@
+package org.owasp.webgoat.plugins;
+
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class PluginFileUtils {
+
+ public static boolean fileEndsWith(Path p, String s) {
+ return p.getFileName().toString().endsWith(s);
+ }
+
+ public static boolean hasParentDirectoryWithName(Path p, String s) {
+ if (p == null || p.getParent() == null || p.getRoot().equals(p.getParent())) {
+ return false;
+ }
+ if (p.getParent().getFileName().toString().equals(s)) {
+ return true;
+ }
+ return hasParentDirectoryWithName(p.getParent(), s);
+ }
+
+ public static Path createDirsIfNotExists(Path p) throws IOException {
+ if ( Files.notExists(p)) {
+ Files.createDirectories(p);
+ }
+ return p;
+ }
+
+}
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..87ffc3669
--- /dev/null
+++ b/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java
@@ -0,0 +1,60 @@
+package org.owasp.webgoat.plugins;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 implements Runnable {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private final Path pluginSource;
+ private Path pluginTarget;
+
+ 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(pluginSource, new SimpleFileVisitor() {
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ try {
+ PluginFileUtils.createDirsIfNotExists(pluginTarget);
+ PluginExtractor extractor = new PluginExtractor(file);
+ 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...");
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ });
+ } catch (IOException e) {
+ logger.error("Loading plugins failed", e);
+ }
+ return plugins;
+ }
+
+
+ @Override
+ public void run() {
+ loadPlugins(true);
+ }
+}
diff --git a/src/main/java/org/owasp/webgoat/plugins/ResourceBundleClassLoader.java b/src/main/java/org/owasp/webgoat/plugins/ResourceBundleClassLoader.java
new file mode 100644
index 000000000..6f69f7ce1
--- /dev/null
+++ b/src/main/java/org/owasp/webgoat/plugins/ResourceBundleClassLoader.java
@@ -0,0 +1,33 @@
+package org.owasp.webgoat.plugins;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ResourceBundleClassLoader {
+
+ private final static ResourceBundleClassLoader classLoader = new ResourceBundleClassLoader();
+ private Path propertiesPath;
+
+ private ResourceBundleClassLoader() {
+ }
+
+ public static void setPropertiesPath(Path path) {
+ classLoader.propertiesPath = path;
+ }
+
+ public static ClassLoader createPropertyFilesClassLoader(ClassLoader parentClassLoader) {
+ final List urls = new ArrayList<>();
+
+ try {
+ urls.add(classLoader.propertiesPath.toUri().toURL());
+ } catch (IOException e) {
+ throw new Plugin.PluginLoadingFailure("Unable to load the properties for the classloader", e);
+ }
+ return new URLClassLoader(urls.toArray(new URL[urls.size()]), Thread.currentThread().getContextClassLoader());
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/owasp/webgoat/session/Course.java b/src/main/java/org/owasp/webgoat/session/Course.java
index 9245db83c..6f4abd4e5 100644
--- a/src/main/java/org/owasp/webgoat/session/Course.java
+++ b/src/main/java/org/owasp/webgoat/session/Course.java
@@ -1,49 +1,54 @@
package org.owasp.webgoat.session;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-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.util.WebGoatI18N;
+import org.owasp.webgoat.plugins.Plugin;
+import org.owasp.webgoat.plugins.PluginsLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.servlet.ServletContext;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
/**
* *************************************************************************************************
- *
- *
+ *
+ *
* 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 +78,7 @@ public class Course {
/**
* Take an absolute file and return the filename.
- *
+ *
* Ex. /etc/password becomes password
*
* @param s
@@ -95,7 +100,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 +119,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 +242,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) {
@@ -276,106 +281,37 @@ 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");
+ private void loadLessionFromPlugin(ServletContext context) {
+ context.getContextPath();
+ logger.debug("Loading plugins into cache");
+ String pluginPath = context.getRealPath("plugin_lessons");
+ String targetPath = context.getRealPath("plugin_extracted");
+ if (pluginPath == null) {
+ logger.error("Plugins directory {} not found", pluginPath);
return;
}
- Iterator itr = resourcePaths.iterator();
+ 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();
+ Object o = c.newInstance();
- while (itr.hasNext()) {
- String file = (String) itr.next();
+ AbstractLesson lesson = (AbstractLesson) o;
+ lesson.setWebgoatContext(webgoatContext);
- if (file.length() != 1 && file.endsWith("/")) {
- loadFiles(context, file);
- } else {
- files.add(file);
- }
- }
- }
+ lesson.update(properties);
- /**
- * Instantiate all the lesson objects into a cache
- *
- * @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);
+ if (lesson.getHidden() == false) {
+ lessons.add(lesson);
}
- }
- }
- }
-
- 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);
+ for(Map.Entry lessonPlan : plugin.getLessonPlans().entrySet()) {
+ lesson.setLessonPlanFileName(lessonPlan.getKey(), lessonPlan.getValue().toString());
}
+ lesson.setLessonSolutionFileName(plugin.getLessonSolutions().get("en").toString());
+ lesson.setSourceFileName(plugin.getLessonSource().toString());
+ } catch (Exception e) {
+ logger.error("Error in loadLessons: ", e);
}
}
}
@@ -384,14 +320,13 @@ 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);
this.webgoatContext = webgoatContext;
- loadFiles(context, path);
- loadLessons(path);
- loadResources();
+ loadLessionFromPlugin(context);
}
+
}
diff --git a/src/main/java/org/owasp/webgoat/session/WebgoatContext.java b/src/main/java/org/owasp/webgoat/session/WebgoatContext.java
index 1d3fb8266..8c9ce4549 100644
--- a/src/main/java/org/owasp/webgoat/session/WebgoatContext.java
+++ b/src/main/java/org/owasp/webgoat/session/WebgoatContext.java
@@ -1,9 +1,10 @@
package org.owasp.webgoat.session;
-import javax.servlet.http.HttpServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.servlet.http.HttpServlet;
+
public class WebgoatContext {
final Logger logger = LoggerFactory.getLogger(WebgoatContext.class);
@@ -80,6 +81,8 @@ public class WebgoatContext {
private String defaultLanguage;
+ private java.nio.file.Path pluginDirectory;
+
public WebgoatContext(HttpServlet servlet) {
this.servlet = servlet;
databaseConnectionString = getParameter(servlet, DATABASE_CONNECTION_STRING);
@@ -212,5 +215,4 @@ public class WebgoatContext {
public String getDefaultLanguage() {
return defaultLanguage;
}
-
}
diff --git a/src/main/java/org/owasp/webgoat/util/LabelProvider.java b/src/main/java/org/owasp/webgoat/util/LabelProvider.java
index ad6ffe4ff..e2861096d 100644
--- a/src/main/java/org/owasp/webgoat/util/LabelProvider.java
+++ b/src/main/java/org/owasp/webgoat/util/LabelProvider.java
@@ -1,10 +1,12 @@
package org.owasp.webgoat.util;
+import org.owasp.webgoat.plugins.ResourceBundleClassLoader;
+import org.springframework.stereotype.Component;
+
import java.util.HashMap;
import java.util.Locale;
import java.util.ResourceBundle;
-import org.springframework.stereotype.Component;
/***************************************************************************************************
@@ -37,7 +39,7 @@ import org.springframework.stereotype.Component;
@Component
public class LabelProvider
{
- public final static String DEFAULT_LANGUAGE = "en";
+ public final static String DEFAULT_LANGUAGE = Locale.ENGLISH.getLanguage();
private final HashMap labels = new HashMap();
private final WebGoatResourceBundleController localeController = new WebGoatResourceBundleController();
@@ -46,7 +48,8 @@ public class LabelProvider
{
if (!labels.containsKey(locale))
{
- ResourceBundle resBundle = ResourceBundle.getBundle("WebGoatLabels", locale, localeController);
+ ClassLoader classLoader = ResourceBundleClassLoader.createPropertyFilesClassLoader(ResourceBundle.class.getClassLoader());
+ ResourceBundle resBundle = ResourceBundle.getBundle("WebGoatLabels", locale, classLoader, localeController);
labels.put(locale, resBundle);
}
return labels.get(locale).getString(strName);
diff --git a/src/main/resources/WebGoatLabels.properties b/src/main/resources/WebGoatLabels.properties
deleted file mode 100644
index 21e1a6125..000000000
--- a/src/main/resources/WebGoatLabels.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-#General
-LessonCompleted=Congratulations. You have successfully completed this lesson.
-RestartLesson=Restart this Lesson
-SolutionVideos=Solution Videos
-ErrorGenerating=Error generating
-InvalidData=Invalid Data
-
diff --git a/src/main/resources/WebGoatLabels_de.properties b/src/main/resources/WebGoatLabels_de.properties
deleted file mode 100644
index d6edeef94..000000000
--- a/src/main/resources/WebGoatLabels_de.properties
+++ /dev/null
@@ -1,8 +0,0 @@
-#General
-LessonCompleted=Herzlichen Gl\u00fcckwunsch! Sie haben diese Lektion erfolgreich abgeschlossen.
-RestartLesson=Lektion neu beginnen
-SolutionVideos=L\u00f6sungsvideos
-ErrorGenerating=Fehler beim Generieren von
-InvalidData=Ung\u00fcltige Daten
-
-
\ No newline at end of file
diff --git a/src/main/resources/WebGoatLabels_fr.properties b/src/main/resources/WebGoatLabels_fr.properties
deleted file mode 100644
index 24b7f4f39..000000000
--- a/src/main/resources/WebGoatLabels_fr.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-#General
-LessonCompleted=F\u00e9licitations. Vous avez termin\u00e9 cette le\u00e7on avec succ\u00e9s.
-RestartLesson=Recommencer cette le\u00e7on
-SolutionVideos=Solution vid\u00e9os
-ErrorGenerating=Error generating
-InvalidData=Donn\u00e9e invalide
-
diff --git a/src/main/resources/WebGoatLabels_ru.properties b/src/main/resources/WebGoatLabels_ru.properties
deleted file mode 100644
index 8b7a4132d..000000000
--- a/src/main/resources/WebGoatLabels_ru.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-#General
-LessonCompleted=\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e. \u0412\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0448\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0439 \u0443\u0440\u043e\u043a.
-RestartLesson=\u041d\u0430\u0447\u0430\u043b\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430
-SolutionVideos=\u0412\u0438\u0434\u0435\u043e \u0441 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c
-ErrorGenerating=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430
-InvalidData=\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435
-
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
index 734eb76c8..7dee72c2e 100644
--- a/src/main/resources/log4j.properties
+++ b/src/main/resources/log4j.properties
@@ -1,4 +1,4 @@
-log4j.rootLogger=DEBUG, MAIN_LOG
+log4j.rootLogger=DEBUG, MAIN_LOG,CONSOLE
#log4j.rootLogger=DEBUG, MAIN_LOG, ERROR_LOG
# MAIN - everything gets logged here
@@ -10,9 +10,20 @@ log4j.appender.MAIN_LOG.MaxFileSize=10MB
log4j.appender.MAIN_LOG.MaxBackupIndex=5
log4j.appender.MAIN_LOG.append=true
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p - %m%n
+
+
# a little less spring output
log4j.category.org.springframework = INFO
+log4j.appender.default.out=org.apache.log4j.ConsoleAppender
+log4j.appender.default.out.threeshold=DEBUG
+log4j.appender.default.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.default.out.layout.ConversionPattern=%-5p %c: %m%n
+
# ERROR
log4j.appender.ERROR_LOG=org.apache.log4j.RollingFileAppender
log4j.appender.ERROR_LOG.File=${catalina.home}/logs/webgoat_error.log
diff --git a/src/main/webapp/js/ace/mode-praat.js b/src/main/webapp/js/ace/mode-praat.js
index bbd5618ac..d47e332f4 100644
--- a/src/main/webapp/js/ace/mode-praat.js
+++ b/src/main/webapp/js/ace/mode-praat.js
@@ -1 +1 @@
-ace.define("ace/mode/praat_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="if|then|else|elsif|elif|endif|fi|endfor|endproc|while|endwhile|repeat|until|select|plus|minus|assert",t="macintosh|windows|unix|praatVersion|praatVersion\\$pi|undefined|newline\\$|tab\\$|shellDirectory\\$|homeDirectory\\$|preferencesDirectory\\$|temporaryDirectory\\$|defaultDirectory\\$",n="clearinfo|endSendPraat",r="writeInfo|writeInfoLine|appendInfo|appendInfoLine|writeFile|writeFileLine|appendFile|appendFileLine|abs|round|floor|ceiling|min|max|imin|imax|sqrt|sin|cos|tan|arcsin|arccos|arctan|arctan2|sinc|sincpi|exp|ln|log10|log2|sinh|cosh|tanh|arcsinh|arccosh|actanh|sigmoid|invSigmoid|erf|erfc|randomUniform|randomInteger|randomGauss|randomPoisson|lnGamma|gaussP|gaussQ|invGaussQ|chiSquareP|chiSquareQ|invChiSquareQ|studentP|studentQ|invStudentQ|fisherP|fisherQ|invFisherQ|binomialP|binomialQ|invBinomialP|invBinomialQ|hertzToBark|barkToHerz|hertzToMel|melToHertz|hertzToSemitones|semitonesToHerz|erb|hertzToErb|erbToHertz|phonToDifferenceLimens|differenceLimensToPhon|beta|besselI|besselK|selected|selected\\$|numberOfSelected|variableExists|index|rindex|startsWith|endsWith|index_regex|rindex_regex|replace_regex\\$|length|extractWord\\$|extractLine\\$|extractNumber|left\\$|right\\$|mid\\$|replace\\$|beginPause|endPause|demoShow|demoWindowTitle|demoInput|demoWaitForInput|demoClicked|demoClickedIn|demoX|demoY|demoKeyPressed|demoKey\\$|demoExtraControlKeyPressed|demoShiftKeyPressed|demoCommandKeyPressed|demoOptionKeyPressed|environment\\$|chooseReadFile\\$|chooseDirectory\\$|createDirectory|fileReadable|deleteFile|selectObject|removeObject|plusObject|minusObject|runScript|exitScript|beginSendPraat|endSendPraat",i="Activation|AffineTransform|AmplitudeTier|Art|Artword|Autosegment|BarkFilter|CCA|Categories|Cepstrum|Cepstrumc|ChebyshevSeries|ClassificationTable|Cochleagram|Collection|Configuration|Confusion|ContingencyTable|Corpus|Correlation|Covariance|CrossCorrelationTable|CrossCorrelationTables|DTW|Diagonalizer|Discriminant|Dissimilarity|Distance|Distributions|DurationTier|EEG|ERP|ERPTier|Eigen|Excitation|Excitations|ExperimentMFC|FFNet|FeatureWeights|Formant|FormantFilter|FormantGrid|FormantPoint|FormantTier|GaussianMixture|HMM|HMM_Observation|HMM_ObservationSequence|HMM_State|HMM_StateSequence|Harmonicity|ISpline|Index|Intensity|IntensityTier|IntervalTier|KNN|KlattGrid|KlattTable|LFCC|LPC|Label|LegendreSeries|LinearRegression|LogisticRegression|LongSound|Ltas|MFCC|MSpline|ManPages|Manipulation|Matrix|MelFilter|MixingMatrix|Movie|Network|OTGrammar|OTHistory|OTMulti|PCA|PairDistribution|ParamCurve|Pattern|Permutation|Pitch|PitchTier|PointProcess|Polygon|Polynomial|Procrustes|RealPoint|RealTier|ResultsMFC|Roots|SPINET|SSCP|SVD|Salience|ScalarProduct|Similarity|SimpleString|SortedSetOfString|Sound|Speaker|Spectrogram|Spectrum|SpectrumTier|SpeechSynthesizer|SpellingChecker|Strings|StringsIndex|Table|TableOfReal|TextGrid|TextInterval|TextPoint|TextTier|Tier|Transition|VocalTract|Weight|WordList";this.$rules={start:[{token:"string.interpolated",regex:/'((?:[a-z][a-zA-Z0-9_]*)(?:\$|#|:[0-9]+)?)'/},{token:["text","text","keyword.operator","text","keyword"],regex:/(^\s*)(?:([a-z][a-zA-Z0-9_]*\$?\s+)(=)(\s+))?(stopwatch)/},{token:["text","keyword","text","string"],regex:/(^\s*)(print(?:line)?|echo|exit|pause|sendpraat|include|execute)(\s+)(.*)/},{token:["text","keyword"],regex:"(^\\s*)("+n+")$"},{token:["text","keyword.operator","text"],regex:/(\s+)((?:\+|-|\/|\*|<|>)=?|==?|!=|%|\^|\||and|or|not)(\s+)/},{token:["text","text","keyword.operator","text","keyword","text","keyword"],regex:/(^\s*)(?:([a-z][a-zA-Z0-9_]*\$?\s+)(=)(\s+))?(?:((?:no)?warn|nocheck|noprogress)(\s+))?((?:[A-Z][^.:"]+)(?:$|(?:\.{3}|:)))/},{token:["text","keyword","text","keyword"],regex:/(^\s*)(?:(demo)?(\s+))((?:[A-Z][^.:"]+)(?:$|(?:\.{3}|:)))/},{token:["text","keyword","text","keyword"],regex:/^(\s*)(?:(demo)(\s+))?(10|12|14|16|24)$/},{token:["text","support.function","text"],regex:/(\s*)(do\$?)(\s*:\s*|\s*\(\s*)/},{token:"entity.name.type",regex:"("+i+")"},{token:"variable.language",regex:"("+t+")"},{token:["support.function","text"],regex:"((?:"+r+")\\$?)(\\s*(?::|\\())"},{token:"keyword",regex:/(\bfor\b)/,next:"for"},{token:"keyword",regex:"(\\b(?:"+e+")\\b)"},{token:"string",regex:/"[^"]*"/},{token:"string",regex:/"[^"]*$/,next:"brokenstring"},{token:["text","keyword","text","entity.name.section"],regex:/(^\s*)(\bform\b)(\s+)(.*)/,next:"form"},{token:"constant.numeric",regex:/\b[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["keyword","text","entity.name.function"],regex:/(procedure)(\s+)(\S+)/},{token:["entity.name.function","text"],regex:/(@\S+)(:|\s*\()/},{token:["text","keyword","text","entity.name.function"],regex:/(^\s*)(call)(\s+)(\S+)/},{token:"comment",regex:/(^\s*#|;).*$/},{token:"text",regex:/\s+/}],form:[{token:["keyword","text","constant.numeric"],regex:/((?:optionmenu|choice)\s+)(\S+:\s+)([0-9]+)/},{token:["keyword","constant.numeric"],regex:/((?:option|button)\s+)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b)/},{token:["keyword","string"],regex:/((?:option|button)\s+)(.*)/},{token:["keyword","text","string"],regex:/((?:sentence|text)\s+)(\S+\s*)(.*)/},{token:["keyword","text","string","invalid.illegal"],regex:/(word\s+)(\S+\s*)(\S+)?(\s.*)?/},{token:["keyword","text","constant.language"],regex:/(boolean\s+)(\S+\s*)(0|1|"?(?:yes|no)"?)/},{token:["keyword","text","constant.numeric"],regex:/((?:real|natural|positive|integer)\s+)(\S+\s*)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b)/},{token:["keyword","string"],regex:/(comment\s+)(.*)/},{token:"keyword",regex:"endform",next:"start"}],"for":[{token:["keyword","text","constant.numeric","text"],regex:/(from|to)(\s+)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?)(\s*)/},{token:["keyword","text"],regex:/(from|to)(\s+\S+\s*)/},{token:"text",regex:/$/,next:"start"}],brokenstring:[{token:["text","string"],regex:/(\s*\.{3})([^"]*)/},{token:"string",regex:/"/,next:"start"}]}};r.inherits(s,i),t.PraatHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n),s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)}}.call(o.prototype)}),ace.define("ace/mode/praat",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/praat_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./praat_highlight_rules").PraatHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("./folding/cstyle").FoldMode,f=function(){this.HighlightRules=s,this.$outdent=new o};r.inherits(f,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[\:]\s*$/);o&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/praat"}.call(f.prototype),t.Mode=f})
\ No newline at end of file
+ace.define("ace/mode/praat_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="if|then|else|elsif|elif|endif|fi|endfor|endproc|while|endwhile|repeat|until|select|plus|minus|assert",t="macintosh|windows|unix|praatVersion|praatVersion\\$pi|undefined|newline\\$|tab\\$|shellDirectory\\$|homeDirectory\\$|preferencesDirectory\\$|temporaryDirectory\\$|defaultDirectory\\$",n="clearinfo|endSendPraat",r="writeInfo|writeInfoLine|appendInfo|appendInfoLine|writeFile|writeFileLine|appendFile|appendFileLine|abs|round|floor|ceiling|min|max|imin|imax|sqrt|sin|cos|tan|arcsin|arccos|arctan|arctan2|sinc|sincpi|exp|ln|log10|log2|sinh|cosh|tanh|arcsinh|arccosh|actanh|sigmoid|invSigmoid|erf|erfc|randomUniform|randomInteger|randomGauss|randomPoisson|lnGamma|gaussP|gaussQ|invGaussQ|chiSquareP|chiSquareQ|invChiSquareQ|studentP|studentQ|invStudentQ|fisherP|fisherQ|invFisherQ|binomialP|binomialQ|invBinomialP|invBinomialQ|hertzToBark|barkToHerz|hertzToMel|melToHertz|hertzToSemitones|semitonesToHerz|erb|hertzToErb|erbToHertz|phonToDifferenceLimens|differenceLimensToPhon|beta|besselI|besselK|selected|selected\\$|numberOfSelected|variableExists|index|rindex|startsWith|fileEndsWith|index_regex|rindex_regex|replace_regex\\$|length|extractWord\\$|extractLine\\$|extractNumber|left\\$|right\\$|mid\\$|replace\\$|beginPause|endPause|demoShow|demoWindowTitle|demoInput|demoWaitForInput|demoClicked|demoClickedIn|demoX|demoY|demoKeyPressed|demoKey\\$|demoExtraControlKeyPressed|demoShiftKeyPressed|demoCommandKeyPressed|demoOptionKeyPressed|environment\\$|chooseReadFile\\$|chooseDirectory\\$|createDirectory|fileReadable|deleteFile|selectObject|removeObject|plusObject|minusObject|runScript|exitScript|beginSendPraat|endSendPraat",i="Activation|AffineTransform|AmplitudeTier|Art|Artword|Autosegment|BarkFilter|CCA|Categories|Cepstrum|Cepstrumc|ChebyshevSeries|ClassificationTable|Cochleagram|Collection|Configuration|Confusion|ContingencyTable|Corpus|Correlation|Covariance|CrossCorrelationTable|CrossCorrelationTables|DTW|Diagonalizer|Discriminant|Dissimilarity|Distance|Distributions|DurationTier|EEG|ERP|ERPTier|Eigen|Excitation|Excitations|ExperimentMFC|FFNet|FeatureWeights|Formant|FormantFilter|FormantGrid|FormantPoint|FormantTier|GaussianMixture|HMM|HMM_Observation|HMM_ObservationSequence|HMM_State|HMM_StateSequence|Harmonicity|ISpline|Index|Intensity|IntensityTier|IntervalTier|KNN|KlattGrid|KlattTable|LFCC|LPC|Label|LegendreSeries|LinearRegression|LogisticRegression|LongSound|Ltas|MFCC|MSpline|ManPages|Manipulation|Matrix|MelFilter|MixingMatrix|Movie|Network|OTGrammar|OTHistory|OTMulti|PCA|PairDistribution|ParamCurve|Pattern|Permutation|Pitch|PitchTier|PointProcess|Polygon|Polynomial|Procrustes|RealPoint|RealTier|ResultsMFC|Roots|SPINET|SSCP|SVD|Salience|ScalarProduct|Similarity|SimpleString|SortedSetOfString|Sound|Speaker|Spectrogram|Spectrum|SpectrumTier|SpeechSynthesizer|SpellingChecker|Strings|StringsIndex|Table|TableOfReal|TextGrid|TextInterval|TextPoint|TextTier|Tier|Transition|VocalTract|Weight|WordList";this.$rules={start:[{token:"string.interpolated",regex:/'((?:[a-z][a-zA-Z0-9_]*)(?:\$|#|:[0-9]+)?)'/},{token:["text","text","keyword.operator","text","keyword"],regex:/(^\s*)(?:([a-z][a-zA-Z0-9_]*\$?\s+)(=)(\s+))?(stopwatch)/},{token:["text","keyword","text","string"],regex:/(^\s*)(print(?:line)?|echo|exit|pause|sendpraat|include|execute)(\s+)(.*)/},{token:["text","keyword"],regex:"(^\\s*)("+n+")$"},{token:["text","keyword.operator","text"],regex:/(\s+)((?:\+|-|\/|\*|<|>)=?|==?|!=|%|\^|\||and|or|not)(\s+)/},{token:["text","text","keyword.operator","text","keyword","text","keyword"],regex:/(^\s*)(?:([a-z][a-zA-Z0-9_]*\$?\s+)(=)(\s+))?(?:((?:no)?warn|nocheck|noprogress)(\s+))?((?:[A-Z][^.:"]+)(?:$|(?:\.{3}|:)))/},{token:["text","keyword","text","keyword"],regex:/(^\s*)(?:(demo)?(\s+))((?:[A-Z][^.:"]+)(?:$|(?:\.{3}|:)))/},{token:["text","keyword","text","keyword"],regex:/^(\s*)(?:(demo)(\s+))?(10|12|14|16|24)$/},{token:["text","support.function","text"],regex:/(\s*)(do\$?)(\s*:\s*|\s*\(\s*)/},{token:"entity.name.type",regex:"("+i+")"},{token:"variable.language",regex:"("+t+")"},{token:["support.function","text"],regex:"((?:"+r+")\\$?)(\\s*(?::|\\())"},{token:"keyword",regex:/(\bfor\b)/,next:"for"},{token:"keyword",regex:"(\\b(?:"+e+")\\b)"},{token:"string",regex:/"[^"]*"/},{token:"string",regex:/"[^"]*$/,next:"brokenstring"},{token:["text","keyword","text","entity.name.section"],regex:/(^\s*)(\bform\b)(\s+)(.*)/,next:"form"},{token:"constant.numeric",regex:/\b[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["keyword","text","entity.name.function"],regex:/(procedure)(\s+)(\S+)/},{token:["entity.name.function","text"],regex:/(@\S+)(:|\s*\()/},{token:["text","keyword","text","entity.name.function"],regex:/(^\s*)(call)(\s+)(\S+)/},{token:"comment",regex:/(^\s*#|;).*$/},{token:"text",regex:/\s+/}],form:[{token:["keyword","text","constant.numeric"],regex:/((?:optionmenu|choice)\s+)(\S+:\s+)([0-9]+)/},{token:["keyword","constant.numeric"],regex:/((?:option|button)\s+)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b)/},{token:["keyword","string"],regex:/((?:option|button)\s+)(.*)/},{token:["keyword","text","string"],regex:/((?:sentence|text)\s+)(\S+\s*)(.*)/},{token:["keyword","text","string","invalid.illegal"],regex:/(word\s+)(\S+\s*)(\S+)?(\s.*)?/},{token:["keyword","text","constant.language"],regex:/(boolean\s+)(\S+\s*)(0|1|"?(?:yes|no)"?)/},{token:["keyword","text","constant.numeric"],regex:/((?:real|natural|positive|integer)\s+)(\S+\s*)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b)/},{token:["keyword","string"],regex:/(comment\s+)(.*)/},{token:"keyword",regex:"endform",next:"start"}],"for":[{token:["keyword","text","constant.numeric","text"],regex:/(from|to)(\s+)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?)(\s*)/},{token:["keyword","text"],regex:/(from|to)(\s+\S+\s*)/},{token:"text",regex:/$/,next:"start"}],brokenstring:[{token:["text","string"],regex:/(\s*\.{3})([^"]*)/},{token:"string",regex:/"/,next:"start"}]}};r.inherits(s,i),t.PraatHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n),s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)}}.call(o.prototype)}),ace.define("ace/mode/praat",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/praat_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./praat_highlight_rules").PraatHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("./folding/cstyle").FoldMode,f=function(){this.HighlightRules=s,this.$outdent=new o};r.inherits(f,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[\:]\s*$/);o&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/praat"}.call(f.prototype),t.Mode=f})
\ No newline at end of file
diff --git a/src/main/webapp/js/ace/snippets/jsp.js b/src/main/webapp/js/ace/snippets/jsp.js
index fcbace719..c39e6c084 100644
--- a/src/main/webapp/js/ace/snippets/jsp.js
+++ b/src/main/webapp/js/ace/snippets/jsp.js
@@ -1 +1 @@
-ace.define("ace/snippets/jsp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='snippet @page\n <%@page contentType="text/html" pageEncoding="UTF-8"%>\nsnippet jstl\n <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\n <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet jstl:c\n <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\nsnippet jstl:fn\n <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet cpath\n ${pageContext.request.contextPath}\nsnippet cout\n \nsnippet cset\n \nsnippet cremove\n \nsnippet ccatch\n \nsnippet cif\n \n ${2}\n \nsnippet cchoose\n \n ${1}\n \nsnippet cwhen\n \n ${2}\n \nsnippet cother\n \n ${1}\n \nsnippet cfore\n \n ${4:}\n \nsnippet cfort\n ${2:item1,item2,item3}\n \n ${5:}\n \nsnippet cparam\n \nsnippet cparam+\n \n cparam+${3}\nsnippet cimport\n \nsnippet cimport+\n \n \n cparam+${4}\n \nsnippet curl\n \n ${3}\nsnippet curl+\n \n \n cparam+${6}\n \n ${3}\nsnippet credirect\n \nsnippet contains\n ${fn:contains(${1:string}, ${2:substr})}\nsnippet contains:i\n ${fn:containsIgnoreCase(${1:string}, ${2:substr})}\nsnippet endswith\n ${fn:endsWith(${1:string}, ${2:suffix})}\nsnippet escape\n ${fn:escapeXml(${1:string})}\nsnippet indexof\n ${fn:indexOf(${1:string}, ${2:substr})}\nsnippet join\n ${fn:join(${1:collection}, ${2:delims})}\nsnippet length\n ${fn:length(${1:collection_or_string})}\nsnippet replace\n ${fn:replace(${1:string}, ${2:substr}, ${3:replace})}\nsnippet split\n ${fn:split(${1:string}, ${2:delims})}\nsnippet startswith\n ${fn:startsWith(${1:string}, ${2:prefix})}\nsnippet substr\n ${fn:substring(${1:string}, ${2:begin}, ${3:end})}\nsnippet substr:a\n ${fn:substringAfter(${1:string}, ${2:substr})}\nsnippet substr:b\n ${fn:substringBefore(${1:string}, ${2:substr})}\nsnippet lc\n ${fn:toLowerCase(${1:string})}\nsnippet uc\n ${fn:toUpperCase(${1:string})}\nsnippet trim\n ${fn:trim(${1:string})}\n',t.scope="jsp"})
\ No newline at end of file
+ace.define("ace/snippets/jsp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='snippet @page\n <%@page contentType="text/html" pageEncoding="UTF-8"%>\nsnippet jstl\n <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\n <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet jstl:c\n <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\nsnippet jstl:fn\n <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet cpath\n ${pageContext.request.contextPath}\nsnippet cout\n \nsnippet cset\n \nsnippet cremove\n \nsnippet ccatch\n \nsnippet cif\n \n ${2}\n \nsnippet cchoose\n \n ${1}\n \nsnippet cwhen\n \n ${2}\n \nsnippet cother\n \n ${1}\n \nsnippet cfore\n \n ${4:}\n \nsnippet cfort\n ${2:item1,item2,item3}\n \n ${5:}\n \nsnippet cparam\n \nsnippet cparam+\n \n cparam+${3}\nsnippet cimport\n \nsnippet cimport+\n \n \n cparam+${4}\n \nsnippet curl\n \n ${3}\nsnippet curl+\n \n \n cparam+${6}\n \n ${3}\nsnippet credirect\n \nsnippet contains\n ${fn:contains(${1:string}, ${2:substr})}\nsnippet contains:i\n ${fn:containsIgnoreCase(${1:string}, ${2:substr})}\nsnippet endswith\n ${fn:fileEndsWith(${1:string}, ${2:suffix})}\nsnippet escape\n ${fn:escapeXml(${1:string})}\nsnippet indexof\n ${fn:indexOf(${1:string}, ${2:substr})}\nsnippet join\n ${fn:join(${1:collection}, ${2:delims})}\nsnippet length\n ${fn:length(${1:collection_or_string})}\nsnippet replace\n ${fn:replace(${1:string}, ${2:substr}, ${3:replace})}\nsnippet split\n ${fn:split(${1:string}, ${2:delims})}\nsnippet startswith\n ${fn:startsWith(${1:string}, ${2:prefix})}\nsnippet substr\n ${fn:substring(${1:string}, ${2:begin}, ${3:end})}\nsnippet substr:a\n ${fn:substringAfter(${1:string}, ${2:substr})}\nsnippet substr:b\n ${fn:substringBefore(${1:string}, ${2:substr})}\nsnippet lc\n ${fn:toLowerCase(${1:string})}\nsnippet uc\n ${fn:toUpperCase(${1:string})}\nsnippet trim\n ${fn:trim(${1:string})}\n',t.scope="jsp"})
\ No newline at end of file
diff --git a/src/main/webapp/lessons/ConfManagement/config.jsp b/src/main/webapp/lessons/ConfManagement/config.jsp
new file mode 100644
index 000000000..31b51e2f1
--- /dev/null
+++ b/src/main/webapp/lessons/ConfManagement/config.jsp
@@ -0,0 +1,19 @@
+<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
+ pageEncoding="ISO-8859-1"%>
+<%@page import="org.owasp.webgoat.session.WebSession"%>
+<%
+WebSession webSession = ((WebSession)session.getAttribute("websession"));
+%>
+
+
+
+
+Configuration Page
+
+
+<% response.sendRedirect(webSession.getCurrentLesson().getLink() +
+ "&succeeded=yes");
+%>
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/plugin_lessons/SqlStringInjection-1.0.jar b/src/main/webapp/plugin_lessons/SqlStringInjection-1.0.jar
new file mode 100644
index 000000000..d032bb12c
Binary files /dev/null and b/src/main/webapp/plugin_lessons/SqlStringInjection-1.0.jar differ