Merge pull request #1 from WebGoat/master

manually creating pull request from WebGoat Master to my fork
This commit is contained in:
misfir3 2015-07-01 20:00:47 -04:00
commit 354a1b935d
29 changed files with 486 additions and 232 deletions

5
.gitignore vendored
View File

@ -24,5 +24,10 @@ src/main/main.iml
*.LOCAL.*.jsp *.LOCAL.*.jsp
*.REMOTE.*.jsp *.REMOTE.*.jsp
src/main/webapp/plugin_extracted/* src/main/webapp/plugin_extracted/*
src/main/webapp/users/*.jar
src/main/webapp/plugin_lessons/*.jar
src/main/webapp/users/*.props
classes/*
/*.iml /*.iml
.extract/*

View File

@ -98,16 +98,33 @@ Building the project (Developers)
Using a command shell/window: Using a command shell/window:
> cd webgoat > cd webgoat-classloader
> mvn clean install
> cd ..
> mvn clean package > mvn clean package
After opening the project in Netbeans or Eclipse, you can easily run the project using maven: Building the webgoat-classloader is only necessary once, the classloader needs to be present in your local repository.
After opening the project in Netbeans or Eclipse, you can easily run the project using:
1. Maven-Tomcat Plugin
Using a command shell/window:
> mvn tomcat:run-war > mvn tomcat:run-war
Maven will run the project in an embedded tomcat. Maven will run the project in an embedded tomcat.
2. Java JAR
the package phase also builds an executable jar file. You can run it using: the package phase also builds an executable jar file. You can run it using:
cd target cd target
java -jar WebGoat-6.0-exec-war.jar java -jar WebGoat-6.0-exec-war.jar
http://localhost:8080/WebGoat http://localhost:8080/WebGoat
3. Tomcat
the package phase also builds a war file. You can deploy it using:
cp target/WebGoat-6.0-exec-war.war <tomcat>/webapps/
Then also clone https://github.com/WebGoat/WebGoat-Lessons run:
cd WebGoat-Lessons
mvn package
cp plugins/* <tomcat>/webapps/WebGoat-6.0-exec-war/plugin_lessons/

3
catalina.policy Normal file
View File

@ -0,0 +1,3 @@
grant {
permission java.security.AllPermission;
};

67
pom.xml
View File

@ -44,6 +44,19 @@
<encoding>ISO-8859-1</encoding> <encoding>ISO-8859-1</encoding>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>create-jar</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId> <artifactId>maven-war-plugin</artifactId>
@ -64,38 +77,6 @@
</archive> </archive>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>copy</goal>
</goals>
<phase>package</phase>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>jar</type>
<classifier>classes</classifier>
<outputDirectory>${project.build.directory}</outputDirectory>
<destFileName>webgoat-container-${project.version}.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId> <artifactId>build-helper-maven-plugin</artifactId>
@ -125,7 +106,24 @@
<url>http://localhost:8080/manager</url> <url>http://localhost:8080/manager</url>
<path>/WebGoat</path> <path>/WebGoat</path>
<attachArtifactClassifier>exec</attachArtifactClassifier> <attachArtifactClassifier>exec</attachArtifactClassifier>
<contextReloadable>true</contextReloadable>
<useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader>
<contextFile>${project.basedir}/src/main/webapp/WEB-INF/context.xml</contextFile>
<extraDependencies>
<extraDependency>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-classloader</artifactId>
<version>${project.version}</version>
</extraDependency>
</extraDependencies>
</configuration> </configuration>
<dependencies>
<dependency>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-container</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<executions> <executions>
<execution> <execution>
<id>tomcat-run</id> <id>tomcat-run</id>
@ -140,6 +138,11 @@
</build> </build>
<dependencies> <dependencies>
<dependency>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-classloader</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>javax.activation</groupId> <groupId>javax.activation</groupId>
<artifactId>activation</artifactId> <artifactId>activation</artifactId>

View File

@ -1,17 +1,5 @@
package org.owasp.webgoat; package org.owasp.webgoat;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.WelcomeScreen; import org.owasp.webgoat.lessons.WelcomeScreen;
import org.owasp.webgoat.lessons.admin.WelcomeAdminScreen; import org.owasp.webgoat.lessons.admin.WelcomeAdminScreen;
@ -24,6 +12,19 @@ import org.owasp.webgoat.session.WebgoatContext;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/** /**
* ************************************************************************************************* * *************************************************************************************************
* *
@ -192,8 +193,7 @@ public class HammerHead extends HttpServlet {
logger.debug("Screen: " + screen); logger.debug("Screen: " + screen);
request.getRequestDispatcher(viewPage).forward(request, response); request.getRequestDispatcher(viewPage).forward(request, response);
} catch (Throwable t) { } catch (Throwable t) {
logger.error("Error handling request", t); logger.error("Error handling request", t); screen = new ErrorScreen(mySession, t);
screen = new ErrorScreen(mySession, t);
} finally { } finally {
try { try {
if (screen instanceof ErrorScreen) { if (screen instanceof ErrorScreen) {

View File

@ -5,13 +5,13 @@
*/ */
package org.owasp.webgoat.application; package org.owasp.webgoat.application;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.Manifest; import java.util.jar.Manifest;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/** /**
* Web application lifecycle listener. * Web application lifecycle listener.

View File

@ -355,8 +355,7 @@ public abstract class AbstractLesson extends Screen implements Comparable<Object
* @return The lessonPlan value * @return The lessonPlan value
*/ */
protected String getLessonName() { protected String getLessonName() {
int index = this.getClass().getName().indexOf("lessons."); return this.getClass().getSimpleName();
return this.getClass().getName().substring(index + "lessons.".length());
} }
/** /**
@ -734,6 +733,7 @@ public abstract class AbstractLesson extends Screen implements Comparable<Object
Form form = new Form(getFormAction(), Form.POST).setName("form").setEncType(""); Form form = new Form(getFormAction(), Form.POST).setName("form").setEncType("");
form.addElement(createContent(s)); form.addElement(createContent(s));
setContent(form); setContent(form);
s.getRequest().getRequestURL();
} }
public String getFormAction() { public String getFormAction() {
@ -802,4 +802,17 @@ public abstract class AbstractLesson extends Screen implements Comparable<Object
} }
return labelManager; return labelManager;
} }
protected final String buildImagePath(WebSession w, String imgResourceName) {
return w.getRequest().getContextPath() + "/plugin_extracted/plugin/" + getLessonName() + "/images/" + imgResourceName;
}
protected final String buildJspPath(WebSession w, String jspResourceName) {
return w.getRequest().getContextPath() + "/plugin_extracted/plugin/" + getLessonName() + "/jsp/" + jspResourceName;
}
protected final String buildJsPath(WebSession w, String jsResourceName) {
return w.getRequest().getContextPath() + "/plugin_extracted/plugin/" + getLessonName() + "/js/" + jsResourceName;
}
} }

View File

@ -1,21 +1,18 @@
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContext;
import org.owasp.webgoat.HammerHead;
import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.session.WebgoatContext; import org.owasp.webgoat.session.WebgoatContext;
import org.owasp.webgoat.session.WebgoatProperties; import org.owasp.webgoat.session.WebgoatProperties;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/** /**
* ************************************************************************************************* * *************************************************************************************************
* <p/> * <p/>
@ -178,7 +175,7 @@ public class LegacyLoader {
for (String file : files) { for (String file : files) {
String className = getClassFile(file, path); String className = getClassFile(file, path);
if (className != null && !className.endsWith("_i")) { if (className != null && !className.endsWith("_i") && className.startsWith("org.owasp.webgoat.lessons.admin")) {
try { try {
Class c = Class.forName(className); Class c = Class.forName(className);
Object o = c.newInstance(); Object o = c.newInstance();

View File

@ -1,16 +1,16 @@
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import org.owasp.webgoat.classloader.PluginClassLoader;
import org.owasp.webgoat.lessons.AbstractLesson; 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.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -20,54 +20,48 @@ import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static org.owasp.webgoat.plugins.PluginFileUtils.fileEndsWith; import static org.owasp.webgoat.plugins.PluginFileUtils.fileEndsWith;
import static org.owasp.webgoat.plugins.PluginFileUtils.hasParentDirectoryWithName; import static org.owasp.webgoat.plugins.PluginFileUtils.hasParentDirectoryWithName;
import static org.owasp.webgoat.plugins.PluginFileUtils.replaceInFiles;
public class Plugin { public class Plugin {
private static final String NAME_LESSON_SOLUTION_DIRECTORY = "lessonSolutions"; private static final String NAME_LESSON_SOLUTION_DIRECTORY = "lessonSolutions";
private static final String NAME_LESSON_PLANS_DIRECTORY = "lessonPlans"; private static final String NAME_LESSON_PLANS_DIRECTORY = "lessonPlans";
private static final String NAME_LESSON_I18N_DIRECTORY = "i18n"; private static final String NAME_LESSON_I18N_DIRECTORY = "i18n";
private final Logger logger = LoggerFactory.getLogger(Plugin.class);
private final Path pluginDirectory; private final Path pluginDirectory;
private Class<AbstractLesson> lesson; private Class<AbstractLesson> lesson;
private Map<String, File> solutionLanguageFiles = new HashMap<>(); private Map<String, File> solutionLanguageFiles = new HashMap<>();
private Map<String, File> lessonPlansLanguageFiles = new HashMap<>(); private Map<String, File> lessonPlansLanguageFiles = new HashMap<>();
private List<File> pluginFiles = Lists.newArrayList();
private File lessonSourceFile; 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) { public Plugin(Path pluginDirectory) {
this.pluginDirectory = pluginDirectory; this.pluginDirectory = pluginDirectory;
} }
public void loadClasses(Map<String, byte[]> classes) { public Plugin(Path pluginDirectory, List<String> classes) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); this.pluginDirectory = pluginDirectory;
PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader); findLesson(classes);
for (Map.Entry<String, byte[]> clazz : classes.entrySet()) { }
loadClass(pluginClassLoader, clazz.getKey(), clazz.getValue());
} private void findLesson(List<String> classes) {
if (lesson == null) { for (String clazzName : classes) {
throw new PluginLoadingFailure(String findLesson(clazzName);
.format("Lesson class not found, following classes were detected in the plugin: %s",
StringUtils.collectionToCommaDelimitedString(classes.keySet())));
} }
} }
private void loadClass(PluginClassLoader pluginClassLoader, String name, byte[] classFile) { private void findLesson(String name) {
String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", ""); String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", "");
PluginClassLoader cl = (PluginClassLoader) Thread.currentThread().getContextClassLoader();
Class clazz = pluginClassLoader.loadClass(realClassName, classFile); try {
if (AbstractLesson.class.isAssignableFrom(clazz)) { Class clazz = cl.loadClass(realClassName, true);
this.lesson = clazz;
if (AbstractLesson.class.isAssignableFrom(clazz)) {
this.lesson = clazz;
}
} catch (ClassNotFoundException ce) {
throw new PluginLoadingFailure("Class " + realClassName + " listed in jar but unable to load the class.", ce);
} }
} }
@ -85,6 +79,9 @@ public class Plugin {
if (fileEndsWith(file, ".properties") && hasParentDirectoryWithName(file, NAME_LESSON_I18N_DIRECTORY)) { if (fileEndsWith(file, ".properties") && hasParentDirectoryWithName(file, NAME_LESSON_I18N_DIRECTORY)) {
copyProperties(reload, file); copyProperties(reload, file);
} }
if (fileEndsWith(file, ".css", ".jsp", ".js")) {
pluginFiles.add(file.toFile());
}
} }
} }
@ -94,6 +91,7 @@ public class Plugin {
Files.copy(file, bos); Files.copy(file, bos);
Path propertiesPath = createPropertiesDirectory(); Path propertiesPath = createPropertiesDirectory();
ResourceBundleClassLoader.setPropertiesPath(propertiesPath); ResourceBundleClassLoader.setPropertiesPath(propertiesPath);
PluginFileUtils.createDirsIfNotExists(file.getParent());
if (reload) { if (reload) {
Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), CREATE, APPEND); Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), CREATE, APPEND);
} else { } else {
@ -114,25 +112,48 @@ public class Plugin {
public void rewritePaths(Path pluginTarget) { public void rewritePaths(Path pluginTarget) {
try { try {
PluginFileUtils.replaceInFiles(this.lesson.getSimpleName() + "_files", replaceInFiles(this.lesson.getSimpleName() + "_files",
pluginTarget.getFileName().toString() + "/plugin/" + this.lesson pluginTarget.getFileName().toString() + "/plugin/" + this.lesson
.getSimpleName() + "/lessonSolutions/en/" + this.lesson.getSimpleName() + "_files", .getSimpleName() + "/lessonSolutions/en/" + this.lesson.getSimpleName() + "_files",
solutionLanguageFiles.values()); solutionLanguageFiles.values());
PluginFileUtils.replaceInFiles(this.lesson.getSimpleName() + "_files", replaceInFiles(this.lesson.getSimpleName() + "_files",
pluginTarget.getFileName().toString() + "/plugin/" + this.lesson pluginTarget.getFileName().toString() + "/plugin/" + this.lesson
.getSimpleName() + "/lessonPlans/en/" + this.lesson.getSimpleName() + "_files", .getSimpleName() + "/lessonPlans/en/" + this.lesson.getSimpleName() + "_files",
lessonPlansLanguageFiles.values()); lessonPlansLanguageFiles.values());
String[] replacements = {"jsp", "js"};
for ( String replacement : replacements ) {
String s = String.format("plugin/%s/%s/", this.lesson.getSimpleName(), replacement);
String r = String.format("%s/plugin/%s/%s/", pluginTarget.getFileName().toString(),
this.lesson.getSimpleName(), replacement);
replaceInFiles(s,r, pluginFiles);
replaceInFiles(s,r, Arrays.asList(lessonSourceFile));
}
//CSS with url('/plugin/images') should not begin with / otherwise image cannot be found
String s = String.format("/plugin/%s/images/", this.lesson.getSimpleName());
String r = String.format("%s/plugin/%s/images/", pluginTarget.getFileName().toString(), this.lesson.getSimpleName());
replaceInFiles(s,r, pluginFiles);
replaceInFiles(s,r, Arrays.asList(lessonSourceFile));
} catch (IOException e) { } catch (IOException e) {
throw new PluginLoadingFailure("Unable to rewrite the paths in the solutions", e); throw new PluginLoadingFailure("Unable to rewrite the paths in the solutions", e);
} }
} }
public AbstractLesson getLesson() { /**
* Lesson is optional, it is also possible that the supplied jar contains only helper classes.
*/
public Optional<AbstractLesson> getLesson() {
try { try {
return lesson.newInstance(); if (lesson != null) {
return Optional.of(lesson.newInstance());
}
} catch (IllegalAccessException | InstantiationException e) { } catch (IllegalAccessException | InstantiationException e) {
throw new PluginLoadingFailure("Unable to instantiate the lesson " + lesson.getName(), e); throw new PluginLoadingFailure("Unable to instantiate the lesson " + lesson.getName(), e);
} }
return Optional.absent();
} }
public Optional<File> getLessonSolution(String language) { public Optional<File> getLessonSolution(String language) {

View File

@ -17,6 +17,7 @@ public class PluginBackgroundLoader implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) { public void contextInitialized(ServletContextEvent event) {
String pluginPath = event.getServletContext().getRealPath("plugin_lessons"); String pluginPath = event.getServletContext().getRealPath("plugin_lessons");
String targetPath = event.getServletContext().getRealPath("plugin_extracted"); String targetPath = event.getServletContext().getRealPath("plugin_extracted");
scheduler = Executors.newSingleThreadScheduledExecutor(); 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)), 0, 5, TimeUnit.MINUTES);
} }

