Skip to content
Permalink
Browse files

#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
  • Loading branch information...
nbaars committed Dec 27, 2016
1 parent de4e581 commit 9c03b6f63b123145a0dcb0874008d0bc3890063e
Showing with 214 additions and 118 deletions.
  1. +1 −1 webgoat-container/src/main/docker/Dockerfile
  2. +2 −0 webgoat-container/src/main/java/org/owasp/webgoat/MvcConfiguration.java
  3. +6 −1 webgoat-container/src/main/java/org/owasp/webgoat/endpoints/AssignmentEndpoint.java
  4. +1 −1 webgoat-container/src/main/java/org/owasp/webgoat/lessons/Assignment.java
  5. +5 −1 webgoat-container/src/main/java/org/owasp/webgoat/plugins/Plugin.java
  6. +6 −2 webgoat-container/src/main/java/org/owasp/webgoat/service/LessonProgressService.java
  7. +1 −2 webgoat-container/src/main/resources/application.properties
  8. +22 −7 webgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js
  9. +8 −0 webgoat-container/src/main/resources/static/js/goatApp/model/AssignmentModel.js
  10. +10 −0 webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewModel.js
  11. +24 −0 webgoat-container/src/main/resources/static/js/goatApp/view/AssignmentOverview.js
  12. +4 −1 webgoat-container/src/main/resources/static/js/goatApp/view/HelpControlsView.js
  13. +20 −1 webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js
  14. +48 −0 webgoat-container/src/main/resources/static/js/goatApp/view/LessonOverviewView.js
  15. +17 −0 webgoat-container/src/main/resources/templates/main_new.html
  16. +1 −1 webgoat-container/src/test/java/org/owasp/webgoat/service/LessonProgressServiceTest.java
  17. +3 −3 webgoat-container/src/test/java/org/owasp/webgoat/session/LessonTrackerTest.java
  18. +3 −3 webgoat-container/src/test/java/org/owasp/webgoat/session/UserTrackerTest.java
  19. +2 −5 webgoat-lessons/client-side-filtering/src/main/java/org/owasp/webgoat/plugin/Attack.java
  20. +2 −5 ...essons/cross-site-scripting/src/main/java/org/owasp/webgoat/plugin/CrossSiteScriptingLesson1.java
  21. +2 −6 ...ssons/cross-site-scripting/src/main/java/org/owasp/webgoat/plugin/CrossSiteScriptingLesson5a.java
  22. +2 −7 ...ssons/cross-site-scripting/src/main/java/org/owasp/webgoat/plugin/CrossSiteScriptingLesson5b.java
  23. +2 −6 ...ssons/cross-site-scripting/src/main/java/org/owasp/webgoat/plugin/CrossSiteScriptingLesson6a.java
  24. +2 −6 ...ssons/cross-site-scripting/src/main/java/org/owasp/webgoat/plugin/CrossSiteScriptingLesson6b.java
  25. +2 −5 ...at-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/plugin/DOMCrossSiteScripting.java
  26. +2 −7 webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/plugin/HttpBasicsLesson.java
  27. +2 −7 webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/plugin/HttpBasicsQuiz.java
  28. +2 −6 webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/plugin/SqlInjectionLesson5a.java
  29. +2 −7 webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/plugin/SqlInjectionLesson5b.java
  30. +2 −6 webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/plugin/SqlInjectionLesson6a.java
  31. +2 −6 webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/plugin/SqlInjectionLesson6b.java
  32. +2 −5 webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java
  33. +2 −5 webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java
  34. +2 −5 webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java
@@ -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
@@ -44,6 +44,7 @@
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 TemplateResolver springThymeleafTemplateResolver(ApplicationContext appli
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setOrder(1);
resolver.setCacheable(false);
resolver.setApplicationContext(applicationContext);
return resolver;
}
@@ -30,6 +30,8 @@
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 @@ protected WebSession getWebSession() {
return webSession;
}


