diff --git a/pom.xml b/pom.xml index 92f26f2ae..af60fd088 100644 --- a/pom.xml +++ b/pom.xml @@ -132,9 +132,11 @@ 3.4 1.2 4.0.0 + 2.2.5 + 2.2.4 18.0 1.4.190 - 1.8.0.10 + 2.3.2 1.3.1 2.6.3 2.6.3 @@ -155,6 +157,7 @@ 3.0.1 2.19 1.6.6 + 2.11.7 2.1.20 2.48.2 1.7.12 diff --git a/webgoat-container/pom.xml b/webgoat-container/pom.xml index 095d2fcfc..f9e4f76ae 100644 --- a/webgoat-container/pom.xml +++ b/webgoat-container/pom.xml @@ -14,6 +14,29 @@ + + + performance + + + + io.gatling + gatling-maven-plugin + ${gatling-plugin.version} + + + + execute + + + + + + + + + + @@ -117,15 +140,24 @@ 1.5.4 - org.liquibase - liquibase-core - 3.4.1 + org.springframework.boot + spring-boot-starter-data-mongodb + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo org.apache.commons commons-lang3 ${commons-lang3.version} + + io.gatling.highcharts + gatling-charts-highcharts + ${gatling.version} + test + javax.servlet jstl @@ -162,7 +194,7 @@ ${h2.version} - hsqldb + org.hsqldb hsqldb ${hsqldb.version} @@ -171,6 +203,11 @@ javax.transaction-api ${javax.transaction-api.version} + + org.scala-lang + scala-compiler + ${scala.version} + @@ -186,7 +223,6 @@ - diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/CleanupLocalProgressFiles.java b/webgoat-container/src/main/java/org/owasp/webgoat/CleanupLocalProgressFiles.java new file mode 100644 index 000000000..9200a8a5a --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/CleanupLocalProgressFiles.java @@ -0,0 +1,36 @@ +package org.owasp.webgoat; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.io.File; + +/** + * @author nbaars + * @since 4/15/17. + */ +@Slf4j +@Configuration +@ConditionalOnExpression("'${webgoat.clean}' == 'true'") +public class CleanupLocalProgressFiles { + + @Value("${webgoat.server.directory}") + private String webgoatHome; + + @PostConstruct + public void clean() { + File dir = new File(webgoatHome); + if (dir.exists()) { + File[] progressFiles = dir.listFiles(f -> f.getName().endsWith(".progress")); + if (progressFiles != null) { + log.info("Removing stored user preferences..."); + for (File f : progressFiles) { + f.delete(); + } + } + } + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java b/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java index 514492360..1c949c85e 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java @@ -71,6 +71,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter { registry.addViewController("/login").setViewName("login"); registry.addViewController("/lesson_content").setViewName("lesson_content"); registry.addViewController("/start.mvc").setViewName("main_new"); + registry.addViewController("/scoreboard").setViewName("scoreboard"); } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java b/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java index 9e88dec97..58b269168 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/WebGoat.java @@ -31,12 +31,14 @@ package org.owasp.webgoat; import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.Context; import org.owasp.webgoat.plugins.PluginEndpointPublisher; import org.owasp.webgoat.plugins.PluginsLoader; -import org.owasp.webgoat.session.*; +import org.owasp.webgoat.session.Course; +import org.owasp.webgoat.session.UserSessionData; +import org.owasp.webgoat.session.WebSession; +import org.owasp.webgoat.session.WebgoatContext; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -104,15 +106,6 @@ public class WebGoat extends SpringBootServletInitializer { return new PluginsLoader(pluginEndpointPublisher).loadPlugins(); } - @Bean - @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) - @SneakyThrows - public UserTracker userTracker(@Value("${webgoat.user.directory}") final String webgoatHome, WebSession webSession) { - UserTracker userTracker = new UserTracker(webgoatHome, webSession.getUserName()); - userTracker.load(); - return userTracker; - } - @Bean public EmbeddedServletContainerFactory servletContainer() { TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory(); @@ -127,4 +120,5 @@ public class WebGoat extends SpringBootServletInitializer { } } + } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentEndpoint.java b/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentEndpoint.java index f44b1cfd0..584ed7155 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentEndpoint.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentEndpoint.java @@ -27,7 +27,8 @@ package org.owasp.webgoat.assignments; import lombok.Getter; import org.owasp.webgoat.i18n.PluginMessages; import org.owasp.webgoat.session.UserSessionData; -import org.owasp.webgoat.session.UserTracker; +import org.owasp.webgoat.users.UserTracker; +import org.owasp.webgoat.users.UserTrackerRepository; import org.owasp.webgoat.session.WebSession; import org.springframework.beans.factory.annotation.Autowired; @@ -43,7 +44,7 @@ import org.springframework.beans.factory.annotation.Autowired; public abstract class AssignmentEndpoint extends Endpoint { @Autowired - private UserTracker userTracker; + private UserTrackerRepository userTrackerRepository; @Autowired private WebSession webSession; @Autowired @@ -54,11 +55,16 @@ public abstract class AssignmentEndpoint extends Endpoint { //// TODO: 11/13/2016 events better fit? protected AttackResult trackProgress(AttackResult attackResult) { + UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName()); + if (userTracker == null) { + userTracker = new UserTracker(webSession.getUserName()); + } if (attackResult.assignmentSolved()) { userTracker.assignmentSolved(webSession.getCurrentLesson(), this.getClass().getSimpleName()); } else { userTracker.assignmentFailed(webSession.getCurrentLesson()); } + userTrackerRepository.save(userTracker); return attackResult; } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AttackResult.java b/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AttackResult.java index 4cf1dbad8..b10917d49 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AttackResult.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AttackResult.java @@ -51,6 +51,12 @@ public class AttackResult { return this; } + public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) { + this.lessonCompleted = lessonCompleted; + this.feedbackResourceBundleKey = resourceBundleKey; + return this; + } + public AttackResultBuilder feedbackArgs(Object... args) { this.feedbackArgs = args; return this; diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Assignment.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Assignment.java index 1df91dc66..bbd993c77 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Assignment.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Assignment.java @@ -1,11 +1,7 @@ package org.owasp.webgoat.lessons; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; +import lombok.*; -import java.io.Serializable; import java.util.List; /** @@ -39,14 +35,14 @@ import java.util.List; */ @AllArgsConstructor @RequiredArgsConstructor +@NoArgsConstructor @Getter -public class Assignment implements Serializable { - - private static final long serialVersionUID = 5410058267505412928L; +@EqualsAndHashCode +public class Assignment { @NonNull - private final String name; + private String name; @NonNull - private final String path; + private String path; private List hints; } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Category.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Category.java index b24015e61..6c3a5833b 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Category.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Category.java @@ -57,7 +57,7 @@ public enum Category { WEB_SERVICES("Web Services", new Integer(1900)), VULNERABLE_COMPONENTS("Vulnerable Components - A9", new Integer(1950)), ADMIN_FUNCTIONS("Admin Functions", new Integer(2000)), - CHALLENGE("Challenge", new Integer(3000)); + CHALLENGE("Challenges", new Integer(3000)); @Getter private String name; diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginResource.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginResource.java index c84f07da2..172b7b965 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginResource.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginResource.java @@ -8,7 +8,6 @@ import org.owasp.webgoat.lessons.NewLesson; import java.net.URL; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; /** @@ -24,21 +23,22 @@ public class PluginResource { private final URL location; private final List classes; - public Optional getLesson() { - return classes.stream().filter(c -> c.getSuperclass() == NewLesson.class).findFirst(); + public List getLessons() { + return classes.stream().filter(c -> c.getSuperclass() == NewLesson.class).collect(Collectors.toList()); } public List> getEndpoints() { return classes.stream(). filter(c -> c.getSuperclass() == AssignmentEndpoint.class || c.getSuperclass() == Endpoint.class). - map(c -> (Class)c). + map(c -> (Class) c). collect(Collectors.toList()); } - public List> getAssignments() { + public List> getAssignments(Class lesson) { return classes.stream(). filter(c -> c.getSuperclass() == AssignmentEndpoint.class). - map(c -> (Class)c). + filter(c -> c.getPackage().equals(lesson.getPackage())). + map(c -> (Class) c). collect(Collectors.toList()); } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java index 50a255381..20e193025 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java @@ -67,13 +67,18 @@ public class PluginsLoader { List lessons = Lists.newArrayList(); for (PluginResource plugin : findPluginResources()) { try { - Class lessonClazz = plugin.getLesson() - .orElseThrow(() -> new PluginLoadingFailure("Plugin resource does not contain lesson")); - NewLesson lesson = (NewLesson) lessonClazz.newInstance(); - List> assignments = plugin.getAssignments(); - lesson.setAssignments(createAssignment(assignments)); - lessons.add(lesson); - pluginEndpointPublisher.publish(plugin.getEndpoints()); + plugin.getLessons().forEach(c -> { + NewLesson lesson = null; + try { + lesson = (NewLesson) c.newInstance(); + } catch (Exception e) { + log.error("Error while loading:" + c, e); + } + List> assignments = plugin.getAssignments(c); + lesson.setAssignments(createAssignment(assignments)); + lessons.add(lesson); + pluginEndpointPublisher.publish(plugin.getEndpoints()); + }); } catch (Exception e) { log.error("Error in loadLessons: ", e); } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonMenuService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonMenuService.java index 4a2e22077..097085c48 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonMenuService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonMenuService.java @@ -34,15 +34,18 @@ import org.owasp.webgoat.lessons.Category; import org.owasp.webgoat.lessons.LessonMenuItem; import org.owasp.webgoat.lessons.LessonMenuItemType; import org.owasp.webgoat.session.Course; -import org.owasp.webgoat.session.LessonTracker; -import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.WebSession; +import org.owasp.webgoat.users.LessonTracker; +import org.owasp.webgoat.users.UserTracker; +import org.owasp.webgoat.users.UserTrackerRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; /** *

LessonMenuService class.

@@ -54,20 +57,23 @@ import java.util.List; @AllArgsConstructor public class LessonMenuService { + public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc"; private final Course course; - private UserTracker userTracker; + private final WebSession webSession; + private UserTrackerRepository userTrackerRepository; /** * Returns the lesson menu which is used to build the left nav * * @return a {@link java.util.List} object. */ - @RequestMapping(path = "/service/lessonmenu.mvc", produces = "application/json") + @RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json") public @ResponseBody List showLeftNav() { - List menu = new ArrayList(); + List menu = new ArrayList<>(); List categories = course.getCategories(); + UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName()); for (Category category : categories) { LessonMenuItem categoryItem = new LessonMenuItem(); @@ -75,6 +81,7 @@ public class LessonMenuService { categoryItem.setType(LessonMenuItemType.CATEGORY); // check for any lessons for this category List lessons = course.getLessons(category); + lessons = lessons.stream().sorted(Comparator.comparing(l -> l.getTitle())).collect(Collectors.toList()); for (AbstractLesson lesson : lessons) { LessonMenuItem lessonItem = new LessonMenuItem(); lessonItem.setName(lesson.getTitle()); diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonProgressService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonProgressService.java index 2a6387ac2..fb4fe0071 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonProgressService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonProgressService.java @@ -7,9 +7,10 @@ import lombok.Getter; import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.lessons.Assignment; import org.owasp.webgoat.lessons.LessonInfoModel; -import org.owasp.webgoat.session.LessonTracker; -import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.WebSession; +import org.owasp.webgoat.users.LessonTracker; +import org.owasp.webgoat.users.UserTracker; +import org.owasp.webgoat.users.UserTrackerRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @@ -28,7 +29,7 @@ import java.util.Map; @AllArgsConstructor public class LessonProgressService { - private UserTracker userTracker; + private UserTrackerRepository userTrackerRepository; private WebSession webSession; /** @@ -39,6 +40,7 @@ public class LessonProgressService { @RequestMapping(value = "/service/lessonprogress.mvc", produces = "application/json") @ResponseBody public Map getLessonInfo() { + UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName()); LessonTracker lessonTracker = userTracker.getLessonTracker(webSession.getCurrentLesson()); Map json = Maps.newHashMap(); String successMessage = ""; @@ -61,6 +63,7 @@ public class LessonProgressService { @RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json") @ResponseBody public List lessonOverview() { + UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName()); AbstractLesson currentLesson = webSession.getCurrentLesson(); List result = Lists.newArrayList(); if ( currentLesson != null ) { diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/ReportCardService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/ReportCardService.java index 1fc17a5ba..21c8c1f20 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/ReportCardService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/ReportCardService.java @@ -29,25 +29,20 @@ package org.owasp.webgoat.service; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; -import lombok.Singular; -import org.apache.catalina.User; import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.session.Course; -import org.owasp.webgoat.session.LessonTracker; -import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.WebSession; +import org.owasp.webgoat.users.LessonTracker; +import org.owasp.webgoat.users.UserTracker; +import org.owasp.webgoat.users.UserTrackerRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** *

ReportCardService

@@ -56,22 +51,20 @@ import java.util.Map; * @version $Id: $Id */ @Controller +@AllArgsConstructor public class ReportCardService { - private final UserTracker userTracker; + private final WebSession webSession; + private final UserTrackerRepository userTrackerRepository; private final Course course; - public ReportCardService(UserTracker userTracker, Course course) { - this.userTracker = userTracker; - this.course = course; - } - /** * Endpoint which generates the report card for the current use to show the stats on the solved lessons */ @GetMapping(path = "/service/reportcard.mvc", produces = "application/json") @ResponseBody public ReportCard reportCard() { + UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName()); List lessons = course.getLessons(); ReportCard reportCard = new ReportCard(); reportCard.setTotalNumberOfLessons(course.getTotalOfLessons()); diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/RestartLessonService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/RestartLessonService.java index 34650cc1c..4ea036996 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/RestartLessonService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/RestartLessonService.java @@ -26,8 +26,9 @@ package org.owasp.webgoat.service; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.lessons.AbstractLesson; -import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.WebSession; +import org.owasp.webgoat.users.UserTracker; +import org.owasp.webgoat.users.UserTrackerRepository; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -45,7 +46,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; public class RestartLessonService { private final WebSession webSession; - private final UserTracker userTracker; + private UserTrackerRepository userTrackerRepository; /** * Returns current lesson @@ -58,6 +59,8 @@ public class RestartLessonService { AbstractLesson al = webSession.getCurrentLesson(); log.debug("Restarting lesson: " + al); + UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName()); userTracker.reset(al); + userTrackerRepository.save(userTracker); } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/session/DatabaseUtilities.java b/webgoat-container/src/main/java/org/owasp/webgoat/session/DatabaseUtilities.java index 4692528b4..1d015ff3d 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/session/DatabaseUtilities.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/session/DatabaseUtilities.java @@ -7,8 +7,6 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.Map; -import org.springframework.beans.factory.annotation.Autowired; - /** ************************************************************************************************* @@ -39,6 +37,8 @@ import org.springframework.beans.factory.annotation.Autowired; * @author Jeff Williams Aspect Security * @version $Id: $Id */ +//TODO: class we need to refactor to new structure, we can put the connection in the current session of the user + // start using jdbc template public class DatabaseUtilities { @@ -122,7 +122,7 @@ public class DatabaseUtilities private static Connection getHsqldbConnection(String user, WebgoatContext context) throws ClassNotFoundException, SQLException { - String url = context.getDatabaseConnectionString().replaceAll("\\$\\{USER\\}", user); + String url = context.getDatabaseConnectionString().replace("{USER}", user); return DriverManager.getConnection(url, "sa", ""); } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/session/UserDatabase.java b/webgoat-container/src/main/java/org/owasp/webgoat/session/UserDatabase.java deleted file mode 100644 index 09e542487..000000000 --- a/webgoat-container/src/main/java/org/owasp/webgoat/session/UserDatabase.java +++ /dev/null @@ -1,181 +0,0 @@ -package org.owasp.webgoat.session; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -class UserDatabase { - private Connection userDB; - private final String USER_DB_URI = "jdbc:h2:" + System.getProperty("user.dir") + File.separator + "UserDatabase"; - - private final String CREATE_USERS_TABLE = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTO_INCREMENT, username VARCHAR(255) NOT NULL UNIQUE);"; - private final String CREATE_ROLES_TABLE = "CREATE TABLE IF NOT EXISTS roles (id INTEGER PRIMARY KEY AUTO_INCREMENT, rolename VARCHAR(255) NOT NULL UNIQUE);"; - private final String CREATE_USER_ROLES_TABLE = "CREATE TABLE IF NOT EXISTS user_roles (id INTEGER PRIMARY KEY AUTO_INCREMENT, user_id INTEGER NOT NULL, role_id INTEGER NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (role_id) REFERENCES roles(id));"; - private final String ADD_DEFAULT_USERS = "INSERT INTO users (username) VALUES ('webgoat'),('basic'),('guest');"; - private final String ADD_DEFAULT_ROLES = "INSERT INTO roles (rolename) VALUES ('webgoat_basic'),('webgoat_admin'),('webgoat_user');"; - private final String ADD_ROLE_TO_USER = "INSERT INTO user_roles (user_id, role_id) SELECT users.id, roles.id FROM users, roles WHERE users.username = ? AND roles.rolename = ?;"; - - private final String QUERY_ALL_USERS = "SELECT username FROM users;"; - private final String QUERY_ALL_ROLES_FOR_USERNAME = "SELECT rolename FROM roles, user_roles, users WHERE roles.id = user_roles.role_id AND user_roles.user_id = users.id AND users.username = ?;"; - private final String QUERY_TABLE_COUNT = "SELECT count(id) AS count FROM table;"; - - /** - *

Constructor for UserDatabase.

- */ - public UserDatabase() { - createDefaultTables(); - if (getTableCount("users") <= 0) { - createDefaultUsers(); - } - if (getTableCount("roles") <= 0) { - createDefaultRoles(); - } - if (getTableCount("user_roles") <= 0) { - addDefaultRolesToDefaultUsers(); - } - } - - /** - *

open.

- * - * @return a boolean. - */ - public boolean open() { - try { - if (userDB == null || userDB.isClosed()) { - Class.forName("org.h2.Driver"); - userDB = DriverManager.getConnection(USER_DB_URI, "webgoat_admin", ""); - } - } catch (SQLException e) { - e.printStackTrace(); - return false; - } catch (ClassNotFoundException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - *

close.

- * - * @return a boolean. - */ - public boolean close() { - try { - if (userDB != null && !userDB.isClosed()) - userDB.close(); - } catch (SQLException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - *

getTableCount.

- * - * @param tableName a {@link java.lang.String} object. - * @return a int. - */ - public int getTableCount(String tableName) { - int count = 0; - try { - open(); - Statement statement = userDB.createStatement(); - ResultSet countResult = statement.executeQuery(QUERY_TABLE_COUNT.replace("table", tableName)); - if (countResult.next()) { - count = countResult.getInt("count"); - } - countResult.close(); - statement.close(); - close(); - } catch (SQLException e) { - e.printStackTrace(); - count = -1; - } - return count; - } - - /** - *

addRoleToUser.

- * - * @param username a {@link java.lang.String} object. - * @param rolename a {@link java.lang.String} object. - * @return a boolean. - */ - public boolean addRoleToUser(String username, String rolename) { - try { - open(); - PreparedStatement statement = userDB.prepareStatement(ADD_ROLE_TO_USER); - statement.setString(1, username); - statement.setString(2, rolename); - statement.execute(); - statement.close(); - close(); - } catch (SQLException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /* - * Methods to initialise the default state of the database. - */ - - private boolean createDefaultTables() { - try { - open(); - Statement statement = userDB.createStatement(); - statement.execute(CREATE_USERS_TABLE); - statement.execute(CREATE_ROLES_TABLE); - statement.execute(CREATE_USER_ROLES_TABLE); - statement.close(); - close(); - } catch (SQLException e) { - e.printStackTrace(); - return false; - } - return true; - } - - private boolean createDefaultUsers() { - try { - open(); - Statement statement = userDB.createStatement(); - statement.execute(ADD_DEFAULT_USERS); - statement.close(); - close(); - } catch (SQLException e) { - e.printStackTrace(); - return false; - } - return true; - } - - private boolean createDefaultRoles() { - try { - open(); - Statement statement = userDB.createStatement(); - statement.execute(ADD_DEFAULT_ROLES); - statement.close(); - close(); - } catch (SQLException e) { - e.printStackTrace(); - return false; - } - return true; - } - - private void addDefaultRolesToDefaultUsers() { - addRoleToUser("webgoat", "webgoat_admin"); - addRoleToUser("basic", "webgoat_user"); - addRoleToUser("basic", "webgoat_basic"); - addRoleToUser("guest", "webgoat_user"); - } -} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/session/UserTracker.java b/webgoat-container/src/main/java/org/owasp/webgoat/session/UserTracker.java deleted file mode 100644 index a25abd00f..000000000 --- a/webgoat-container/src/main/java/org/owasp/webgoat/session/UserTracker.java +++ /dev/null @@ -1,154 +0,0 @@ - -package org.owasp.webgoat.session; - -import com.google.common.collect.Maps; -import com.google.common.io.ByteStreams; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.lessons.AbstractLesson; -import org.owasp.webgoat.lessons.Assignment; -import org.springframework.core.serializer.DefaultDeserializer; - -import java.io.*; -import java.util.Map; -import java.util.stream.Collectors; - - -/** - * ************************************************************************************************ - *

- *

- * 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. - * - * @author Bruce Mayhew WebGoat - * @version $Id: $Id - * @since October 29, 2003 - */ -@Slf4j -public class UserTracker { - - private final String webgoatHome; - private final String user; - - public UserTracker(final String webgoatHome, final String user) { - this.webgoatHome = webgoatHome; - this.user = user; - } - - /** - * Returns the lesson tracker for a specific lesson if available. - * - * @param lesson the lesson - * @return the optional lesson tracker - */ - public LessonTracker getLessonTracker(AbstractLesson lesson) { - return getLessonTracker(load(), lesson); - } - - /** - * Returns the lesson tracker for a specific lesson if available. - * - * @param lesson the lesson - * @return the optional lesson tracker - */ - public LessonTracker getLessonTracker(Map storage, AbstractLesson lesson) { - LessonTracker lessonTracker = storage.get(lesson.getTitle()); - if (lessonTracker == null) { - lessonTracker = new LessonTracker(lesson); - storage.put(lesson.getTitle(), lessonTracker); - save(storage); - } - return lessonTracker; - } - - public void assignmentSolved(AbstractLesson lesson, String assignmentName) { - Map storage = load(); - LessonTracker lessonTracker = storage.get(lesson.getTitle()); - lessonTracker.incrementAttempts(); - lessonTracker.assignmentSolved(assignmentName); - save(storage); - } - - public void assignmentFailed(AbstractLesson lesson) { - Map storage = load(); - LessonTracker lessonTracker = storage.get(lesson.getTitle()); - lessonTracker.incrementAttempts(); - save(storage); - } - - public Map load() { - File file = new File(webgoatHome, user + ".progress"); - Map storage = Maps.newHashMap(); - if (file.exists() && file.isFile()) { - try { - DefaultDeserializer deserializer = new DefaultDeserializer(Thread.currentThread().getContextClassLoader()); - try (FileInputStream fis = new FileInputStream(file)) { - byte[] b = ByteStreams.toByteArray(fis); - storage = (Map) deserializer.deserialize(new ByteArrayInputStream(b)); - } - } catch (Exception e) { - log.error("Unable to read the progress file, creating a new one..."); - } - } - return storage; - } - - @SneakyThrows - private void save(Map storage) { - File file = new File(webgoatHome, user + ".progress"); - - try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file))) { - objectOutputStream.writeObject(storage); - objectOutputStream.flush(); - } - } - - - public void reset(AbstractLesson al) { - Map storage = load(); - LessonTracker lessonTracker = getLessonTracker(storage, al); - lessonTracker.reset(); - save(storage); - } - - public int numberOfLessonsSolved() { - int numberOfLessonsSolved = 0; - Map storage = load(); - for (LessonTracker lessonTracker : storage.values()) { - if (lessonTracker.isLessonSolved()) { - numberOfLessonsSolved = numberOfLessonsSolved + 1; - } - } - return numberOfLessonsSolved; - } - - public int numberOfAssignmentsSolved() { - int numberOfAssignmentsSolved = 0; - Map storage = load(); - for (LessonTracker lessonTracker : storage.values()) { - Map lessonOverview = lessonTracker.getLessonOverview(); - numberOfAssignmentsSolved = lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue(); - } - return numberOfAssignmentsSolved; - } -} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/session/WebSession.java b/webgoat-container/src/main/java/org/owasp/webgoat/session/WebSession.java index bc12af039..33196575a 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/session/WebSession.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/session/WebSession.java @@ -2,6 +2,7 @@ package org.owasp.webgoat.session; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.lessons.AbstractLesson; +import org.owasp.webgoat.users.WebGoatUser; import org.springframework.security.core.context.SecurityContextHolder; import java.sql.Connection; diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/session/LessonTracker.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/LessonTracker.java similarity index 93% rename from webgoat-container/src/main/java/org/owasp/webgoat/session/LessonTracker.java rename to webgoat-container/src/main/java/org/owasp/webgoat/users/LessonTracker.java index edd0d1483..bff30316e 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/session/LessonTracker.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/LessonTracker.java @@ -1,5 +1,5 @@ -package org.owasp.webgoat.session; +package org.owasp.webgoat.users; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -7,7 +7,6 @@ import lombok.Getter; import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.lessons.Assignment; -import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Optional; @@ -45,14 +44,20 @@ import java.util.stream.Collectors; * @version $Id: $Id * @since October 29, 2003 */ -public class LessonTracker implements Serializable { - private static final long serialVersionUID = 5410058267505412928L; +public class LessonTracker { + @Getter + private String lessonName; private final Set solvedAssignments = Sets.newHashSet(); private final List allAssignments = Lists.newArrayList(); @Getter private int numberOfAttempts = 0; + protected LessonTracker() { + //Mongo + } + public LessonTracker(AbstractLesson lesson) { + lessonName = lesson.getId(); allAssignments.addAll(lesson.getAssignments()); } @@ -61,7 +66,7 @@ public class LessonTracker implements Serializable { } /** - * Mark an assingment as solved + * Mark an assignment as solved * * @param solvedAssignment the assignment which the user solved */ diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/RegistrationController.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/RegistrationController.java index f4bf8fe61..5c7a4fff3 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/users/RegistrationController.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/RegistrationController.java @@ -2,7 +2,6 @@ package org.owasp.webgoat.users; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.session.WebGoatUser; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/Scoreboard.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/Scoreboard.java new file mode 100644 index 000000000..1b08e35bc --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/Scoreboard.java @@ -0,0 +1,61 @@ +package org.owasp.webgoat.users; + +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.owasp.webgoat.i18n.PluginMessages; +import org.owasp.webgoat.session.Course; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Temp endpoint just for the CTF. + * + * @author nbaars + * @since 3/23/17. + */ +@RestController +@AllArgsConstructor +public class Scoreboard { + + private final UserTrackerRepository userTrackerRepository; + private final UserRepository userRepository; + private final Course course; + private final PluginMessages pluginMessages; + + @AllArgsConstructor + @Getter + private class Ranking { + private String username; + private List flagsCaptured; + } + + @GetMapping("/scoreboard-data") + public List getRankings() { + List allUsers = userRepository.findAll(); + List rankings = Lists.newArrayList(); + for (WebGoatUser user : allUsers) { + UserTracker userTracker = userTrackerRepository.findOne(user.getUsername()); + rankings.add(new Ranking(user.getUsername(), challengesSolved(userTracker))); + } + return rankings; + } + + private List challengesSolved(UserTracker userTracker) { + List challenges = Lists.newArrayList("Challenge1", "Challenge2", "Challenge3", "Challenge4", "Challenge5"); + return challenges.stream() + .map(c -> userTracker.getLessonTracker(c)) + .filter(l -> l.isPresent()).map(l -> l.get()) + .map(l -> l.getLessonName()) + .map(l -> toLessonTitle(l)) + .collect(Collectors.toList()); + } + + private String toLessonTitle(String id) { + String titleKey = course.getLessons().stream().filter(l -> l.getId().equals(id)).findFirst().get().getTitle(); + return pluginMessages.getMessage(titleKey, titleKey); + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserRepository.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/UserRepository.java index d7d85db54..ae2f1063e 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserRepository.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/UserRepository.java @@ -1,13 +1,12 @@ package org.owasp.webgoat.users; -import org.owasp.webgoat.session.WebGoatUser; -import org.springframework.data.repository.CrudRepository; +import org.springframework.data.mongodb.repository.MongoRepository; /** * @author nbaars * @since 3/19/17. */ -public interface UserRepository extends CrudRepository { +public interface UserRepository extends MongoRepository { WebGoatUser findByUsername(String username); } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserService.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/UserService.java index 15a6cf6d4..7a1175c45 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/UserService.java @@ -1,7 +1,6 @@ package org.owasp.webgoat.users; import lombok.AllArgsConstructor; -import org.owasp.webgoat.session.WebGoatUser; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @@ -15,6 +14,7 @@ import org.springframework.stereotype.Service; public class UserService implements UserDetailsService { private final UserRepository userRepository; + private final UserTrackerRepository userTrackerRepository; @Override public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException { @@ -29,5 +29,6 @@ public class UserService implements UserDetailsService { public void addUser(String username, String password) { userRepository.save(new WebGoatUser(username, password)); + userTrackerRepository.save(new UserTracker(username)); } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserTracker.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/UserTracker.java new file mode 100644 index 000000000..c139d2571 --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/UserTracker.java @@ -0,0 +1,119 @@ + +package org.owasp.webgoat.users; + +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.lessons.AbstractLesson; +import org.owasp.webgoat.lessons.Assignment; +import org.springframework.data.annotation.Id; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + + +/** + * ************************************************************************************************ + *

+ *

+ * 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. + * + * @author Bruce Mayhew WebGoat + * @version $Id: $Id + * @since October 29, 2003 + */ +@Slf4j +public class UserTracker { + + @Id + private final String user; + private List lessonTrackers = Lists.newArrayList(); + + public UserTracker(final String user) { + this.user = user; + } + + /** + * Returns an existing lesson tracker or create a new one based on the lesson + * + * @param lesson the lesson + * @return a lesson tracker created if not already present + */ + public LessonTracker getLessonTracker(AbstractLesson lesson) { + Optional lessonTracker = lessonTrackers + .stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst(); + if (!lessonTracker.isPresent()) { + LessonTracker newLessonTracker = new LessonTracker(lesson); + lessonTrackers.add(newLessonTracker); + return newLessonTracker; + } else { + return lessonTracker.get(); + } + } + + /** + * Query method for finding a specific lesson tracker based on id + * + * @param id the id of the lesson + * @return optional due to the fact we can only create a lesson tracker based on a lesson + */ + public Optional getLessonTracker(String id) { + return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst(); + } + + public void assignmentSolved(AbstractLesson lesson, String assignmentName) { + LessonTracker lessonTracker = getLessonTracker(lesson); + lessonTracker.incrementAttempts(); + lessonTracker.assignmentSolved(assignmentName); + } + + public void assignmentFailed(AbstractLesson lesson) { + LessonTracker lessonTracker = getLessonTracker(lesson); + lessonTracker.incrementAttempts(); + } + + public void reset(AbstractLesson al) { + LessonTracker lessonTracker = getLessonTracker(al); + lessonTracker.reset(); + } + + public int numberOfLessonsSolved() { + int numberOfLessonsSolved = 0; + for (LessonTracker lessonTracker : lessonTrackers) { + if (lessonTracker.isLessonSolved()) { + numberOfLessonsSolved = numberOfLessonsSolved + 1; + } + } + return numberOfLessonsSolved; + } + + public int numberOfAssignmentsSolved() { + int numberOfAssignmentsSolved = 0; + for (LessonTracker lessonTracker : lessonTrackers) { + Map lessonOverview = lessonTracker.getLessonOverview(); + numberOfAssignmentsSolved = lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue(); + } + return numberOfAssignmentsSolved; + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserTrackerRepository.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/UserTrackerRepository.java new file mode 100644 index 000000000..f915154cb --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/UserTrackerRepository.java @@ -0,0 +1,12 @@ +package org.owasp.webgoat.users; + +import org.springframework.data.mongodb.repository.MongoRepository; + +/** + * @author nbaars + * @since 4/30/17. + */ +public interface UserTrackerRepository extends MongoRepository { + + +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/session/WebGoatUser.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/WebGoatUser.java similarity index 91% rename from webgoat-container/src/main/java/org/owasp/webgoat/session/WebGoatUser.java rename to webgoat-container/src/main/java/org/owasp/webgoat/users/WebGoatUser.java index 381617386..8b3c7c88c 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/session/WebGoatUser.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/WebGoatUser.java @@ -1,14 +1,13 @@ -package org.owasp.webgoat.session; +package org.owasp.webgoat.users; import lombok.Getter; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Transient; import java.util.Collection; import java.util.Collections; @@ -17,7 +16,6 @@ import java.util.Collections; * @since 3/19/17. */ @Getter -@Entity public class WebGoatUser implements UserDetails { public static final String ROLE_USER = "WEBGOAT_USER"; diff --git a/webgoat-container/src/main/resources/application.properties b/webgoat-container/src/main/resources/application.properties index 9d6ad7e56..4119f9887 100644 --- a/webgoat-container/src/main/resources/application.properties +++ b/webgoat-container/src/main/resources/application.properties @@ -16,6 +16,7 @@ security.enable-csrf=false spring.resources.cache-period=0 spring.thymeleaf.cache=false +webgoat.clean=true webgoat.server.directory=${user.home}/.webgoat/ webgoat.user.directory=${user.home}/.webgoat/ webgoat.build.version=@project.version@ @@ -25,18 +26,11 @@ webgoat.emaillist=owasp-webgoat@lists.owasp.org webgoat.feedback.address=webgoat@owasp.org webgoat.feedback.address.html=webgoat@owasp.org webgoat.database.driver=org.hsqldb.jdbcDriver -webgoat.database.connection.string=jdbc:hsqldb:mem:test -# TODO_NB -#webgoat.database.connection.string=jdbc:hsqldb:mem:${USER} +webgoat.database.connection.string=jdbc:hsqldb:mem:{USER} webgoat.default.language=en +spring.data.mongodb.database=webgoat +spring.mongodb.embedded.storage.databaseDir=${webgoat.user.directory}/mongodb/ -liquibase.change-log=classpath:db/changelog/db.changelog-master.xml -spring.datasource.url=jdbc:hsqldb:file:${user.home}/.webgoat/WebGoatDatabase;hsqldb.write_delay=false -spring.datasource.driverClassName=org.hsqldb.jdbcDriver -spring.datasource.username=sa -spring.datasource.password= -spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect -spring.jpa.show-sql=true -spring.jpa.hibernate.ddl-auto=none - +#For static file refresh ... and faster dev :D +spring.devtools.restart.additional-paths=webgoat-container/src/main/resources/static/js,webgoat-container/src/main/resources/static/css diff --git a/webgoat-container/src/main/resources/db/changelog/db.changelog-master.xml b/webgoat-container/src/main/resources/db/changelog/db.changelog-master.xml index f3b67826b..e2f25133e 100644 --- a/webgoat-container/src/main/resources/db/changelog/db.changelog-master.xml +++ b/webgoat-container/src/main/resources/db/changelog/db.changelog-master.xml @@ -6,9 +6,7 @@ - - - + diff --git a/webgoat-container/src/main/resources/static/css/img/appseceu-17.png b/webgoat-container/src/main/resources/static/css/img/appseceu-17.png new file mode 100644 index 000000000..4cab365f0 Binary files /dev/null and b/webgoat-container/src/main/resources/static/css/img/appseceu-17.png differ diff --git a/webgoat-container/src/main/resources/static/css/main.css b/webgoat-container/src/main/resources/static/css/main.css index f733b3d14..93edc7081 100644 --- a/webgoat-container/src/main/resources/static/css/main.css +++ b/webgoat-container/src/main/resources/static/css/main.css @@ -152,7 +152,7 @@ img { margin-left: 1.5em;*/ margin-right: 5px; margin-top: -38px; /* << don't like doing this, but otherwise it does not line up correctly */ - color:#0F0 + color:#88FB88 /* #0F0 */ } /* ========================================================================== @@ -958,8 +958,52 @@ cookie-container { cursor:pointer; } +.page-nav-wrapper { + display:inline-block; + width: 30px; +} + +.attack-link, .page-link { + display: inline-block; + background-color: #555; + border-radius: 8px; + min-width: 20px; + text-align: center; + font-weight: bold; + padding-top:2px; +} + +.attack-link.solved-true { + color:#88FB88; +} + +.attack-link.solved-false { + color:#f2baba; +} + +.attack-link.cur-page, .page-link.cur-page { + color:#fff; +} + +.page-link { + color:#eee; +} + +.page-link-wrapper { + display:inline-block; +} + +.page-link-wrapper span { + margin: 3px; +} + +.cur-page { + border-bottom: 2px solid #000; + color:#aaa; +} + span.show-next-page, span.show-prev-page { -font-size: 1.3em; + font-size: 1.3em; } .show-prev-page { @@ -970,6 +1014,8 @@ font-size: 1.3em; cursor:pointer; } +/* attack ... */ + .attack-feedback { font-weight:800; } @@ -984,10 +1030,11 @@ font-size: 1.3em; } #lesson-hint { - background-color: #ccc; + background-color: #f1f1f1; border-radius: 4px; - border-color: #999; - margin-top:4px; + border-color: #4fa44c; + margin-top: 4px; + border: 2px solid #24b054; } #hintsViewTop{ @@ -1043,6 +1090,60 @@ font-size: 1.3em; padding: 10px; } +/* temp override +//TODO: come up with longer term solution for full-window viewing +*/ .col-md-8 { - width: 95% !important; + width: 95% !important +} + +/* scoreboard */ +div.scoreboard-title { + font-size:xx-large; +} + +.scoreboard-table tr { +} + +div.scoreboard-username { + background-color: #222; + color: aliceblue; + padding: 4px; + padding-left:8px; + font-size: x-large; + border-radius:6px; +} + +th.username { + padding-bottom: 6px; +} + +td.user-flags { + padding-left: 8px; + padding-bottom: 6px; +} + +div.captured-flag { + border-radius: 6px; + background-color: #444; + color: white; + padding: 4px; + font-size: x-large; + display: inline-block; +} + +.scoreboard-page { + background-color: #e0dfdc; + padding: 20px; +} + +.fa-flag { + color:red +} + +.appseceu-banner { + background: url('img/appseceu-17.png') no-repeat 0px 0px; + height: 117px; + width: 1268px; + margin-bottom: 20px; } \ No newline at end of file diff --git a/webgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js b/webgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js index a4b93ff18..07a64c502 100644 --- a/webgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js +++ b/webgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js @@ -3,12 +3,11 @@ define(['jquery', 'libs/backbone', 'goatApp/model/LessonContentModel', 'goatApp/view/LessonContentView', - 'goatApp/view/PlanView', - 'goatApp/view/SourceView', - 'goatApp/view/SolutionView', +// 'goatApp/view/PlanView', +// 'goatApp/view/SourceView', +// 'goatApp/view/SolutionView', 'goatApp/view/HintView', 'goatApp/view/HelpControlsView', - 'goatApp/view/CookieView', 'goatApp/view/ParamView', 'goatApp/model/ParamModel', 'goatApp/view/DeveloperControlsView', @@ -27,12 +26,11 @@ define(['jquery', Backbone, LessonContentModel, LessonContentView, - PlanView, - SourceView, - SolutionView, +// PlanView, +// SourceView, +// SolutionView, HintView, HelpControlsView, - CookieView, ParamView, ParamModel, DeveloperControlsView, @@ -66,13 +64,29 @@ define(['jquery', this.menuButtonView = new MenuButtonView(); this.listenTo(this.lessonContentView, 'assignment:complete', this.updateMenu); this.listenTo(this.lessonContentView, 'assignment:complete', this.updateLessonOverview); + this.listenTo(this.lessonContentView, 'endpoints:filtered', this.filterPageHints); }; - this.loadLesson = function(name,pageNum) { + this.filterPageHints = function(endpoints) { + //filter hints for page by + this.lessonHintView.filterHints(endpoints); + } + this.onHideHintsButton = function() { + this.helpControlsView.hideHintsButton(); + } + + this.onShowHintsButton = function() { + this.helpControlsView.showHintsButton(); + } + + this.loadLesson = function(name,pageNum) { if (this.name === name) { + this.listenTo(this.lessonHintView, 'hints:showButton', this.onShowHintsButton); + this.listenTo(this.lessonHintView, 'hints:hideButton', this.onHideHintsButton); this.lessonContentView.navToPage(pageNum); this.lessonHintView.hideHints(); + //this.lessonHintView.selectHints(); this.titleView.render(this.lessonInfoModel.get('lessonTitle')); return; } @@ -82,10 +96,10 @@ define(['jquery', //TODO: implement lesson not found or return to welcome page? } this.lessonContent.loadData({'name':name}); - this.planView = {}; - this.solutionView = {}; - this.sourceView = {}; - this.lessonHintView = {}; +// this.planView = {}; +// this.solutionView = {}; +// this.sourceView = {}; +// this.lessonHintView = {}; this.name = name; }; @@ -98,15 +112,13 @@ define(['jquery', this.listenTo(this.helpControlsView,'hints:show',this.showHints); this.listenTo(this.helpControlsView,'lessonOverview:show',this.showLessonOverview) - this.listenTo(this.helpControlsView,'solution:show',this.hideShowHelps); - this.listenTo(this.helpControlsView,'source:show',this.hideShowHelps); + this.listenTo(this.helpControlsView,'lesson:restart',this.restartLesson); this.listenTo(this.developerControlsView, 'dev:labels', this.restartLesson); this.helpControlsView.render(); this.lessonOverview.hideLessonOverview(); this.titleView.render(this.lessonInfoModel.get('lessonTitle')); - this.helpControlsView.showHideHintsButton({}); }; this.updateMenu = function() { @@ -126,11 +138,14 @@ define(['jquery', this.lessonContentView.model = this.lessonContent; this.lessonContentView.render(); - this.planView = new PlanView(); - this.solutionView = new SolutionView(); - this.sourceView = new SourceView(); + //this.planView = new PlanView(); + //this.solutionView = new SolutionView(); + //this.sourceView = new SourceView(); + if (this.lessonHintView) { + this.lessonHintView.stopListening(); + this.lessonHintView = null; + } this.lessonHintView = new HintView(); - this.cookieView = new CookieView(); //TODO: instantiate model with values (not sure why was not working before) var paramModel = new ParamModel({}); @@ -150,34 +165,34 @@ define(['jquery', this.helpsLoaded[curHelp.helpElement] = curHelp.value; }; - this.hideShowHelps = function(showHelp) { - var showId = '#lesson-' + showHelp + '-row'; - var contentId = '#lesson-' + showHelp + '-content'; - $('.lesson-help').not(showId).hide(); - if (!showId) { - return; - } - - if ($(showId).is(':visible')) { - $(showId).hide(); - return; - } else { - //TODO: move individual .html operations into individual help views - switch(showHelp) { - case 'plan': - $(contentId).html(this.planView.model.get('content')); - break; - case 'solution': - $(showId).html(this.solutionView.model.get('content')); - break; - case 'source': - $(contentId).html('

' + this.sourceView.model.get('content') + '
'); - break; - } - $(showId).show(); - GoatUtils.scrollToHelp() - } - }; +// this.hideShowHelps = function(showHelp) { +// var showId = '#lesson-' + showHelp + '-row'; +// var contentId = '#lesson-' + showHelp + '-content'; +// $('.lesson-help').not(showId).hide(); +// if (!showId) { +// return; +// } +// +// if ($(showId).is(':visible')) { +// $(showId).hide(); +// return; +// } else { +// //TODO: move individual .html operations into individual help views +// switch(showHelp) { +// case 'plan': +// $(contentId).html(this.planView.model.get('content')); +// break; +// case 'solution': +// $(showId).html(this.solutionView.model.get('content')); +// break; +// case 'source': +// $(contentId).html('
' + this.sourceView.model.get('content') + '
'); +// break; +// } +// $(showId).show(); +// GoatUtils.scrollToHelp() +// } +// }; this.showHints = function() { this.lessonHintView.render(); @@ -194,6 +209,7 @@ define(['jquery', method:'GET' }).done(function(lessonLink) { self.loadLesson(self.name); + self.updateMenu(); }); }; diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/CookieModel.js b/webgoat-container/src/main/resources/static/js/goatApp/model/FlagModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/CookieModel.js rename to webgoat-container/src/main/resources/static/js/goatApp/model/FlagModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/CookieCollection.js b/webgoat-container/src/main/resources/static/js/goatApp/model/FlagsCollection.js similarity index 55% rename from webgoat-container/src/main/resources/static/js/goatApp/model/CookieCollection.js rename to webgoat-container/src/main/resources/static/js/goatApp/model/FlagsCollection.js index ae3ed023a..8832310a3 100644 --- a/webgoat-container/src/main/resources/static/js/goatApp/model/CookieCollection.js +++ b/webgoat-container/src/main/resources/static/js/goatApp/model/FlagsCollection.js @@ -1,13 +1,13 @@ define(['jquery', 'underscore', 'backbone', - 'goatApp/model/CookieModel'], + 'goatApp/model/FlagModel'], function($, _, Backbone, - CookieModel) { + FlagModel) { return Backbone.Collection.extend({ - url:'service/cookie.mvc', - model:CookieModel + url:'/WebGoat/scoreboard-data', + model:FlagModel }); }); \ No newline at end of file diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/LessonContentModel.js b/webgoat-container/src/main/resources/static/js/goatApp/model/LessonContentModel.js index f036f22e6..f26aad564 100644 --- a/webgoat-container/src/main/resources/static/js/goatApp/model/LessonContentModel.js +++ b/webgoat-container/src/main/resources/static/js/goatApp/model/LessonContentModel.js @@ -20,7 +20,6 @@ define(['jquery', loadData: function(options) { this.urlRoot = _.escape(encodeURIComponent(options.name)) + '.lesson' - var self = this; this.fetch().done(function(data) { self.setContent(data); @@ -32,7 +31,8 @@ define(['jquery', loadHelps = true; } this.set('content',content); - this.set('lessonUrl',document.URL); + this.set('lessonUrl',document.URL.replace(/\.lesson.*/,'.lesson')); + this.set('pageNum',document.URL.replace(/.*\.lesson\/(\d{1,4})$/,'$1')); this.trigger('content:loaded',this,loadHelps); }, diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewModel.js b/webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewModel.js index 28647bb89..17c906bd5 100644 --- a/webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewModel.js +++ b/webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewModel.js @@ -3,7 +3,7 @@ define([ function( Backbone) { return Backbone.Collection.extend({ - tagName: 'ul', + //tagName: 'ul', url: 'service/lessonoverview.mvc' }); }); diff --git a/webgoat-container/src/main/resources/static/js/goatApp/scoreboardApp.js b/webgoat-container/src/main/resources/static/js/goatApp/scoreboardApp.js new file mode 100644 index 000000000..d47bd0b60 --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/scoreboardApp.js @@ -0,0 +1,17 @@ +define(['jquery', + 'underscore', + 'backbone', + 'goatApp/support/goatAsyncErrorHandler', + 'goatApp/view/ScoreboardView'], + function ($, + _, + Backbone, + asyncErrorHandler, + ScoreboardView) { + 'use strict' + return { + initApp: function () { + scoreboard = new ScoreboardView(); + } + }; + }); \ No newline at end of file diff --git a/webgoat-container/src/main/resources/static/js/goatApp/templates/paging_controls.html b/webgoat-container/src/main/resources/static/js/goatApp/templates/paging_controls.html new file mode 100644 index 000000000..141c3f5af --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/templates/paging_controls.html @@ -0,0 +1,16 @@ +
+ + + +
\ No newline at end of file diff --git a/webgoat-container/src/main/resources/static/js/goatApp/templates/scoreboard.html b/webgoat-container/src/main/resources/static/js/goatApp/templates/scoreboard.html new file mode 100644 index 000000000..cae628c71 --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/templates/scoreboard.html @@ -0,0 +1,16 @@ +
WebGoat Challenge - AppSec EU 2017
+
banner here
+ + <% _.each(rankings, function(userRanking, index) { %> + + + + + <% }); %> +
<%= index%> <%=userRanking.username %>
<% _.each(userRanking.flagsCaptured, function(flag) { %> + +
+ + <%=flag%>
+ <% }); %> +
\ No newline at end of file diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/CookieView.js b/webgoat-container/src/main/resources/static/js/goatApp/view/CookieView.js deleted file mode 100644 index 106b6f952..000000000 --- a/webgoat-container/src/main/resources/static/js/goatApp/view/CookieView.js +++ /dev/null @@ -1,34 +0,0 @@ -define(['jquery', - 'underscore', - 'backbone', - 'goatApp/model/CookieCollection'], -function($, - _, - Backbone, - CookieCollection) { - return Backbone.View.extend({ - el:'#cookies-view', - - initialize: function() { - this.collection = new CookieCollection(); - this.listenTo(this.collection,'reset',this.render) - this.collection.fetch({reset:true}); - }, - - render: function() { - this.$el.html('') - var cookieTable; - this.collection.each(function(model) { - cookieTable = $('',{'class':'cookie-table table-striped table-nonfluid'}); - _.each(model.keys(), function(attribute) { - var newRow = $(''); - newRow.append($('
',{text:_.escape(attribute)})) - newRow.append($('',{text:_.escape(model.get(attribute))})); - cookieTable.append(newRow); - }); - }); - this.$el.append($('

',{text:'Cookie/s'})); - this.$el.append(cookieTable); - } - }); -}); \ No newline at end of file diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/HelpControlsView.js b/webgoat-container/src/main/resources/static/js/goatApp/view/HelpControlsView.js index 26f20f345..c317ba0f3 100644 --- a/webgoat-container/src/main/resources/static/js/goatApp/view/HelpControlsView.js +++ b/webgoat-container/src/main/resources/static/js/goatApp/view/HelpControlsView.js @@ -12,18 +12,14 @@ function($,_,Backbone) { this.hasPlan = options.hasPlan; this.hasSolution = options.hasSolution; this.hasSource = options.hasSource; - var self = this; - Backbone.on('navigatedToPage', function(nav) { - self.showHideHintsButton(nav) - }); }, - showHideHintsButton: function(nav) { - if (typeof nav['assignmentPath'] !== 'undefined') { - this.$el.find('#show-hints-button').unbind().on('click',this.showHints.bind(this)).show(); - } else { - $('#show-hints-button').hide(); - } + showHintsButton: function(nav) { + this.$el.find('#show-hints-button').unbind().on('click',this.showHints.bind(this)).show(); + }, + + hideHintsButton: function(){ + $('#show-hints-button').hide(); }, render:function(title) { @@ -38,7 +34,7 @@ function($,_,Backbone) { this.$el.find('#show-solution-button').unbind().on('click',_.bind(this.showSolution,this)).show(); } - this.$el.find('#show-lesson-overview-button').unbind().on('click', _.bind(this.showLessonOverview, this)).show(); + //this.$el.find('#show-lesson-overview-button').unbind().on('click', _.bind(this.showLessonOverview, this)).show(); this.$el.find('#restart-lesson-button').unbind().on('click',_.bind(this.restartLesson,this)).show(); }, diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/HintView.js b/webgoat-container/src/main/resources/static/js/goatApp/view/HintView.js index 042b17c54..85508c432 100644 --- a/webgoat-container/src/main/resources/static/js/goatApp/view/HintView.js +++ b/webgoat-container/src/main/resources/static/js/goatApp/view/HintView.js @@ -19,8 +19,10 @@ function($, this.listenTo(this.collection,'loaded',this.onModelLoaded); this.hideHints(); var self = this; + // different way to do this? Backbone.on('navigatedToPage', function(nav){ - self.selectHints(nav) + self.selectHints(nav); + // end event delegation?? }); }, @@ -61,14 +63,25 @@ function($, * * @param nav the json structure for navigating */ - selectHints: function(nav) { - this.curHint = 0; - var assignmentPath = nav['assignmentPath']; - if (assignmentPath != null) { - this.hintsToShow = this.collection.getHintsForAssignment(assignmentPath); + filterHints: function(endpoints) { + this.hintsToShow = []; + _.each(endpoints, this.filterHint, this); + + if (this.hintsToShow.length > 0) { + this.trigger('hints:showButton'); } else { - this.hintsToShow = new Array(); - } + this.trigger('hints:hideButton'); + } + }, + + filterHint: function(endpoint) { + var self = this; + _.each(this.collection.models, function(hintModel) { + if (endpoint.indexOf(hintModel.get('assignmentPath')) > -1) { + self.hintsToShow.push(hintModel.get('hint')); + } + }); + console.log(this.hintsToShow); }, onModelLoaded: function() { @@ -97,7 +110,7 @@ function($, if(this.hintsToShow.length == 0) { // this.hideHints(); } else { - this.$el.find('#lesson-hint-content').html(polyglot.t(this.hintsToShow[curHint].get('hint'))); + this.$el.find('#lesson-hint-content').html(polyglot.t(this.hintsToShow[curHint])); } }, diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js b/webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js index 7f478dab2..beb0f6fa6 100644 --- a/webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js +++ b/webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js @@ -3,13 +3,15 @@ define(['jquery', 'underscore', 'backbone', 'libs/jquery.form', - 'goatApp/view/ErrorNotificationView'], + 'goatApp/view/ErrorNotificationView', + 'goatApp/view/PaginationControlView'], function( $, _, Backbone, JQueryForm, - ErrorNotificationView) { + ErrorNotificationView, + PaginationControlView) { return Backbone.View.extend({ el:'#lesson-content-wrapper', //TODO << get this fixed up in DOM @@ -37,7 +39,7 @@ define(['jquery', return -1; }, - /* initial renering */ + /* initial rendering */ render: function() { this.$el.find('.lesson-content').html(this.model.get('content')); this.$el.find('.attack-feedback').hide(); @@ -45,31 +47,17 @@ define(['jquery', this.makeFormsAjax(); //this.ajaxifyAttackHref(); $(window).scrollTop(0); //work-around til we get the scroll down sorted out - this.initPagination(); + var startPageNum = this.model.get('pageNum'); + this.initPagination(startPageNum); }, - initPagination: function() { + initPagination: function(startPageNum) { //get basic pagination info - this.currentPage = 0; this.$contentPages = this.$el.find('.lesson-page-wrapper'); - this.numPages = this.$contentPages.length; - - // - this.addPaginationControls(); - if (this.numPages > 1) { - //no animation on init - this.$contentPages.hide(); - this.$el.find(this.$contentPages[this.currentPage]).show(); - this.showNextPageButton(); - this.hidePrevPageButton(); - } else if (this.numPages === 1) { - this.hideNextPageButton(); - this.hidePrevPageButton(); - } - }, - - setCurrentPage: function (pageNum) { - this.currentPage = (_.isNumber(pageNum) && pageNum < this.numPages) ? pageNum : 0; + var currentPage = (!isNaN(startPageNum) && startPageNum && startPageNum < this.$contentPages) ? startPageNum : 0; + //init views & pagination + this.showCurContentPage(currentPage); + this.paginationControlView = new PaginationControlView(this.$contentPages,this.model.get('lessonUrl')); }, getCurrentPage: function () { @@ -160,128 +148,29 @@ define(['jquery', this.$curOutput.show(400) }, - /* create, show & hide pagination controls */ - - addPaginationControls: function() { - var pagingControlsDiv; - //this.$el.html(); - //prev - var prevPageButton = $('',{class:'glyphicon-class glyphicon glyphicon-circle-arrow-left show-prev-page'}); - prevPageButton.unbind().on('click',this.decrementPageView.bind(this)); - //next - var nextPageButton = $('',{class:'glyphicon-class glyphicon glyphicon-circle-arrow-right show-next-page'}); - nextPageButton.unbind().on('click',this.incrementPageView.bind(this)); - //add to DOM - if (this.$el.find('#lesson-page-controls').length < 1) { - pagingControlsDiv = $('
',{class:'panel-body', id:'lesson-page-controls'}); - pagingControlsDiv.append(prevPageButton); - pagingControlsDiv.append(nextPageButton); - this.$el.find('.lesson-page-controls').append(pagingControlsDiv); - } - - }, - - showPrevPageButton: function() { - $('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-left.show-prev-page').show(); - }, - - hidePrevPageButton: function() { - $('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-left.show-prev-page').hide(); - }, - - showNextPageButton: function() { - $('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-right.show-next-page').show(); - }, - - hideNextPageButton: function() { - $('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-right.show-next-page').hide(); - }, - - /* increment, decrement & display handlers */ - incrementPageView: function() { - if (this.currentPage < this.numPages -1) { - this.currentPage++; - window.location.href = this.model.get('lessonUrl') + '/' + this.currentPage; - //this.showCurContentPage(true);Con - } - - if (this.currentPage > 0) { - this.showPrevPageButton(); - } - - if (this.currentPage >= this.numPages -1) { - this.hideNextPageButton(); - this.showPrevPageButton; - } - }, - - decrementPageView: function() { - if (this.currentPage > 0) { - this.currentPage--; - window.location.href = this.model.get('lessonUrl') + '/' + this.currentPage; - //this.showCurContentPage(false); - } - - if (this.currentPage < this.numPages -1) { - this.showNextPageButton(); - } - - if (this.currentPage == 0) { - this.hidePrevPageButton(); - this.showNextPageButton() - } - - }, - - showCurContentPage: function(isIncrement) { + showCurContentPage: function(pageNum) { this.$contentPages.hide(); - this.$el.find(this.$contentPages[this.currentPage]).show(); + this.$el.find(this.$contentPages[pageNum]).show(); }, - findAssigmentEndpointOnPage: function(pageNumber) { - var contentPage = this.$contentPages[this.currentPage]; - var form = $('form.attack-form', contentPage); - var action = form.attr('action') - if (action !== undefined) { - return action; + findAssigmentEndpointsOnPage: function(pageNumber) { + var contentPage = this.$contentPages[pageNumber]; + var endpoints = []; //going to assume uniqueness since these are assignments + var pageForms = $(contentPage).find('form.attack-form'); + for (var i=0; i 1) { - this.showNextPageButton(); - } - return; - } - // > first page, but not last - if (this.currentPage > 0 && this.currentPage < this.numPages -1) { - this.showNextPageButton(); - this.showPrevPageButton(); - return; - } - // last page and more than one page - if (this.currentPage === this.numPages -1 && this.numPages > 1) { - this.hideNextPageButton(); - this.showPrevPageButton(); - return; - } - + this.paginationControlView.setCurrentPage(pageNum);//provides validation + this.showCurContentPage(this.paginationControlView.currentPage); + this.paginationControlView.render(); + this.paginationControlView.hideShowNavButtons(); + var assignmentPaths = this.findAssigmentEndpointsOnPage(pageNum); + this.trigger('endpoints:filtered',assignmentPaths); }, /* for testing */ diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/LessonOverviewView.js b/webgoat-container/src/main/resources/static/js/goatApp/view/LessonOverviewView.js index f17b3e9aa..c073fbe9f 100644 --- a/webgoat-container/src/main/resources/static/js/goatApp/view/LessonOverviewView.js +++ b/webgoat-container/src/main/resources/static/js/goatApp/view/LessonOverviewView.js @@ -39,7 +39,7 @@ function($, } else { this.$el.show(); } - this.showAssignments(); + //this.showAssignments(); return this; }, diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/PaginationControlView.js b/webgoat-container/src/main/resources/static/js/goatApp/view/PaginationControlView.js new file mode 100644 index 000000000..d156a6756 --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/view/PaginationControlView.js @@ -0,0 +1,186 @@ +define(['jquery', + 'underscore', + 'backbone', + 'goatApp/model/LessonOverviewModel', + 'text!templates/paging_controls.html'], + function ($, + _, + Backbone, + LessonOverviewModel, + PaginationTemplate) { + return Backbone.View.extend({ + template: PaginationTemplate, + el: '#lesson-page-controls', + + initialize: function ($contentPages,baseLessonUrl) { + this.$contentPages = $contentPages; + this.model = new LessonOverviewModel(); + this.listenTo(this.model, 'change add remove update reset', this.render); + this.numPages = this.$contentPages.length; + this.baseUrl = baseLessonUrl; + + this.model.fetch(); + this.initPagination(); + this.render(); + }, + + render: function () { + this.parseLinks(); + var t = _.template(this.template); + this.$el.html(t({'overview':this.lessonOverview})); + this.bindNavButtons(); + this.hideShowNavButtons(); + }, + + bindNavButtons: function() { + this.$el.find('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-right.show-next-page').unbind().on('click',this.incrementPageView.bind(this)); + this.$el.find('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-left.show-prev-page').unbind().on('click', this.decrementPageView.bind(this)); + this.navButtonsBound = true; + }, + + parseLinks: function() { + var assignmentCount = this.$contentPages.find('.attack-container'); + var solvedMap = {}; + var pages = []; + // one pass on solved assignmets + _.each(this.model.toJSON(), function(assignment) { + if (assignment.solved) { + var key = assignment.assignment.path; //.replace(/\//g,''); + solvedMap[key] = assignment.assignment.name; + } + }); + + isAttackSolved = function (path) { + //strip + var newPath = path.replace(/^\/WebGoat/,''); + if (typeof solvedMap[newPath] !== 'undefined') { + return true; + } + return false; + }; + + var self = this; + var pages, pageClass, solved; + _.each(this.$contentPages,function(page,index) { + var curPageClass = (self.currentPage == index) ? ' cur-page' : ''; + + if ($(page).find('.attack-container').length < 1) { // no assignments [attacks] + pageClass = 'page-link'; + pages.push({content:'content',pageClass:pageClass,curPageClass:curPageClass}); + } else { + var $assignmentForms = $(page).find('.attack-container form'); + // use for loop to avoid anonymous function scope hell + //var pageAssignments = {content:'attack',attacks:[]} + pageClass = 'attack-link' + var solvedClass = 'solved-true' + for (var i=0; i< $assignmentForms.length; i++) { + //normalize path + var action = $assignmentForms.attr('action');//.replace(/\//g,''); + if (action && isAttackSolved(action)) { + //pageClass = 'fa fa-check-square-o assignment-solved'; + //pageAssignments.attacks.push({solved:true}); + } else { + solvedClass = 'solved-false'; + + } + } + pages.push({solvedClass:solvedClass,content:'assignment',curPageClass:curPageClass,pageClass:pageClass}); + } + }); + //assign to the view + this.lessonOverview = { + baseUrl: this.baseUrl, + pages: pages + } + }, + + showPrevPageButton: function() { + $('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-left.show-prev-page').show(); + }, + + hidePrevPageButton: function() { + $('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-left.show-prev-page').hide(); + }, + + showNextPageButton: function() { + $('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-right.show-next-page').show(); + }, + + hideNextPageButton: function() { + $('span.glyphicon-class.glyphicon.glyphicon-circle-arrow-right.show-next-page').hide(); + }, + + initPagination: function() { + //track pagination state in this view ... start at 0 + this.currentPage = 0; + }, + + setCurrentPage: function (pageNum) { + this.currentPage = (_.isNumber(pageNum) && pageNum < this.numPages) ? pageNum : 0; + }, + + /* increment, decrement & display handlers */ + incrementPageView: function() { + if (this.currentPage < this.numPages -1) { + this.currentPage++; + window.location.href = this.baseUrl + '/' + this.currentPage; + } + + if (this.currentPage > 0) { + this.showPrevPageButton(); + } + + if (this.currentPage >= this.numPages -1) { + this.hideNextPageButton(); + this.showPrevPageButton; + } + this.render(); + }, + + decrementPageView: function() { + if (this.currentPage > 0) { + this.currentPage--; + window.location.href = this.baseUrl + '/' + this.currentPage; + } + + if (this.currentPage < this.numPages -1) { + this.showNextPageButton(); + } + + if (this.currentPage == 0) { + this.hidePrevPageButton(); + this.showNextPageButton() + } + this.render(); + }, + + hideShowNavButtons: function () { + //one page only + if (this.numPages === 1) { + this.hidePrevPageButton(); + this.hideNextPageButton(); + } + //first page + if (this.currentPage === 0) { + this.hidePrevPageButton(); + if (this.numPages > 1) { + this.showNextPageButton(); + } + return; + } + // > first page, but not last + if (this.currentPage > 0 && this.currentPage < this.numPages -1) { + this.showNextPageButton(); + this.showPrevPageButton(); + return; + } + // last page and more than one page + if (this.currentPage === this.numPages -1 && this.numPages > 1) { + this.hideNextPageButton(); + this.showPrevPageButton(); + return; + } + + }, + }); + }); \ No newline at end of file diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/ScoreboardView.js b/webgoat-container/src/main/resources/static/js/goatApp/view/ScoreboardView.js new file mode 100644 index 000000000..fb72d16aa --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/view/ScoreboardView.js @@ -0,0 +1,32 @@ +define(['jquery', + 'underscore', + 'backbone', + 'goatApp/model/FlagsCollection', + 'text!templates/scoreboard.html'], +function($, + _, + Backbone, + FlagsCollection, + ScoreboardTemplate) { + return Backbone.View.extend({ + el:'#scoreboard', + + initialize: function() { + this.template = ScoreboardTemplate, + this.collection = new FlagsCollection(); + this.listenTo(this.collection,'reset',this.render) + this.collection.fetch({reset:true}); + }, + + render: function() { + //this.$el.html('test'); + var t = _.template(this.template); + this.$el.html(t({'rankings':this.collection.toJSON()})); + setTimeout(this.pollData.bind(this), 5000); + }, + + pollData: function() { + this.collection.fetch({reset:true}); + } + }); +}); \ No newline at end of file diff --git a/webgoat-container/src/main/resources/static/js/scoreboard.js b/webgoat-container/src/main/resources/static/js/scoreboard.js new file mode 100644 index 000000000..f561853f2 --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/scoreboard.js @@ -0,0 +1,47 @@ +//main.js +/* +/js +js/main.js << main file for require.js +--/libs/(jquery,backbone,etc.) << base libs +--/goatApp/ << base dir for goat application, js-wise +--/goatApp/model +--/goatApp/view +--/goatApp/support +--/goatApp/controller +*/ + +require.config({ + baseUrl: "js/", + paths: { + jquery: 'libs/jquery-2.2.4.min', + jqueryui: 'libs/jquery-ui-1.10.4', + underscore: 'libs/underscore-min', + backbone: 'libs/backbone-min', + text: 'libs/text', + templates: 'goatApp/templates', + polyglot: 'libs/polyglot.min' + }, + + map: { + 'libs/jquery-base' : {'jquery':'libs/jquery-2.2.4.min'}, + 'libs/jquery-vuln' : {'jquery':'libs/jquery-2.1.4.min'} + }, + + shim: { + "jqueryui": { + exports:"$", + deps: ['jquery'] + }, + underscore: { + exports: "_" + }, + backbone: { + deps: ['underscore', 'jquery'], + exports: 'Backbone' + } + } +}); + +require(['jquery','libs/jquery-base','libs/jquery-vuln','jqueryui', 'underscore','backbone','goatApp/scoreboardApp'], function($,jqueryBase,jqueryVuln,jqueryui,_,Backbone,ScoreboardApp){ + ScoreboardApp.initApp(); +}); \ No newline at end of file diff --git a/webgoat-container/src/main/resources/templates/main_new.html b/webgoat-container/src/main/resources/templates/main_new.html index 925781ffa..e96256199 100644 --- a/webgoat-container/src/main/resources/templates/main_new.html +++ b/webgoat-container/src/main/resources/templates/main_new.html @@ -2,7 +2,7 @@ - + @@ -143,10 +143,7 @@ - + @@ -172,7 +169,7 @@
-
+
diff --git a/webgoat-container/src/main/resources/templates/scoreboard.html b/webgoat-container/src/main/resources/templates/scoreboard.html new file mode 100644 index 000000000..1e9d0a674 --- /dev/null +++ b/webgoat-container/src/main/resources/templates/scoreboard.html @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+ +
+ +
+
+

Samsung Galaxy S8

+
Samsung · + (124421 reviews) +
+ +
+ PRICE +
+

US $899

+ +
+
+ COLOR +
+
+
+
+
+
+
+
+ CAPACITY +
+
+
64 GB
+
128 GB
+
+
+
+
+ QUANTITY +
+
+
+ +
+
+
+ +
+
+ CHECKOUT CODE +
+ + + +
+ +
+ +
+ Like
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge3.html b/webgoat-lessons/challenge/src/main/resources/html/Challenge3.html new file mode 100644 index 000000000..62255ab95 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/html/Challenge3.html @@ -0,0 +1,72 @@ + + + + + +
+
+ + +
+
+ +
+
+
+
+ user profile image +
+
+
+ John Doe + uploaded a photo. +
+
24 days ago
+
+
+ +
+ image post +
+ +
+ +
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+ + +
+
+
+
+
+ \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge4.html b/webgoat-lessons/challenge/src/main/resources/html/Challenge4.html new file mode 100644 index 000000000..f760beffe --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/html/Challenge4.html @@ -0,0 +1,75 @@ + + + + + +
+
+ + + +
+
+
+ +
+ +
+
+ +
+

Welcome back,

+
+
+ +
+

Vote for your favorite

+
+
+ +
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge5.html b/webgoat-lessons/challenge/src/main/resources/html/Challenge5.html new file mode 100644 index 000000000..149313275 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/html/Challenge5.html @@ -0,0 +1,91 @@ + + + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge6.html b/webgoat-lessons/challenge/src/main/resources/html/Challenge6.html new file mode 100644 index 000000000..f34af864e --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/html/Challenge6.html @@ -0,0 +1,125 @@ + + + + + +
+
+ + +
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties index cbae74dcb..9328177ef 100644 --- a/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties @@ -1 +1,18 @@ -challenge.title=WebGoat Challenge +challenge0.title=WebGoat Challenge +challenge1.title=Admin lost password +challenge2.title=Get it for free +challenge3.title=Photo comments +challenge4.title=Voting +challenge5.title=Without password +challenge6.title=Creating a new account +challenge.solved=Congratulations, you solved the challenge. Here is your flag: {0} +challenge.close=This is not the correct password for tom, please try again. + +user.exists=User {0} already exists please try to register with a different username. +user.created=User {0} created, please proceed to the login page. +input.invalid=Input for user, email and/or password is empty or too long, please fill in all field and/or limit all fields to 30 characters. + +challenge.flag.correct=Congratulations you have solved the challenge!! +challenge.flag.incorrect=Sorry this is not the correct flag, please try again. + +ip.address.unknown=IP address unknown, e-mail has been sent. \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/images/avatar1.png b/webgoat-lessons/challenge/src/main/resources/images/avatar1.png new file mode 100644 index 000000000..4ea864f90 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/avatar1.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/boss.jpg b/webgoat-lessons/challenge/src/main/resources/images/boss.jpg new file mode 100644 index 000000000..3fcd1c36e Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/boss.jpg differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/cat.jpg b/webgoat-lessons/challenge/src/main/resources/images/cat.jpg new file mode 100644 index 000000000..e0e1fb983 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/cat.jpg differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge1-small.png b/webgoat-lessons/challenge/src/main/resources/images/challenge1-small.png new file mode 100644 index 000000000..a4fbc3470 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge1-small.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge1.png b/webgoat-lessons/challenge/src/main/resources/images/challenge1.png new file mode 100644 index 000000000..0008ceb5e Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge1.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge2-small.png b/webgoat-lessons/challenge/src/main/resources/images/challenge2-small.png new file mode 100644 index 000000000..777b5a093 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge2-small.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge2.png b/webgoat-lessons/challenge/src/main/resources/images/challenge2.png new file mode 100644 index 000000000..d1eadfefe Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge2.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge3-small.png b/webgoat-lessons/challenge/src/main/resources/images/challenge3-small.png new file mode 100644 index 000000000..daf7f7ebb Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge3-small.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge3.png b/webgoat-lessons/challenge/src/main/resources/images/challenge3.png new file mode 100644 index 000000000..b271d4ea1 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge3.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge4-small.png b/webgoat-lessons/challenge/src/main/resources/images/challenge4-small.png new file mode 100644 index 000000000..b9ddaa7e7 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge4-small.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge4.png b/webgoat-lessons/challenge/src/main/resources/images/challenge4.png new file mode 100644 index 000000000..cb5301ac9 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge4.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge5-small.png b/webgoat-lessons/challenge/src/main/resources/images/challenge5-small.png new file mode 100644 index 000000000..1aa84ab4c Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge5-small.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge5.png b/webgoat-lessons/challenge/src/main/resources/images/challenge5.png new file mode 100644 index 000000000..e5d9a8108 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/challenge5.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/samsung-black.jpg b/webgoat-lessons/challenge/src/main/resources/images/samsung-black.jpg new file mode 100644 index 000000000..7b0c1f809 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/samsung-black.jpg differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/samsung-grey.jpg b/webgoat-lessons/challenge/src/main/resources/images/samsung-grey.jpg new file mode 100644 index 000000000..2dd9ea557 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/samsung-grey.jpg differ diff --git a/webgoat-lessons/challenge/src/main/resources/images/webgoat2.png b/webgoat-lessons/challenge/src/main/resources/images/webgoat2.png new file mode 100644 index 000000000..19b3f90f4 Binary files /dev/null and b/webgoat-lessons/challenge/src/main/resources/images/webgoat2.png differ diff --git a/webgoat-lessons/challenge/src/main/resources/js/bootstrap.min.js b/webgoat-lessons/challenge/src/main/resources/js/bootstrap.min.js new file mode 100644 index 000000000..b04a0e82f --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/js/bootstrap.min.js @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.1.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/js/challenge2.js b/webgoat-lessons/challenge/src/main/resources/js/challenge2.js new file mode 100644 index 000000000..78df37939 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/js/challenge2.js @@ -0,0 +1,64 @@ +$(document).ready(function () { + //-- Click on detail + $("ul.menu-items > li").on("click", function () { + $("ul.menu-items > li").removeClass("active"); + $(this).addClass("active"); + }) + + $(".attr,.attr2").on("click", function () { + var clase = $(this).attr("class"); + + $("." + clase).removeClass("active"); + $(this).addClass("active"); + }) + + //-- Click on QUANTITY + $(".btn-minus").on("click", function () { + var now = $(".quantity").val(); + if ($.isNumeric(now)) { + if (parseInt(now) - 1 > 0) { + now--; + } + $(".quantity").val(now); + $('#price').text(now * 899); + } else { + $(".quantity").val("1"); + $('#price').text(899); + } + calculate(); + }) + $(".btn-plus").on("click", function () { + var now = $(".quantity").val(); + if ($.isNumeric(now)) { + $(".quantity").val(parseInt(now) + 1); + } else { + $(".quantity").val("1"); + } + calculate(); + }) + $(".checkoutCode").on("blur", function () { + var checkoutCode = $(".checkoutCode").val(); + $.get("challenge-store/coupons/" + checkoutCode, function (result, status) { + var discount = result.discount; + if (discount > 0) { + $('#discount').text(discount); + calculate(); + } else { + $('#discount').text(0); + calculate(); + } + }); + }) + + function calculate() { + var d = $('#discount').text(); + var price = $('#price').val(); + var quantity = parseInt($(".quantity").val()); + if (d > 0) { + $('#price').text((quantity * (899 - (899 * d / 100))).toFixed(2)); + + } else { + $('#price').text(quantity * 899); + } + } +}) \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/js/challenge3.js b/webgoat-lessons/challenge/src/main/resources/js/challenge3.js new file mode 100644 index 000000000..fb902e050 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/js/challenge3.js @@ -0,0 +1,45 @@ +$(document).ready(function () { + $("#postComment").on("click", function () { + var commentInput = $("#commentInput").val(); + $.ajax({ + type: 'POST', + url: 'challenge/3', + data: JSON.stringify({text: commentInput}), + contentType: "application/json", + dataType: 'json' + }).then( + function () { + getChallenges(); + $("#commentInput").val(''); + } + ) + }) + + var html = '
  • ' + + '
    ' + + 'avatar' + + '
    ' + + '
    ' + + '
    ' + + '

    USER

    ' + + '
    DATETIME
    ' + + '
    ' + + '

    COMMENT

    ' + + '
    ' + + '
  • '; + + getChallenges(); + + function getChallenges() { + $("#list").empty(); + $.get("challenge/3", function (result, status) { + for (var i = 0; i < result.length; i++) { + var comment = html.replace('USER', result[i].user); + comment = comment.replace('DATETIME', result[i].dateTime); + comment = comment.replace('COMMENT', result[i].text); + $("#list").append(comment); + } + + }); + } +}) \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/js/challenge4.js b/webgoat-lessons/challenge/src/main/resources/js/challenge4.js new file mode 100644 index 000000000..5c9d6a38d --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/js/challenge4.js @@ -0,0 +1,84 @@ +$(document).ready(function () { + login('Guest'); +}) + +function login(user) { + $("#name").text(user); + $.ajax({ + url: "votings/login?user=" + user, + complete: function (result, status) { + getVotings(); + } + }); +} + +var html = '' + + '
    ' + + '
    ' + + 'placehold.it/350x250' + + '
    ' + + '
    ' + + '
    ' + + '

    TITLE

    ' + + '

    INFORMATION

    ' + + '
    ' + + '
    ' + + '

    NO_VOTES' + + ' votes' + + '

    ' + + '' + + '
    ' + + '' + + '' + + '' + + '' + + '
    ' + + '

    Average AVERAGE /4

    ' + + '
    ' + + '
    ' + + '
    '; + +function getVotings() { + $("#votesList").empty(); + $.get("votings/", function (result, status) { + for (var i = 0; i < result.length; i++) { + var voteTemplate = html.replace('IMAGE_SMALL', result[i].imageSmall); + if (i === 0) { + voteTemplate = voteTemplate.replace('ACTIVE', 'active'); + voteTemplate = voteTemplate.replace('BUTTON', 'btn-default'); + } else { + voteTemplate = voteTemplate.replace('ACTIVE', ''); + voteTemplate = voteTemplate.replace('BUTTON', 'btn-primary'); + } + voteTemplate = voteTemplate.replace(/TITLE/g, result[i].title); + voteTemplate = voteTemplate.replace('INFORMATION', result[i].information || ''); + voteTemplate = voteTemplate.replace('NO_VOTES', result[i].numberOfVotes || ''); + voteTemplate = voteTemplate.replace('AVERAGE', result[i].average || ''); + + var hidden = (result[i].numberOfVotes === undefined ? 'hidden' : ''); + voteTemplate = voteTemplate.replace(/HIDDEN_VIEW_VOTES/g, hidden); + hidden = (result[i].average === undefined ? 'hidden' : ''); + voteTemplate = voteTemplate.replace(/HIDDEN_VIEW_RATING/g, hidden); + + $("#votesList").append(voteTemplate); + } + }) +} + +function vote(title) { + var user = $("#name").text(); + if (user === 'Guest') { + alert("As a guest you are not allowed to vote, please login first.") + } else { + $.ajax({ + type: 'POST', + url: 'votings/' + title + }).then( + function () { + getVotings(); + } + ) + } +} + + diff --git a/webgoat-lessons/challenge/src/main/resources/js/challenge6.js b/webgoat-lessons/challenge/src/main/resources/js/challenge6.js new file mode 100644 index 000000000..9107e1176 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/js/challenge6.js @@ -0,0 +1,18 @@ +$(function() { + + $('#login-form-link').click(function(e) { + $("#login-form").delay(100).fadeIn(100); + $("#register-form").fadeOut(100); + $('#register-form-link').removeClass('active'); + $(this).addClass('active'); + e.preventDefault(); + }); + $('#register-form-link').click(function(e) { + $("#register-form").delay(100).fadeIn(100); + $("#login-form").fadeOut(100); + $('#login-form-link').removeClass('active'); + $(this).addClass('active'); + e.preventDefault(); + }); + +}); \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_1.adoc b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_1.adoc new file mode 100644 index 000000000..36e25fe5a --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_1.adoc @@ -0,0 +1 @@ +The admin forgot where the password is stashed, can you help? \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_2.adoc b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_2.adoc new file mode 100644 index 000000000..5a1e072f2 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_2.adoc @@ -0,0 +1 @@ +No need to pay if you know the code ... \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_3.adoc b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_3.adoc new file mode 100644 index 000000000..396cbfa0f --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_3.adoc @@ -0,0 +1 @@ +Changing language can help you find the 'secret' file \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_4.adoc b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_4.adoc new file mode 100644 index 000000000..883d4be45 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_4.adoc @@ -0,0 +1 @@ +Try to change to a different user, maybe you can find the flag? \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_5.adoc b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_5.adoc new file mode 100644 index 000000000..0102ca98f --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_5.adoc @@ -0,0 +1 @@ +Can you login as Larry? \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_6.adoc b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_6.adoc new file mode 100644 index 000000000..1e1e61ac2 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_6.adoc @@ -0,0 +1 @@ +Can you login as Tom? It may be a little harder than it was for Larry. \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_content1.adoc b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_content1.adoc deleted file mode 100644 index 987f45684..000000000 --- a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_content1.adoc +++ /dev/null @@ -1 +0,0 @@ -This is the challenge \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_introduction.adoc b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_introduction.adoc new file mode 100644 index 000000000..03774d6f7 --- /dev/null +++ b/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_introduction.adoc @@ -0,0 +1,23 @@ +=== Welcome to the WebGoat challenge (CTF) + +:hardbreaks: +In this CTF you will need to solve a couple of challenges, each challenge will give you a flag which you will +need to post in order to gain points. + +Flags have the following format: `a7179f89-906b-4fec-9d99-f15b796e7208` + +==== Rules + +- Do not try to hack the competition infrastructure. If you happen to find a bug or vulnerability please send us +an e-mail. + +- Play fair, do not try sabotage other competing teams, or in any way hindering the progress of another team. + +- Brute forcing of challenges / flags is not allowed. + +:hardbreaks: +*Have fun!!* +Team WebGoat + + +image::images/boss.jpg[] \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/plugin/Challenge/html/Challenge.html b/webgoat-lessons/challenge/src/main/resources/plugin/Challenge/html/Challenge.html deleted file mode 100644 index 00c0e2c2f..000000000 --- a/webgoat-lessons/challenge/src/main/resources/plugin/Challenge/html/Challenge.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - -
    - - -
    -
    - - \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/plugin/Challenge/lessonPlans/en/Challenge_content1.adoc b/webgoat-lessons/challenge/src/main/resources/plugin/Challenge/lessonPlans/en/Challenge_content1.adoc deleted file mode 100644 index 987f45684..000000000 --- a/webgoat-lessons/challenge/src/main/resources/plugin/Challenge/lessonPlans/en/Challenge_content1.adoc +++ /dev/null @@ -1 +0,0 @@ -This is the challenge \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/main/resources/plugin/i18n/WebGoatLabels.properties b/webgoat-lessons/challenge/src/main/resources/plugin/i18n/WebGoatLabels.properties deleted file mode 100644 index cbae74dcb..000000000 --- a/webgoat-lessons/challenge/src/main/resources/plugin/i18n/WebGoatLabels.properties +++ /dev/null @@ -1 +0,0 @@ -challenge.title=WebGoat Challenge diff --git a/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge1/Assignment1Test.java b/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge1/Assignment1Test.java new file mode 100644 index 000000000..b496bc4e5 --- /dev/null +++ b/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge1/Assignment1Test.java @@ -0,0 +1,76 @@ +package org.owasp.webgoat.plugin.challenge1; + +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import org.owasp.webgoat.assignments.AssignmentEndpointTest; +import org.owasp.webgoat.plugin.Flag; +import org.owasp.webgoat.plugin.SolutionConstants; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.net.InetAddress; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +/** + * @author nbaars + * @since 5/2/17. + */ +@RunWith(MockitoJUnitRunner.class) +public class Assignment1Test extends AssignmentEndpointTest { + + private MockMvc mockMvc; + + @Before + public void setup() { + Assignment1 assignment1 = new Assignment1(); + init(assignment1); + new Flag().initFlags(); + this.mockMvc = standaloneSetup(assignment1).build(); + } + + @Test + public void success() throws Exception { + InetAddress addr = InetAddress.getLocalHost(); + String host = addr.getHostAddress(); + mockMvc.perform(MockMvcRequestBuilders.post("/challenge/1") + .header("X-Forwarded-For", host) + .param("username", "admin") + .param("password", SolutionConstants.PASSWORD)) + .andExpect(jsonPath("$.feedback", CoreMatchers.containsString("flag: " + Flag.FLAGS.get(1)))) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); + } + + @Test + public void wrongPassword() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/challenge/1") + .param("username", "admin") + .param("password", "wrong")) + .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved")))) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + } + +// @Test +// public void correctPasswordXForwardHeaderMissing() throws Exception { +// mockMvc.perform(MockMvcRequestBuilders.post("/challenge/1") +// .param("username", "admin") +// .param("password", SolutionConstants.PASSWORD)) +// .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("ip.address.unknown")))) +// .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); +// } + +// @Test +// public void correctPasswordXForwardHeaderWrong() throws Exception { +// mockMvc.perform(MockMvcRequestBuilders.post("/challenge/1") +// .header("X-Forwarded-For", "127.0.1.2") +// .param("username", "admin") +// .param("password", SolutionConstants.PASSWORD)) +// .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("ip.address.unknown")))) +// .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); +// } + +} \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge2/Assignment2Test.java b/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge2/Assignment2Test.java new file mode 100644 index 000000000..edf70df28 --- /dev/null +++ b/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge2/Assignment2Test.java @@ -0,0 +1,49 @@ +package org.owasp.webgoat.plugin.challenge2; + +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import org.owasp.webgoat.assignments.AssignmentEndpointTest; +import org.owasp.webgoat.plugin.Flag; +import org.owasp.webgoat.plugin.SolutionConstants; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +/** + * @author nbaars + * @since 5/2/17. + */ +@RunWith(MockitoJUnitRunner.class) +public class Assignment2Test extends AssignmentEndpointTest { + + private MockMvc mockMvc; + + @Before + public void setup() { + Assignment2 assignment2 = new Assignment2(); + init(assignment2); + new Flag().initFlags(); + this.mockMvc = standaloneSetup(assignment2).build(); + } + + @Test + public void success() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/challenge/2") + .param("checkoutCode", SolutionConstants.SUPER_COUPON_CODE)) + .andExpect(jsonPath("$.feedback", CoreMatchers.containsString("flag: " + Flag.FLAGS.get(2)))) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); + } + + @Test + public void wrongCouponCode() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/challenge/2") + .param("checkoutCode", "test")) + .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved")))) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + } +} \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge2/ShopEndpointTest.java b/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge2/ShopEndpointTest.java new file mode 100644 index 000000000..87710a6b7 --- /dev/null +++ b/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge2/ShopEndpointTest.java @@ -0,0 +1,60 @@ +package org.owasp.webgoat.plugin.challenge2; + +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; + +import static org.hamcrest.Matchers.is; +import static org.owasp.webgoat.plugin.SolutionConstants.SUPER_COUPON_CODE; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +/** + * @author nbaars + * @since 5/2/17. + */ +@RunWith(MockitoJUnitRunner.class) +public class ShopEndpointTest { + + private MockMvc mockMvc; + + @Before + public void setup() { + ShopEndpoint shopEndpoint = new ShopEndpoint(); + this.mockMvc = standaloneSetup(shopEndpoint).build(); + } + + @Test + public void getSuperCoupon() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/challenge-store/coupons/" + SUPER_COUPON_CODE)) + .andExpect(jsonPath("$.code", CoreMatchers.is(SUPER_COUPON_CODE))) + .andExpect(jsonPath("$.discount", CoreMatchers.is(100))); + } + + @Test + public void getCoupon() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/challenge-store/coupons/webgoat")) + .andDo(MockMvcResultHandlers.print()) + .andExpect(jsonPath("$.code", CoreMatchers.is("webgoat"))) + .andExpect(jsonPath("$.discount", CoreMatchers.is(25))); + } + + @Test + public void askForUnknownCouponCode() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/challenge-store/coupons/does-not-exists")) + .andExpect(jsonPath("$.code", CoreMatchers.is("no"))) + .andExpect(jsonPath("$.discount", CoreMatchers.is(0))); + } + + @Test + public void fetchAllTheCouponsShouldContainGetItForFree() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/challenge-store/coupons/")) + .andExpect(jsonPath("$.codes[3].code", is("get_it_for_free"))); + } + +} \ No newline at end of file diff --git a/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge4/VotesEndpointTest.java b/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge4/VotesEndpointTest.java new file mode 100644 index 000000000..b27519506 --- /dev/null +++ b/webgoat-lessons/challenge/src/test/java/org/owasp/webgoat/plugin/challenge4/VotesEndpointTest.java @@ -0,0 +1,163 @@ +package org.owasp.webgoat.plugin.challenge4; + +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import org.owasp.webgoat.plugin.Flag; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; + +import javax.servlet.http.Cookie; + +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +/** + * @author nbaars + * @since 5/2/17. + */ +@RunWith(MockitoJUnitRunner.class) +public class VotesEndpointTest { + + private MockMvc mockMvc; + + @Before + public void setup() { + VotesEndpoint votesEndpoint = new VotesEndpoint(); + votesEndpoint.initVotes(); + new Flag().initFlags(); + this.mockMvc = standaloneSetup(votesEndpoint).build(); + } + + @Test + public void loginWithUnknownUser() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings/login") + .param("user", "uknown")) + .andExpect(unauthenticated()); + } + + @Test + public void loginWithTomShouldGiveJwtToken() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings/login") + .param("user", "Tom")) + .andExpect(status().isOk()).andExpect(cookie().exists("access_token")); + } + + @Test + public void loginWithGuestShouldNotGiveJwtToken() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings/login") + .param("user", "Guest")) + .andExpect(unauthenticated()).andExpect(cookie().value("access_token", "")); + } + + @Test + public void userShouldSeeMore() throws Exception { + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login") + .param("user", "Tom")) + .andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn(); + mockMvc.perform(MockMvcRequestBuilders.get("/votings") + .cookie(mvcResult.getResponse().getCookie("access_token"))) + .andExpect(jsonPath("$.[*].numberOfVotes").exists()); + } + + @Test + public void guestShouldNotSeeNumberOfVotes() throws Exception { + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login") + .param("user", "Guest")) + .andExpect(unauthenticated()).andExpect(cookie().exists("access_token")).andReturn(); + mockMvc.perform(MockMvcRequestBuilders.get("/votings") + .cookie(mvcResult.getResponse().getCookie("access_token"))) + .andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist()); + } + + @Test + public void adminShouldSeeFlags() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings") + .cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJhZG1pbiI6InRydWUiLCJ1c2VyIjoiSmVycnkifQ."))) + .andExpect(jsonPath("$.[*].flag").isNotEmpty()); + } + + @Test + public void votingIsNotAllowedAsGuest() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free")) + .andExpect(unauthenticated()); + } + + @Test + public void normalUserShouldBeAbleToVote() throws Exception { + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login") + .param("user", "Tom")) + .andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn(); + mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free") + .cookie(mvcResult.getResponse().getCookie("access_token"))); + mockMvc.perform(MockMvcRequestBuilders.get("/votings/") + .cookie(mvcResult.getResponse().getCookie("access_token"))) + .andDo(MockMvcResultHandlers.print()) + .andExpect(jsonPath("$..[?(@.title == 'Get it for free')].numberOfVotes", CoreMatchers.hasItem(20001))); + } + + @Test + public void votingForUnknownLessonShouldNotCrash() throws Exception { + MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login") + .param("user", "Tom")) + .andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn(); + mockMvc.perform(MockMvcRequestBuilders.post("/votings/UKNOWN_VOTE") + .cookie(mvcResult.getResponse().getCookie("access_token"))).andExpect(status().isAccepted()); + } + + @Test + public void votingWithInvalidToken() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/votings/UKNOWN_VOTE") + .cookie(new Cookie("access_token", "abc"))).andExpect(unauthenticated()); + } + + @Test + public void gettingVotesWithInvalidToken() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings/") + .cookie(new Cookie("access_token", "abc"))).andExpect(unauthenticated()); + } + + @Test + public void gettingVotesWithUnknownUserInToken() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings/") + .cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJhZG1pbiI6InRydWUiLCJ1c2VyIjoiVW5rbm93biJ9."))) + .andExpect(unauthenticated()) + .andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist()); + } + + @Test + public void gettingVotesForUnknownShouldWork() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings/") + .cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVW5rbm93biJ9."))) + .andExpect(unauthenticated()) + .andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist()); + } + + @Test + public void gettingVotesForKnownWithoutAdminFieldShouldWork() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings/") + .cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVG9tIn0."))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.[*].numberOfVotes").exists()); + } + + @Test + public void gettingVotesWithEmptyToken() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/votings/") + .cookie(new Cookie("access_token", ""))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist()); + } + + @Test + public void votingAsUnknownUserShouldNotBeAllowed() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free") + .cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVW5rbm93biJ9."))) + .andExpect(unauthenticated()); + } +} \ No newline at end of file diff --git a/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html b/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html index efdfb9e61..595c7f960 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html +++ b/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html @@ -32,7 +32,7 @@
    -
    +
    Options from the menu +. Select Local Proxy on the left +. Choose an available port ... Since WebGoat is using port 8080, use something different like 8090 +. Click OK + +image::images/zap-local-proxy.png[ZAP local proxy,800,648,style="lesson-image"] \ No newline at end of file diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro1.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro1.adoc index 7cc84b033..bce555d3b 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro1.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro1.adoc @@ -17,11 +17,3 @@ When ZAP starts, you will be presented with a dialog such as the one below ... image::images/zap-start.png[ZAP Start,548,256,style="lesson-image"] -=== Configure Proxy's Port - -. Select Tools > Options from the menu -. Select Local Proxy on the left -. Choose an available port ... Since WebGoat is using port 8080, use something different like 8090 -. Click OK - -image::images/zap-local-proxy.png[ZAP local proxy,800,648,style="lesson-image"] diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro2.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro2.adoc index 6b4fad1b1..4adcecc5f 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro2.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro2.adoc @@ -11,7 +11,7 @@ This will send all of your traffic to the proxy. Since we haven't set up a trust . Click _Settings_ . Select _Manual proxy configuration_ .. input *127.0.0.1* as the Proxy -.. input *8090* as the port +.. input *8080* as the port .. check the _Use this proxy server for all protocols_ checkbox image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"] @@ -23,11 +23,20 @@ image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesso . Click the _Change proxy settings_ button . Select the _proxies_ tab . Select Web Proxy (HTTP) -. Input 127..0.0.1 in the first box under _Web Proxy Server_ and your port # (8090 is what used earlier) in the second box (to the right) +. Input 127.0.0.1 in the first box under _Web Proxy Server_ and your port # (8080 is what used earlier) in the second box (to the right) . You may also want to clear the _Bypass proxy settings for these Hosts & Domains_ text input at the bottom, but shouldn't need to + image::images/chrome-manual-proxy.png[Chrome Proxy Config,700,447,style="lesson-image"] +(Mac config image above) + + + +image::images/chrome-manual-proxy-win.png[Chrome Proxy, 394,346,style="lesson-image"] + +(Win config image above) + === Other Proxy Configuration Options If you don't want to manage the proxy manually, there are extensions or plugins that can help you to do so without digging through as much config, diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntercept.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro4.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntercept.adoc rename to webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/HttpBasics_ProxyIntro4.adoc diff --git a/webgoat-lessons/http-proxies/src/test/java/org/owasp/webgoat/plugin/HttpBasicsInterceptRequestTest.java b/webgoat-lessons/http-proxies/src/test/java/org/owasp/webgoat/plugin/HttpBasicsInterceptRequestTest.java index 90686bca7..ae32edacb 100644 --- a/webgoat-lessons/http-proxies/src/test/java/org/owasp/webgoat/plugin/HttpBasicsInterceptRequestTest.java +++ b/webgoat-lessons/http-proxies/src/test/java/org/owasp/webgoat/plugin/HttpBasicsInterceptRequestTest.java @@ -53,7 +53,7 @@ public class HttpBasicsInterceptRequestTest extends AssignmentEndpointTest { @Test public void success() throws Exception { - mockMvc.perform(MockMvcRequestBuilders.get("/HttpProxies/intercept-request") + mockMvc.perform(MockMvcRequestBuilders.get("/challenge/1") .header("x-request-intercepted", "true") .param("changeMe", "Requests are tampered easily")) .andExpect(status().isOk()).andDo(MockMvcResultHandlers.print()) diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java index 69b0e8e1c..1896cad1f 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java @@ -22,8 +22,6 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; -import static org.owasp.webgoat.plugin.SimpleXXE.parseXml; - /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, @@ -75,7 +73,7 @@ public class BlindSendFileAssignment extends AssignmentEndpoint { public AttackResult createNewUser(@RequestBody String userInfo) throws Exception { String error = "Parsing successful contents not send to server"; try { - parseXml(userInfo); + //parseXml(userInfo); } catch (Exception e) { error = ExceptionUtils.getFullStackTrace(e); } diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Comment.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Comment.java new file mode 100644 index 000000000..bce74cc40 --- /dev/null +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Comment.java @@ -0,0 +1,23 @@ +package org.owasp.webgoat.plugin; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * @author nbaars + * @since 4/8/17. + */ +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@XmlRootElement +public class Comment { + private String user; + private String dateTime; + private String text; +} diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Comments.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Comments.java new file mode 100644 index 000000000..22ba7cf72 --- /dev/null +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Comments.java @@ -0,0 +1,90 @@ +package org.owasp.webgoat.plugin; + +import com.beust.jcommander.internal.Lists; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.EvictingQueue; +import com.google.common.collect.Maps; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.owasp.webgoat.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Collection; +import java.util.Map; + +/** + * @author nbaars + * @since 5/3/17. + */ +@Component +@Scope("singleton") +public class Comments { + + @Autowired + protected WebSession webSession; + + protected static DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd, HH:mm:ss"); + + private static final Map> userComments = Maps.newHashMap(); + private static final EvictingQueue comments = EvictingQueue.create(100); + + static { + comments.add(new Comment("webgoat", DateTime.now().toString(fmt), "Silly cat....")); + comments.add(new Comment("guest", DateTime.now().toString(fmt), "I think I will use this picture in one of my projects.")); + comments.add(new Comment("guest", DateTime.now().toString(fmt), "Lol!! :-).")); + } + + protected Collection getComments() { + Collection allComments = Lists.newArrayList(); + Collection xmlComments = userComments.get(webSession.getUserName()); + if (xmlComments != null) { + allComments.addAll(xmlComments); + } + allComments.addAll(comments); + return allComments; + } + + protected Comment parseXml(String xml) throws Exception { + JAXBContext jc = JAXBContext.newInstance(Comment.class); + + XMLInputFactory xif = XMLInputFactory.newFactory(); + xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true); + xif.setProperty(XMLInputFactory.IS_VALIDATING, false); + + xif.setProperty(XMLInputFactory.SUPPORT_DTD, true); + XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml)); + + Unmarshaller unmarshaller = jc.createUnmarshaller(); + return (Comment) unmarshaller.unmarshal(xsr); + } + + protected Comment parseJson(String comment) { + ObjectMapper mapper = new ObjectMapper(); + try { + return mapper.readValue(comment, Comment.class); + } catch (IOException e) { + return new Comment(); + } + } + + public void addComment(Comment comment, boolean visibleForAllUsers) { + comment.setDateTime(DateTime.now().toString(fmt)); + comment.setUser(webSession.getUserName()); + if (visibleForAllUsers) { + comments.add(comment); + } else { + EvictingQueue comments = userComments.getOrDefault(webSession.getUserName(), EvictingQueue.create(100)); + comments.add(comment); + userComments.put(webSession.getUserName(), comments); + } + } +} diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java index 0b2fa3611..be850944e 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java @@ -6,17 +6,10 @@ import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AttackResult; import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import java.io.IOException; -import static org.owasp.webgoat.plugin.SimpleXXE.checkSolution; -import static org.owasp.webgoat.plugin.SimpleXXE.parseXml; - /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, @@ -60,13 +53,13 @@ public class ContentTypeAssignment extends AssignmentEndpoint { attackResult = failed().feedback("xxe.content.type.feedback.json").build(); } if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) { - user = parseXml(userInfo); + // user = parseXml(userInfo); attackResult = failed().feedback("xxe.content.type.feedback.xml").build(); } - if (checkSolution(user)) { - attackResult = success().output("xxe.content.output").outputArgs(user.getUsername()).build(); - } +// if (checkSolution(user)) { +// attackResult = success().output("xxe.content.output").outputArgs(user.getUsername()).build(); +// } return attackResult; } diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java index eff49b9d3..d5c6b6a82 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java @@ -5,17 +5,21 @@ import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamReader; -import java.io.StringReader; +import java.util.Collection; + +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.web.bind.annotation.RequestMethod.POST; /** * ************************************************************************************************ @@ -46,47 +50,58 @@ import java.io.StringReader; * @version $Id: $Id * @since November 17, 2016 */ -@AssignmentPath("XXE/simple") + +/** + * @author nbaars + * @since 4/8/17. + */ +@AssignmentPath("xxe/simple") @AssignmentHints({"xxe.hints.simple.xxe.1", "xxe.hints.simple.xxe.2", "xxe.hints.simple.xxe.3", "xxe.hints.simple.xxe.4"}) public class SimpleXXE extends AssignmentEndpoint { private final static String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "opt", "var"}; private final static String[] DEFAULT_WINDOWS_DIRECTORIES = {"Windows", "Program Files (x86)", "Program Files"}; - @RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @Value("${webgoat.server.directory}") + private String webGoatHomeDirectory; + @Autowired + private WebSession webSession; + @Autowired + private Comments comments; + + @RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - public AttackResult createNewUser(@RequestBody String userInfo) throws Exception { - User user = parseXml(userInfo); - if (checkSolution(user)) { - return trackProgress(success() - .output("xxe.simple.output") - .outputArgs(user.getUsername()).build()); + public Collection retrieveComments() { + return comments.getComments(); + } + + @RequestMapping(method = POST, consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult createNewComment(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception { + Comment comment = null; + if (APPLICATION_JSON_VALUE.equals(contentType)) { + comment = comments.parseJson(commentStr); + comments.addComment(comment, true); + } + if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) { + //Do not show these comments to all users + comment = comments.parseXml(commentStr); + comments.addComment(comment, false); + } + if (checkSolution(comment)) { + return trackProgress(success() + .output("xxe.simple.output") + .outputArgs(webSession.getUserName()).build()); } return trackProgress(failed().build()); } - public static User parseXml(String xml) throws Exception { - JAXBContext jc = JAXBContext.newInstance(User.class); - - XMLInputFactory xif = XMLInputFactory.newFactory(); - xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true); - xif.setProperty(XMLInputFactory.IS_VALIDATING, false); - - xif.setProperty(XMLInputFactory.SUPPORT_DTD, true); - XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml)); - - Unmarshaller unmarshaller = jc.createUnmarshaller(); - return (User) unmarshaller.unmarshal(xsr); - } - - public static boolean checkSolution(User userInfo) { + private boolean checkSolution(Comment comment) { String[] directoriesToCheck = OS.isFamilyUnix() ? DEFAULT_LINUX_DIRECTORIES : DEFAULT_WINDOWS_DIRECTORIES; boolean success = true; for (String directory : directoriesToCheck) { - success &= userInfo.getUsername().contains(directory); + success &= comment.getText().contains(directory); } return success; } - - } diff --git a/webgoat-lessons/xxe/src/main/resources/css/xxe.css b/webgoat-lessons/xxe/src/main/resources/css/xxe.css new file mode 100644 index 000000000..3bc2ca4eb --- /dev/null +++ b/webgoat-lessons/xxe/src/main/resources/css/xxe.css @@ -0,0 +1,75 @@ +/* Component: Posts */ +.post .post-heading { + height: 95px; + padding: 20px 15px; +} +.post .post-heading .avatar { + width: 60px; + height: 60px; + display: block; + margin-right: 15px; +} +.post .post-heading .meta .title { + margin-bottom: 0; +} +.post .post-heading .meta .title a { + color: black; +} +.post .post-heading .meta .title a:hover { + color: #aaaaaa; +} +.post .post-heading .meta .time { + margin-top: 8px; + color: #999; +} +.post .post-image .image { + width:20%; + height: 40%; +} +.post .post-description { + padding: 5px; +} +.post .post-footer { + border-top: 1px solid #ddd; + padding: 15px; +} +.post .post-footer .input-group-addon a { + color: #454545; +} +.post .post-footer .comments-list { + padding: 0; + margin-top: 20px; + list-style-type: none; +} +.post .post-footer .comments-list .comment { + display: block; + width: 100%; + margin: 20px 0; +} +.post .post-footer .comments-list .comment .avatar { + width: 35px; + height: 35px; +} +.post .post-footer .comments-list .comment .comment-heading { + display: block; + width: 100%; +} +.post .post-footer .comments-list .comment .comment-heading .user { + font-size: 14px; + font-weight: bold; + display: inline; + margin-top: 0; + margin-right: 10px; +} +.post .post-footer .comments-list .comment .comment-heading .time { + font-size: 12px; + color: #aaa; + margin-top: 0; + display: inline; +} +.post .post-footer .comments-list .comment .comment-body { + margin-left: 50px; +} +.post .post-footer .comments-list .comment > .comments-list { + margin-left: 50px; +} \ No newline at end of file diff --git a/webgoat-lessons/xxe/src/main/resources/html/XXE.html b/webgoat-lessons/xxe/src/main/resources/html/XXE.html index 831d55cdc..7d79b58d3 100644 --- a/webgoat-lessons/xxe/src/main/resources/html/XXE.html +++ b/webgoat-lessons/xxe/src/main/resources/html/XXE.html @@ -1,5 +1,8 @@ + + +
    -
    - +
    - - - - - - -
    - Registration form - - - - - - - - - - - - - - - - - - -
    Username
    E-mail
    Password
    -
    -
    - -
    -
    + +
    +
    +
    +
    + user profile image +
    +
    +
    + John Doe + uploaded a photo. +
    +
    24 days ago
    +
    +
    + +
    + image post +
    + +
    + +
    +
    - -
    +
    + +
    +
    +
    - -
    -
    -
    @@ -77,8 +79,6 @@
    -
    Registration form @@ -137,8 +137,6 @@ -
    Registration form diff --git a/webgoat-lessons/xxe/src/main/resources/images/cat.jpg b/webgoat-lessons/xxe/src/main/resources/images/cat.jpg new file mode 100644 index 000000000..e0e1fb983 Binary files /dev/null and b/webgoat-lessons/xxe/src/main/resources/images/cat.jpg differ diff --git a/webgoat-lessons/xxe/src/main/resources/js/xxe.js b/webgoat-lessons/xxe/src/main/resources/js/xxe.js index b38c2d9c2..9002de2e4 100644 --- a/webgoat-lessons/xxe/src/main/resources/js/xxe.js +++ b/webgoat-lessons/xxe/src/main/resources/js/xxe.js @@ -1,15 +1,45 @@ -webgoat.customjs.register = function () { - var xml = '' + - '' + - ' ' + 'test' + '' + - ' ' + 'test' + '' + - ''; - return xml; -} -webgoat.customjs.registerJson = function () { - var json = '{' + - ' "user":' + '"test"' + - ' "password":' + '"test"' + - '}'; - return json; -} +$(document).ready(function () { + $("#postComment").unbind(); + $("#postComment").on("click", function () { + var commentInput = $("#commentInput").val(); + $.ajax({ + type: 'POST', + url: 'xxe/simple', + data: JSON.stringify({text: commentInput}), + contentType: "application/json", + dataType: 'json' + }).then( + function () { + getComments(); + $("#commentInput").val(''); + } + ) + }) + getComments(); +}) + +var html = '
  • ' + + '
    ' + + 'avatar' + + '
    ' + + '
    ' + + '
    ' + + '

    USER

    ' + + '
    DATETIME
    ' + + '
    ' + + '

    COMMENT

    ' + + '
    ' + + '
  • '; + +function getComments() { + $.get("xxe/simple", function (result, status) { + $("#comments_list").empty(); + for (var i = 0; i < result.length; i++) { + var comment = html.replace('USER', result[i].user); + comment = comment.replace('DATETIME', result[i].dateTime); + comment = comment.replace('COMMENT', result[i].text); + $("#comments_list").append(comment); + } + + }); +} \ No newline at end of file diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple.adoc b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple.adoc index dd5506af9..ef51f2d19 100644 --- a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple.adoc +++ b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple.adoc @@ -1,4 +1,4 @@ == Let's try -In this assignment you will need to sign up with a registration form. When submitting the form try to execute an XXE injection with the -username field. Try listing the root directory of the filesystem. +In this assignment you will add a comment to the photo, when submitting the form try to execute an XXE +injection with the comments field. Try listing the root directory of the filesystem. diff --git a/webgoat-server/pom.xml b/webgoat-server/pom.xml index 8dff0cda9..78d55dff2 100644 --- a/webgoat-server/pom.xml +++ b/webgoat-server/pom.xml @@ -86,11 +86,11 @@ webgoat-container ${project.version} - - - - - + + org.owasp.webgoat.lesson + challenge + ${project.version} + org.owasp.webgoat.lesson client-side-filtering