View File

@ -1,43 +0,0 @@
package org.owasp.webgoat.plugins;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class PluginClassLoader extends ClassLoader {
private final List<Class<?>> classes = new ArrayList<>();
private final Logger logger = LoggerFactory.getLogger(Plugin.class);
public Class<?> loadClass(String nameOfClass, byte[] classFile) {
Class<?> clazz = defineClass(nameOfClass, classFile, 0, classFile.length);
classes.add(clazz);
return clazz;
}
public PluginClassLoader(ClassLoader contextClassLoader) {
super(contextClassLoader);
}
public Class findClass(final String name) throws ClassNotFoundException {
logger.debug("Finding class " + name);
Optional<Class<?>> foundClass = FluentIterable.from(classes)
.firstMatch(new Predicate<Class<?>>() {
@Override
public boolean apply(Class<?> clazz) {
return clazz.getName().equals(name);
}
});
if (foundClass.isPresent()) {
return foundClass.get();
}
throw new ClassNotFoundException("Class " + name + " not found");
}
}

View File

@ -1,6 +1,7 @@
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import java.io.ByteArrayOutputStream; import com.google.common.collect.Lists;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.file.FileSystem; import java.nio.file.FileSystem;
@ -14,20 +15,19 @@ import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import static java.lang.String.format; import static java.lang.String.format;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.owasp.webgoat.plugins.PluginFileUtils.createDirsIfNotExists; 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 * Extract the jar file and place them in the system temp directory in the folder webgoat and collect the files
* and classes. * and classes.
*/ */
public class PluginExtractor { public class PluginExtractor {
private final Path pluginArchive; private final Path pluginArchive;
private final Map<String, byte[]> classes = new HashMap<>(); private final List<String> classes = Lists.newArrayList();
private final List<Path> files = new ArrayList<>(); private final List<Path> files = new ArrayList<>();
public PluginExtractor(Path pluginArchive) { public PluginExtractor(Path pluginArchive) {
@ -41,20 +41,18 @@ public class PluginExtractor {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toString().endsWith(".class")) { if (file.toString().endsWith(".class")) {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); classes.add(file.toString());
Files.copy(file, bos);
classes.put(file.toString(), bos.toByteArray());
} }
files.add(Files.copy(file, createDirsIfNotExists(Paths.get(target.toString(), file.toString())), REPLACE_EXISTING)); files.add(Files.copy(file, createDirsIfNotExists(Paths.get(target.toString(), file.toString())), REPLACE_EXISTING));
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }
}); });
} catch (Exception e) { } catch (Exception e) {
new Plugin.PluginLoadingFailure(format("Unable to extract: %s", pluginArchive.getFileName()), e); new PluginLoadingFailure(format("Unable to extract: %s", pluginArchive.getFileName()), e);
} }
} }
public Map<String, byte[]> getClasses() { public List<String> getClasses() {
return this.classes; return this.classes;
} }