@Override
public final String getPath() {
return this.getClass().getAnnotationsByType(Path.class)[0].value();
}
}
@@ -39,6 +39,6 @@
public class Assignment implements Serializable {

private final String name;

private final String path;

}
@@ -114,7 +114,11 @@ public void loadFiles(Path file) {


private List<Assignment> createAssignment(List<Class<AssignmentEndpoint>> 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<AssignmentEndpoint> e) {
return e.getAnnotationsByType(javax.ws.rs.Path.class)[0].value();
}


@@ -64,8 +64,12 @@ public Map getLessonInfo() {
@ResponseBody
public List<LessonOverview> lessonOverview() {
AbstractLesson currentLesson = webSession.getCurrentLesson();
LessonTracker lessonTracker = userTracker.getLessonTracker(currentLesson);
return toJson(lessonTracker.getLessonOverview());
List<LessonOverview> result = Lists.newArrayList();
if ( currentLesson != null ) {
LessonTracker lessonTracker = userTracker.getLessonTracker(currentLesson);
result = toJson(lessonTracker.getLessonOverview());
}
return result;
}

private List<LessonOverview> toJson(Map<Assignment, Boolean> map) {
@@ -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
@@ -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();
@@ -0,0 +1,8 @@
define([
'backbone'],
function(
Backbone) {
return Backbone.Model.extend({
});
});

@@ -0,0 +1,10 @@
define([
'backbone'],
function(
Backbone) {
return Backbone.Collection.extend({
tagName: 'ul',
url: 'service/lessonoverview.mvc'
});
});

@@ -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;
}
});
});
@@ -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');
}
});
});
@@ -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';
@@ -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();
}
}
});
});
@@ -39,6 +39,14 @@
<title>WebGoat</title>
</head>
<body>
<script id="assignmentTemplate" type="text/template">
<![CDATA[
<!-- CDATA is necessary otherwise we get into trouble with the strict HTML5 validation (Thymeleaf) -->
<strong><a href="#" data-id=<%= assignment.path %>><%= assignment.name %></a></strong> (<%= solved %>)
<!-- ]]> -->
</script>

<section id="container">
<header id="header">
<!--logo start-->
@@ -126,6 +134,8 @@
<!--<button class="btn btn-primary btn-xs btn-danger help-button" id="show-attack-button">-->
<!--Attack It-->
<!--</button>-->
<button class="btn btn-primary btn-xs btn-danger help-button" id="show-lesson-overview-button">Lesson overview
</button>
<button class="btn btn-xs help-button" id="restart-lesson-button">
Reset Lesson
</button>
@@ -145,6 +155,13 @@
</div>
</div>
</div>

<div class="lesson-hint" id="lesson-overview-container">
<div class="panel">
<div class="panel-body" id="lesson-overview"></div>
</div>
</div>

<div class="lesson-content">

</div>
@@ -71,7 +71,7 @@

@Before
public void setup() {
Assignment assignment = new Assignment("test");
Assignment assignment = new Assignment("test", "test");
when(userTracker.getLessonTracker(any())).thenReturn(lessonTracker);
when(websession.getCurrentLesson()).thenReturn(lesson);
when(lessonTracker.getLessonOverview()).thenReturn(Maps.newHashMap(assignment, true));
@@ -47,7 +47,7 @@
@Test
public void allAssignmentsSolvedShouldMarkLessonAsComplete() {
AbstractLesson lesson = mock(AbstractLesson.class);
when(lesson.getAssignments()).thenReturn(Lists.newArrayList(new Assignment("assignment")));
when(lesson.getAssignments()).thenReturn(Lists.newArrayList(new Assignment("assignment", "")));
LessonTracker lessonTracker = new LessonTracker(lesson);
lessonTracker.assignmentSolved("assignment");

@@ -57,8 +57,8 @@ public void allAssignmentsSolvedShouldMarkLessonAsComplete() {
@Test
public void noAssignmentsSolvedShouldMarkLessonAsInComplete() {
AbstractLesson lesson = mock(AbstractLesson.class);
Assignment a1 = new Assignment("a1");
Assignment a2 = new Assignment("a2");
Assignment a1 = new Assignment("a1", "a1");
Assignment a2 = new Assignment("a2", "a2");
List<Assignment> assignments = Lists.newArrayList(a1, a2);
when(lesson.getAssignments()).thenReturn(assignments);
LessonTracker lessonTracker = new LessonTracker(lesson);

0 comments on commit 9c03b6f

Please sign in to comment.
You can’t perform that action at this time.