From 9c03b6f63b123145a0dcb0874008d0bc3890063e Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 27 Dec 2016 21:04:56 +0100 Subject: [PATCH] #276 Automatic lesson summary page - Basic overview of all the assignments needed to be solved in a lesson - Clicking on a link will jump to the correct page with the assignment - Lesson completed also updates lesson overview immediately --- webgoat-container/src/main/docker/Dockerfile | 2 +- .../org/owasp/webgoat/MvcConfiguration.java | 2 + .../webgoat/endpoints/AssignmentEndpoint.java | 7 ++- .../org/owasp/webgoat/lessons/Assignment.java | 2 +- .../org/owasp/webgoat/plugins/Plugin.java | 6 ++- .../service/LessonProgressService.java | 8 +++- .../src/main/resources/application.properties | 3 +- .../js/goatApp/controller/LessonController.js | 29 ++++++++--- .../js/goatApp/model/AssignmentModel.js | 8 ++++ .../js/goatApp/model/LessonOverviewModel.js | 10 ++++ .../js/goatApp/view/AssignmentOverview.js | 24 ++++++++++ .../js/goatApp/view/HelpControlsView.js | 5 +- .../js/goatApp/view/LessonContentView.js | 21 +++++++- .../js/goatApp/view/LessonOverviewView.js | 48 +++++++++++++++++++ .../main/resources/templates/main_new.html | 17 +++++++ .../service/LessonProgressServiceTest.java | 2 +- .../webgoat/session/LessonTrackerTest.java | 6 +-- .../webgoat/session/UserTrackerTest.java | 6 +-- .../java/org/owasp/webgoat/plugin/Attack.java | 7 +-- .../plugin/CrossSiteScriptingLesson1.java | 7 +-- .../plugin/CrossSiteScriptingLesson5a.java | 8 +--- .../plugin/CrossSiteScriptingLesson5b.java | 9 +--- .../plugin/CrossSiteScriptingLesson6a.java | 8 +--- .../plugin/CrossSiteScriptingLesson6b.java | 8 +--- .../webgoat/plugin/DOMCrossSiteScripting.java | 7 +-- .../webgoat/plugin/HttpBasicsLesson.java | 9 +--- .../owasp/webgoat/plugin/HttpBasicsQuiz.java | 9 +--- .../webgoat/plugin/SqlInjectionLesson5a.java | 8 +--- .../webgoat/plugin/SqlInjectionLesson5b.java | 9 +--- .../webgoat/plugin/SqlInjectionLesson6a.java | 8 +--- .../webgoat/plugin/SqlInjectionLesson6b.java | 8 +--- .../plugin/BlindSendFileAssignment.java | 7 +-- .../webgoat/plugin/ContentTypeAssignment.java | 7 +-- .../org/owasp/webgoat/plugin/SimpleXXE.java | 7 +-- 34 files changed, 214 insertions(+), 118 deletions(-) create mode 100644 webgoat-container/src/main/resources/static/js/goatApp/model/AssignmentModel.js create mode 100644 webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewModel.js create mode 100644 webgoat-container/src/main/resources/static/js/goatApp/view/AssignmentOverview.js create mode 100644 webgoat-container/src/main/resources/static/js/goatApp/view/LessonOverviewView.js diff --git a/webgoat-container/src/main/docker/Dockerfile b/webgoat-container/src/main/docker/Dockerfile index a33389017..6d7bf526b 100644 --- a/webgoat-container/src/main/docker/Dockerfile +++ b/webgoat-container/src/main/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM frolvlad/alpine-oraclejdk8:slim +FROM openjdk:8-jre VOLUME /tmp RUN cd /root; mkdir -p .webgoat ADD webgoat-container-8.0-SNAPSHOT.war webgoat.jar 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 02ab81b6d..4028d2ca4 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java @@ -44,6 +44,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect; import org.thymeleaf.spring4.SpringTemplateEngine; import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.templatemode.StandardTemplateModeHandlers; import org.thymeleaf.templateresolver.TemplateResolver; import java.io.File; @@ -71,6 +72,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter { resolver.setPrefix("classpath:/templates/"); resolver.setSuffix(".html"); resolver.setOrder(1); + resolver.setCacheable(false); resolver.setApplicationContext(applicationContext); return resolver; } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/endpoints/AssignmentEndpoint.java b/webgoat-container/src/main/java/org/owasp/webgoat/endpoints/AssignmentEndpoint.java index e279b1814..b1cbd9544 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/endpoints/AssignmentEndpoint.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/endpoints/AssignmentEndpoint.java @@ -30,6 +30,8 @@ import org.owasp.webgoat.session.UserTracker; import org.owasp.webgoat.session.WebSession; import org.springframework.beans.factory.annotation.Autowired; +import javax.ws.rs.Path; + /** * Each lesson can define an endpoint which can support the lesson. So for example if you create a lesson which uses JavaScript and * needs to call out to the server to fetch data you can define an endpoint in that lesson. WebGoat will pick up this endpoint and @@ -61,5 +63,8 @@ public abstract class AssignmentEndpoint extends Endpoint { return webSession; } - + @Override + public final String getPath() { + return this.getClass().getAnnotationsByType(Path.class)[0].value(); + } } 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 704a40189..3011c079d 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 @@ -39,6 +39,6 @@ import java.io.Serializable; public class Assignment implements Serializable { private final String name; - + private final String path; } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java index 9ebee50e0..d86aecc6b 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java @@ -114,7 +114,11 @@ public class Plugin { private List createAssignment(List> endpoints) { - return endpoints.stream().map(e -> new Assignment(e.getSimpleName())).collect(toList()); + return endpoints.stream().map(e -> new Assignment(e.getSimpleName(), getPath(e))).collect(toList()); + } + + private String getPath(Class e) { + return e.getAnnotationsByType(javax.ws.rs.Path.class)[0].value(); } 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 0583bcd76..fff1452ba 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 @@ -64,8 +64,12 @@ public class LessonProgressService { @ResponseBody public List lessonOverview() { AbstractLesson currentLesson = webSession.getCurrentLesson(); - LessonTracker lessonTracker = userTracker.getLessonTracker(currentLesson); - return toJson(lessonTracker.getLessonOverview()); + List result = Lists.newArrayList(); + if ( currentLesson != null ) { + LessonTracker lessonTracker = userTracker.getLessonTracker(currentLesson); + result = toJson(lessonTracker.getLessonOverview()); + } + return result; } private List toJson(Map map) { diff --git a/webgoat-container/src/main/resources/application.properties b/webgoat-container/src/main/resources/application.properties index b274e88e4..6eeb2f6bd 100644 --- a/webgoat-container/src/main/resources/application.properties +++ b/webgoat-container/src/main/resources/application.properties @@ -10,11 +10,10 @@ logging.level.org.springframework.boot.devtools=WARN logging.level.org.owasp=DEBUG logging.level.org.owasp.webgoat=TRACE -spring.thymeleaf.cache=false -spring.thymeleaf.content-type=text/html security.enable-csrf=false spring.devtools.restart.enabled=false +spring.resources.cache-period=0 webgoat.tracker.overwrite=true 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 c12544765..1f6f1f935 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 @@ -18,8 +18,10 @@ define(['jquery', 'goatApp/model/LessonInfoModel', 'goatApp/view/TitleView', 'goatApp/model/LessonProgressModel', - 'goatApp/view/LessonProgressView' - ], + 'goatApp/view/LessonProgressView', + 'goatApp/view/LessonOverviewView', + 'goatApp/model/LessonOverviewModel' + ], function($, _, Backbone, @@ -40,19 +42,22 @@ define(['jquery', LessonInfoModel, TitleView, LessonProgressModel, - LessonProgressView - + LessonProgressView, + LessonOverviewView, + LessonOverviewModel ) { 'use strict' - - + var Controller = function(options) { this.lessonContent = new LessonContentModel(); this.lessonProgressModel = new LessonProgressModel(); this.lessonProgressView = new LessonProgressView(this.lessonProgressModel); + this.lessonOverviewModel = new LessonOverviewModel(); + this.lessonOverview = new LessonOverviewView(this.lessonOverviewModel); this.lessonContentView = options.lessonContentView; this.developerControlsView = new DeveloperControlsView(); + _.extend(Controller.prototype,Backbone.Events); this.start = function() { @@ -92,16 +97,18 @@ define(['jquery', }); this.listenTo(this.helpControlsView,'hints:show',this.showHints); + this.listenTo(this.helpControlsView,'lessonOverview:show',this.showLessonOverview) this.listenTo(this.helpControlsView,'attack:show',this.hideShowAttack); 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.listenTo(this.lessonContentView, 'lesson:complete', this.updateMenu) - + this.listenTo(this.lessonContentView, 'lesson:complete', this.updateLessonOverview) this.listenTo(this,'hints:show',this.onShowHints); this.helpControlsView.render(); + this.lessonOverviewModel.fetch(); this.titleView.render(this.lessonInfoModel.get('lessonTitle')); }; @@ -110,6 +117,10 @@ define(['jquery', this.trigger('menu:reload') }; + this.updateLessonOverview = function() { + this.lessonOverviewModel.fetch(); + } + this.onContentLoaded = function(loadHelps) { this.lessonInfoModel = new LessonInfoModel(); this.listenTo(this.lessonInfoModel,'info:loaded',this.onInfoLoaded); @@ -177,6 +188,10 @@ define(['jquery', //this.lessonHintView. }; + this.showLessonOverview = function() { + this.lessonOverview.render(); + }; + this.hideShowAttack = function (options) { // will likely expand this to encompass if (options.show) { $('#attack-container').show(); diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/AssignmentModel.js b/webgoat-container/src/main/resources/static/js/goatApp/model/AssignmentModel.js new file mode 100644 index 000000000..7d18b6bf4 --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/model/AssignmentModel.js @@ -0,0 +1,8 @@ +define([ + 'backbone'], + function( + Backbone) { + return Backbone.Model.extend({ + }); +}); + 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 new file mode 100644 index 000000000..28647bb89 --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewModel.js @@ -0,0 +1,10 @@ +define([ + 'backbone'], + function( + Backbone) { + return Backbone.Collection.extend({ + tagName: 'ul', + url: 'service/lessonoverview.mvc' + }); +}); + diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/AssignmentOverview.js b/webgoat-container/src/main/resources/static/js/goatApp/view/AssignmentOverview.js new file mode 100644 index 000000000..f1f33dc18 --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/view/AssignmentOverview.js @@ -0,0 +1,24 @@ +define([ + 'backbone'], + function( + Backbone) { + return Backbone.View.extend({ + tagName: 'li', + template: _.template($('#assignmentTemplate').html() ), + + events: { + "click a": "clicked" + }, + + clicked: function(e){ + e.preventDefault(); + var id = $(e.currentTarget).data("id"); + Backbone.trigger('assignment:navTo',{'assignment': id}); + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON())); + return this; + } + }); +}); 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 b29220224..34798fda5 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 @@ -36,7 +36,7 @@ function($,_,Backbone) { this.$el.find('#show-attack-button').unbind().on('click',_.bind(this.showAttack,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(); //this.$el.append(this.helpButtons.restartLesson); }, @@ -59,6 +59,9 @@ function($,_,Backbone) { restartLesson: function() { this.trigger('lesson:restart'); + }, + showLessonOverview: function() { + this.trigger('lessonOverview:show'); } }); }); \ No newline at end of file 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 b9c220124..ee9a381b0 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 @@ -16,6 +16,25 @@ define(['jquery', initialize: function(options) { options = options || {}; new ErrorNotificationView(); + var self = this; + Backbone.on('assignment:navTo', function(assignment){ + var page = self.findPage(assignment); + if (page != -1) { + self.navToPage(page); + } + }); + }, + + findPage: function(assignment) { + for (var i = 0; i < this.$contentPages.length; i++) { + var contentPage = this.$contentPages[i]; + var form = $('form.attack-form', contentPage); + var action = form.attr('action') + if (action !== undefined && action.includes(assignment.assignment)) { + return i; + } + } + return -1; }, /* initial renering */ @@ -76,7 +95,7 @@ define(['jquery', var submitData = (typeof webgoat.customjs[prepareDataFunctionName] === 'function') ? webgoat.customjs[prepareDataFunctionName]() : this.$form.serialize(); // var submitData = this.$form.serialize(); this.$curFeedback = $(curForm).closest('.attack-container').find('.attack-feedback'); - this.$curOutput = $(curForm).closest('.atatck-container').find('.attack-output'); + this.$curOutput = $(curForm).closest('.attack-container').find('.attack-output'); var formUrl = $(curForm).attr('action'); var formMethod = $(curForm).attr('method'); var contentType = ($(curForm).attr('contentType')) ? $(curForm).attr('contentType') : 'application/x-www-form-urlencoded; charset=UTF-8'; 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 new file mode 100644 index 000000000..58f1c2e80 --- /dev/null +++ b/webgoat-container/src/main/resources/static/js/goatApp/view/LessonOverviewView.js @@ -0,0 +1,48 @@ +define(['jquery', + 'underscore', + 'backbone', + 'goatApp/model/LessonOverviewModel', + 'goatApp/view/AssignmentOverview'], +function($, + _, + Backbone, + LessonOverviewModel, + AssignmentOverview) { + return Backbone.View.extend({ + el:'#lesson-overview', + initialize: function (lessonOverviewModel) { + this.model = lessonOverviewModel; + this.listenTo(this.model, 'change add remove update', this.render); + this.hideLessonOverview(); + }, + + showAssignments: function() { + this.$el.html(''); + this.model.each(function(assignment) { + var assignmentView = new AssignmentOverview({ model: assignment }); + this.$el.append(assignmentView.render().el); + }, this); + }, + + render: function() { + if (this.isVisible()) { + this.$el.hide(); + } else { + this.$el.show(); + } + this.showAssignments(); + + return this; + }, + + isVisible: function() { + return this.$el.is(':visible'); + }, + + hideLessonOverview: function() { + if (this.$el.is(':visible')) { + this.$el.hide(); + } + } + }); +}); \ 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 0da077a2f..53cf9bb61 100644 --- a/webgoat-container/src/main/resources/templates/main_new.html +++ b/webgoat-container/src/main/resources/templates/main_new.html @@ -39,6 +39,14 @@ WebGoat + + +