View File

@ -7,6 +7,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.DirectoryStream; import java.nio.file.DirectoryStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
@ -20,6 +21,15 @@ public class PluginFileUtils {
return p.getFileName().toString().endsWith(s); return p.getFileName().toString().endsWith(s);
} }
public static boolean fileEndsWith(Path p, String... suffixes) {
for (String suffix : suffixes) {
if (fileEndsWith(p, suffix)) {
return true;
}
}
return false;
}
public static boolean hasParentDirectoryWithName(Path p, String s) { public static boolean hasParentDirectoryWithName(Path p, String s) {
if (p == null || p.getParent() == null || p.getParent().equals(p.getRoot())) { if (p == null || p.getParent() == null || p.getParent().equals(p.getRoot())) {
return false; return false;
@ -69,4 +79,12 @@ public class PluginFileUtils {
Files.write(file, fileAsString.getBytes(), StandardOpenOption.TRUNCATE_EXISTING); Files.write(file, fileAsString.getBytes(), StandardOpenOption.TRUNCATE_EXISTING);
} }
public static void writeFile(Path targetFile, byte[] bytes, OpenOption... options) throws IOException {
createDirsIfNotExists(targetFile.getParent());
if (!Files.exists(targetFile)) {
Files.createFile(targetFile);
}
Files.write(targetFile, bytes, options);
}
} }

View File

@ -0,0 +1,8 @@
package org.owasp.webgoat.plugins;
public class PluginLoadingFailure extends RuntimeException {
public PluginLoadingFailure(String message, Exception e) {
super(message, e);
}
}

View File

@ -1,60 +1,84 @@
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.owasp.webgoat.classloader.PluginClassLoader;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class PluginsLoader implements Runnable { public class PluginsLoader implements Runnable {
protected static final String WEBGOAT_PLUGIN_EXTENSION = "jar"; protected static final String WEBGOAT_PLUGIN_EXTENSION = "jar";
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Path pluginSource; private final Path pluginSource;
private Path pluginTarget; private Path pluginTarget;
public PluginsLoader(Path pluginSource, Path pluginTarget) { public PluginsLoader(Path pluginSource, Path pluginTarget) {
Preconditions.checkNotNull(pluginSource, "plugin source cannot be null");
Preconditions.checkNotNull(pluginTarget, "plugin target cannot be null");
this.pluginSource = pluginSource; this.pluginSource = pluginSource;
this.pluginTarget = pluginTarget; this.pluginTarget = pluginTarget;
} }
public List<Plugin> loadPlugins(final boolean reload) { public List<Plugin> loadPlugins(final boolean reload) {
final List<Plugin> plugins = new ArrayList<Plugin>(); final PluginClassLoader cl = (PluginClassLoader)Thread.currentThread().getContextClassLoader();
List<Plugin> plugins = Lists.newArrayList();
try { try {
Files.walkFileTree(pluginSource, new SimpleFileVisitor<Path>() { List<URL> jars = listJars();
cl.addURL(jars);
@Override plugins = processPlugins(jars, reload);
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { } catch (IOException | URISyntaxException e) {
try {
if (PluginFileUtils.fileEndsWith(file, WEBGOAT_PLUGIN_EXTENSION)) {
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...", e);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
logger.error("Loading plugins failed", e); logger.error("Loading plugins failed", e);
} }
return plugins; return plugins;
} }
private List<URL> listJars() throws IOException {
final List<URL> jars = Lists.newArrayList();
Files.walkFileTree(pluginSource, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (PluginFileUtils.fileEndsWith(file, WEBGOAT_PLUGIN_EXTENSION)) {
jars.add(file.toUri().toURL());
}
return FileVisitResult.CONTINUE;
}
});
return jars;
}
private List<Plugin> processPlugins(List<URL> jars, boolean reload) throws URISyntaxException, IOException {
final List<Plugin> plugins = Lists.newArrayList();
for (URL jar : jars) {
PluginExtractor extractor = new PluginExtractor(Paths.get(jar.toURI()));
extractor.extract(pluginTarget);
Plugin plugin = new Plugin(pluginTarget, extractor.getClasses());
if (plugin.getLesson().isPresent()) {
PluginFileUtils.createDirsIfNotExists(pluginTarget);
plugin.loadFiles(extractor.getFiles(), reload);
plugin.rewritePaths(pluginTarget);
plugins.add(plugin);
}
}
return plugins;
}
@Override @Override
public void run() { public void run() {

View File

@ -19,13 +19,13 @@ public class ResourceBundleClassLoader {
classLoader.propertiesPath = path; classLoader.propertiesPath = path;
} }
public static ClassLoader createPropertyFilesClassLoader(ClassLoader parentClassLoader) { public static ClassLoader createPropertyFilesClassLoader() {
final List<URL> urls = new ArrayList<>(); final List<URL> urls = new ArrayList<>();
try { try {
urls.add(classLoader.propertiesPath.toUri().toURL()); urls.add(classLoader.propertiesPath.toUri().toURL());
} catch (IOException e) { } catch (IOException e) {
throw new Plugin.PluginLoadingFailure("Unable to load the properties for the classloader", e); throw new PluginLoadingFailure("Unable to load the properties for the classloader", e);
} }
return new URLClassLoader(urls.toArray(new URL[urls.size()]), Thread.currentThread().getContextClassLoader()); return new URLClassLoader(urls.toArray(new URL[urls.size()]), Thread.currentThread().getContextClassLoader());
} }

View File

@ -265,7 +265,6 @@ public class Course {
} }
Collections.sort(lessonList); Collections.sort(lessonList);
// System.out.println(java.util.Arrays.asList(lessonList));
return lessonList; return lessonList;
} }
@ -295,6 +294,7 @@ public class Course {
logger.debug("Loading plugins into cache"); logger.debug("Loading plugins into cache");
String pluginPath = context.getRealPath("plugin_lessons"); String pluginPath = context.getRealPath("plugin_lessons");
String targetPath = context.getRealPath("plugin_extracted"); String targetPath = context.getRealPath("plugin_extracted");
if (pluginPath == null) { if (pluginPath == null) {
logger.error("Plugins directory {} not found", pluginPath); logger.error("Plugins directory {} not found", pluginPath);
return; return;
@ -304,7 +304,7 @@ public class Course {
List<Plugin> plugins = new PluginsLoader(Paths.get(pluginPath), Paths.get(targetPath)).loadPlugins(true); List<Plugin> plugins = new PluginsLoader(Paths.get(pluginPath), Paths.get(targetPath)).loadPlugins(true);
for (Plugin plugin : plugins) { for (Plugin plugin : plugins) {
try { try {
AbstractLesson lesson = plugin.getLesson(); AbstractLesson lesson = plugin.getLesson().get();
lesson.setWebgoatContext(webgoatContext); lesson.setWebgoatContext(webgoatContext);
lesson.update(properties); lesson.update(properties);

View File

@ -48,7 +48,7 @@ public class LabelProvider
{ {
if (!labels.containsKey(locale)) if (!labels.containsKey(locale))
{ {
ClassLoader classLoader = ResourceBundleClassLoader.createPropertyFilesClassLoader(ResourceBundle.class.getClassLoader()); ClassLoader classLoader = ResourceBundleClassLoader.createPropertyFilesClassLoader();
ResourceBundle resBundle = ResourceBundle.getBundle("WebGoatLabels", locale, classLoader, localeController); ResourceBundle resBundle = ResourceBundle.getBundle("WebGoatLabels", locale, classLoader, localeController);
labels.put(locale, resBundle); labels.put(locale, resBundle);
} }

View File

@ -1,2 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/WebGoat"/> <Context antiJARLocking="true" path="/WebGoat">
<Loader delegate="true" loaderClass="org.owasp.webgoat.classloader.PluginClassLoader" searchExternalFirst="true"/>
</Context>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/WebGoat">
<Loader delegate="true" loaderClass="org.owasp.webgoat.classloader.PluginClassLoader" searchExternalFirst="true"/>
</Context>

View File

@ -23,7 +23,7 @@ public class GlobalPropertiesTest {
new GlobalProperties(pluginDirectory).loadProperties(directory); new GlobalProperties(pluginDirectory).loadProperties(directory);
ClassLoader propertyFilesClassLoader = ClassLoader propertyFilesClassLoader =
ResourceBundleClassLoader.createPropertyFilesClassLoader(this.getClass().getClassLoader()); ResourceBundleClassLoader.createPropertyFilesClassLoader();
assertNotNull(propertyFilesClassLoader.getResourceAsStream("global.properties")); assertNotNull(propertyFilesClassLoader.getResourceAsStream("global.properties"));
} }

View File

@ -1,45 +1,32 @@
package org.owasp.webgoat.plugins; package org.owasp.webgoat.plugins;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.containsString;
import static org.junit.matchers.JUnitMatchers.hasItem;
public class PluginTest { public class PluginTest {
@Test // @Test
public void pathShouldBeRewrittenInHtmlFile() throws Exception { // public void pathShouldBeRewrittenInHtmlFile() throws Exception {
Path tmpDir = PluginTestHelper.createTmpDir(); // Path tmpDir = PluginTestHelper.createTmpDir();
Path pluginSourcePath = PluginTestHelper.pathForLoading(); // Path pluginSourcePath = PluginTestHelper.pathForLoading();
Plugin plugin = PluginTestHelper.createPluginFor(TestPlugin.class); // Plugin plugin = PluginTestHelper.createPluginFor(TestPlugin.class);
Path htmlFile = Paths.get(pluginSourcePath.toString(), "lessonSolutions", "rewrite_test.html"); // Path htmlFile = Paths.get(pluginSourcePath.toString(), "lessonSolutions", "rewrite_test.html");
plugin.loadFiles(Arrays.asList(htmlFile), true); // plugin.loadFiles(Arrays.asList(htmlFile), true);
plugin.rewritePaths(tmpDir); // plugin.rewritePaths(tmpDir);
List<String> allLines = Files.readAllLines(htmlFile, StandardCharsets.UTF_8); // List<String> allLines = Files.readAllLines(htmlFile, StandardCharsets.UTF_8);
//
assertThat(allLines, // assertThat(allLines,
hasItem(containsString("plugin/TestPlugin/lessonSolutions/en/TestPlugin_files/image001.png"))); // hasItem(containsString("plugin/TestPlugin/lessonSolutions/en/TestPlugin_files/image001.png")));
} // }
//
@Test // @Test
public void shouldNotRewriteOtherLinks() throws Exception { // public void shouldNotRewriteOtherLinks() throws Exception {
Path tmpDir = PluginTestHelper.createTmpDir(); // Path tmpDir = PluginTestHelper.createTmpDir();
Path pluginSourcePath = PluginTestHelper.pathForLoading(); // Path pluginSourcePath = PluginTestHelper.pathForLoading();
Plugin plugin = PluginTestHelper.createPluginFor(TestPlugin.class); // Plugin plugin = PluginTestHelper.createPluginFor(TestPlugin.class);
Path htmlFile = Paths.get(pluginSourcePath.toString(), "lessonSolutions", "rewrite_test.html"); // Path htmlFile = Paths.get(pluginSourcePath.toString(), "lessonSolutions", "rewrite_test.html");
plugin.loadFiles(Arrays.asList(htmlFile), true); // plugin.loadFiles(Arrays.asList(htmlFile), true);
plugin.rewritePaths(tmpDir); // plugin.rewritePaths(tmpDir);
List<String> allLines = Files.readAllLines(htmlFile, StandardCharsets.UTF_8); // List<String> allLines = Files.readAllLines(htmlFile, StandardCharsets.UTF_8);
//
assertThat(allLines, // assertThat(allLines,
hasItem(containsString("Unknown_files/image001.png"))); // hasItem(containsString("Unknown_files/image001.png")));
} // }
} }

View File

@ -5,8 +5,6 @@ import java.net.URISyntaxException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class PluginTestHelper { public class PluginTestHelper {
@ -23,12 +21,11 @@ public class PluginTestHelper {
return Paths.get(path.toString(), "org/owasp/webgoat/plugins"); return Paths.get(path.toString(), "org/owasp/webgoat/plugins");
} }
public static Plugin createPluginFor(Class pluginClass) throws Exception { // public static Plugin createPluginFor(Class pluginClass) throws Exception {
Path pluginTargetPath = Files.createDirectory(Paths.get(tempDirectory.toString(), "pluginTargetPath")); // Path pluginTargetPath = Files.createDirectory(Paths.get(tempDirectory.toString(), "pluginTargetPath"));
Plugin plugin = new Plugin(pluginTargetPath); // Map<String, byte[]> classes = new HashMap<>();
Map<String, byte[]> classes = new HashMap<>(); // classes.put(pluginClass.getName(), Files.readAllBytes(Paths.get(pathForLoading().toString(), pluginClass.getSimpleName() + ".class")));
classes.put(pluginClass.getName(), Files.readAllBytes(Paths.get(pathForLoading().toString(), pluginClass.getSimpleName() + ".class"))); // Plugin plugin = new Plugin(pluginTargetPath, classes);
plugin.loadClasses(classes); // return plugin;
return plugin; // }
}
} }

4
webgoat-classloader/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
target/
.idea/
*.iml
dependency-reduced-pom.xml

View File

@ -0,0 +1,18 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<name>WebGoat</name>
<modelVersion>4.0.0</modelVersion>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-classloader</artifactId>
<packaging>jar</packaging>
<version>6.1.0</version>
<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>7.0.47</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,31 @@
package org.owasp.webgoat.classloader;
import org.apache.catalina.loader.WebappClassLoader;
import java.net.URL;
import java.util.List;
/**
* Classloader for Tomcat.
*
* We need to provide this classloader otherwise jsp files cannot be compiled. JspContextWrapper uses
* Thread.currentThread().getContextClassLoader() but during initialisation it loads the classloader which means
* this classloader will never pickup the plugin classes.
*
* With this loader we can add jars we load during the plugin loading and the jsp will pick it up because this is
* the same classloader.
*/
public class PluginClassLoader extends WebappClassLoader {
public PluginClassLoader() {
}
public PluginClassLoader(ClassLoader parent) {
super(parent);
}
public void addURL(List<URL> urls) {
for (URL url : urls) {
super.addURL(url);
}
}
}

4
webgoat-release/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
target/
.idea/
*.iml
dependency-reduced-pom.xml

18
webgoat-release/README.md Normal file
View File

@ -0,0 +1,18 @@
# Releasing WebGoat
## Introduction
This project will create a release for WebGoat ready for distribution.
This project creates a war with all the lessons included.
## Details
The following steps happen during the release:
* Download the webgoat-container.war from the repository
* Unpack the war
* Download the dist-plugin.zip from the repository
* Unpack the lessons
* Build the war again (webgoat-release-${version}.war)
* Create the executable jar (webgoat-release-${version}-war-exec.jar)

124
webgoat-release/pom.xml Normal file
View File

@ -0,0 +1,124 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<name>WebGoat</name>
<modelVersion>4.0.0</modelVersion>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-release</artifactId>
<packaging>war</packaging>
<version>6.1.0</version>
<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<name>Java.net Maven 2 Repository</name>
<url>http://download.java.net/maven/2</url>
</repository>
</repositories>
<!-- Shared version number properties -->
<properties>
<tiles.version>2.2.2</tiles.version>
<!-- If run from Bamboo this will be replaced with the bamboo build number -->
<build.number>local</build.number>
<lessons.version>1.0</lessons.version>
<war.output.dir>${project.build.directory}/war/</war.output.dir>
<lessons.output.dir>${war.output.dir}/plugin_lessons</lessons.output.dir>
</properties>
<!--
Step 1: Unpack the container WAR file
Step 2: Use the zip file and unpack it
Step 3: Build a new WAR file and install it in the repository
-->
<build>
<plugins>
<!-- Unpack the container.war and dist-plugins.jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>unpack-war</id>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-container</artifactId>
<version>${project.version}</version>
<type>war</type>
</artifactItem>
</artifactItems>
<outputDirectory>${war.output.dir}</outputDirectory>
</configuration>
</execution>
<execution>
<id>unpack-lessons-zip</id>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<skip>false</skip>
<artifactItems>
<artifactItem>
<includes>**/*.jar</includes>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>dist</artifactId>
<version>${lessons.version}</version>
<type>zip</type>
<classifier>plugins</classifier>
</artifactItem>
</artifactItems>
<outputDirectory>${lessons.output.dir}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- Create the war -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<warSourceDirectory>${war.output.dir}</warSourceDirectory>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Specification-Title>${project.name}</Specification-Title>
<Specification-Version>${project.version}</Specification-Version>
<Implementation-Version>${build.number}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
<!-- Create the executable jar -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<url>http://localhost:8080/manager</url>
<path>/WebGoat</path>
<attachArtifactClassifier>exec</attachArtifactClassifier>
</configuration>
<executions>
<execution>
<id>tomcat-run</id>
<goals>
<goal>exec-war-only</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>