From 6e670c4ac54498dc632e4a868e2e3caceee1833e Mon Sep 17 00:00:00 2001 From: Jason White Date: Sun, 20 Sep 2015 00:09:38 -0400 Subject: [PATCH 1/2] #23, #24 - LessonInfo Service now used for TitleView and HelpControlsView --- .../lessons/model/LessonInfoModel.java | 72 ++++ .../webgoat/lessons/model/LessonMenuItem.java | 38 +- .../webgoat/service/LessonInfoService.java | 42 ++ .../webgoat/service/LessonMenuService.java | 14 - .../main/webapp/WEB-INF/pages/main_new.jsp | 5 + .../js/goatApp/controller/LessonController.js | 106 +++-- .../js/goatApp/controller/MenuController.js | 9 +- .../js/goatApp/model/LessonContentModel.js | 9 +- .../js/goatApp/model/LessonInfoModel.js | 21 + .../main/webapp/js/goatApp/view/GoatRouter.js | 22 +- .../main/webapp/js/goatApp/view/TitleView.js | 1 - .../src/main/webapp/js/libs/text.js | 391 ++++++++++++++++++ webgoat-container/src/main/webapp/js/main.js | 3 +- 13 files changed, 605 insertions(+), 128 deletions(-) create mode 100644 webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonInfoModel.java create mode 100644 webgoat-container/src/main/java/org/owasp/webgoat/service/LessonInfoService.java create mode 100644 webgoat-container/src/main/webapp/js/goatApp/model/LessonInfoModel.js create mode 100644 webgoat-container/src/main/webapp/js/libs/text.js diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonInfoModel.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonInfoModel.java new file mode 100644 index 000000000..ccafa2cea --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonInfoModel.java @@ -0,0 +1,72 @@ +package org.owasp.webgoat.lessons.model; + +import org.owasp.webgoat.lessons.AbstractLesson; +import org.owasp.webgoat.session.WebSession; + +/** + * Created by jason on 9/18/15. + */ +public class LessonInfoModel { + + private String lessonTitle; + private int numberHints; + private boolean hasSource; + private boolean hasSolution; + private boolean hasPlan; + private String source; + private String solution; + private String plan; + + public LessonInfoModel(WebSession webSession) { + AbstractLesson lesson = webSession.getCurrentLesson(); + //TODO make these first class citizens of the lesson itself; and stop passing the session all over +// this.source = (lesson.getSource(webSession)); +// this.plan = (lesson.getPage(webSession)); +// this.solution = (lesson.getSolution(webSession)); + + this.hasSource = !lesson.getSource(webSession).contains("Could not find the source file or source file does not exist"); + this.hasPlan = !lesson.getSource(webSession).contains("Could not find lesson plan"); + this.hasSolution = !lesson.getSolution(webSession).contains("Could not find the solution file or solution file does not exist"); + this.lessonTitle = lesson.getTitle(); + this.numberHints = lesson.getHintCount(webSession); + + if (this.numberHints == 1 && lesson.getHint(webSession,0).equals("Hint: There are no hints defined.")){ + this.numberHints = 0; + } + System.out.println("*** numHints = " + this.numberHints); + } + + // GETTERS + public String getLessonTitle() { + return lessonTitle; + } + + public int getNumberHints() { + return numberHints; + } + + public boolean isHasSource() { + return hasSource; + } + + public boolean isHasSolution() { + return hasSolution; + } + + public boolean isHasPlan() { + return hasPlan; + } + + public String getSource() { + return source; + } + + public String getSolution() { + return solution; + } + + public String getPlan() { + return plan; + } + +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonMenuItem.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonMenuItem.java index f45df8ead..9bf9364b8 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonMenuItem.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonMenuItem.java @@ -46,8 +46,8 @@ public class LessonMenuItem { private List children = new ArrayList(); private boolean complete; private String link; - private boolean showSource = true; - private boolean showHints = true; +// private boolean showSource = true; +// private boolean showHints = true; /** *

Getter for the field name.

@@ -157,40 +157,6 @@ public class LessonMenuItem { this.link = link; } - /** - *

isShowSource.

- * - * @return the showSource - */ - public boolean isShowSource() { - return showSource; - } - /** - *

Setter for the field showSource.

- * - * @param showSource the showSource to set - */ - public void setShowSource(boolean showSource) { - this.showSource = showSource; - } - - /** - *

isShowHints.

- * - * @return the showHints - */ - public boolean isShowHints() { - return showHints; - } - - /** - *

Setter for the field showHints.

- * - * @param showHints the showHints to set - */ - public void setShowHints(boolean showHints) { - this.showHints = showHints; - } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonInfoService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonInfoService.java new file mode 100644 index 000000000..d23e70254 --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonInfoService.java @@ -0,0 +1,42 @@ +package org.owasp.webgoat.service; + +import org.owasp.webgoat.lessons.AbstractLesson; +import org.owasp.webgoat.lessons.model.LessonInfoModel; +import org.owasp.webgoat.lessons.model.LessonMenuItem; +import org.owasp.webgoat.session.WebSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import javax.servlet.http.HttpSession; + +@Controller +public class LessonInfoService extends BaseService { + + private static final Logger logger = LoggerFactory.getLogger(LessonMenuService.class); + + @RequestMapping(value = "/lessoninfo.mvc", produces = "application/json") + public @ResponseBody + LessonInfoModel getLessonInfo(HttpSession session) { + WebSession webSession = getWebSession(session); + return new LessonInfoModel(webSession); + } + + @ExceptionHandler(Exception.class) + @ResponseBody + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) + public String handleException(Exception ex) { + return "An error occurred retrieving the LessonInfoModel:" + ex.getMessage(); + } + + protected LessonInfoModel getLessonInfoModel(WebSession webSession) { + return new LessonInfoModel(webSession); + } + + +} 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 d981716ad..3e6e11464 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 @@ -88,20 +88,6 @@ public class LessonMenuService extends BaseService { if (lesson.isCompleted(ws)) { lessonItem.setComplete(true); } - /* @TODO - do this in a more efficient way - if (lesson.isAuthorized(ws, role, WebSession.SHOWHINTS)) { - lessonItem.setShowHints(true); - } - - if (lesson.isAuthorized(ws, role, WebSession.SHOWSOURCE)) { - lessonItem.setShowSource(true); - } - */ - // special handling for challenge role - if (Category.CHALLENGE.equals(lesson.getCategory())) { - lessonItem.setShowHints(lesson.isAuthorized(ws, AbstractLesson.CHALLENGE_ROLE, WebSession.SHOWHINTS)); - lessonItem.setShowSource(lesson.isAuthorized(ws, AbstractLesson.CHALLENGE_ROLE, WebSession.SHOWHINTS)); - } categoryItem.addChild(lessonItem); // Does the lesson have stages diff --git a/webgoat-container/src/main/webapp/WEB-INF/pages/main_new.jsp b/webgoat-container/src/main/webapp/WEB-INF/pages/main_new.jsp index 87b0cf0d6..8946fa162 100644 --- a/webgoat-container/src/main/webapp/WEB-INF/pages/main_new.jsp +++ b/webgoat-container/src/main/webapp/WEB-INF/pages/main_new.jsp @@ -9,6 +9,11 @@ + + + + + diff --git a/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js b/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js index b45b1db1d..94214a97f 100644 --- a/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js +++ b/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js @@ -13,7 +13,9 @@ define(['jquery', 'goatApp/model/ParamModel', 'goatApp/support/GoatUtils', 'goatApp/view/UserAndInfoView', - 'goatApp/view/MenuButtonView' + 'goatApp/view/MenuButtonView', + 'goatApp/model/LessonInfoModel', + 'goatApp/view/TitleView' ], function($, _, @@ -30,7 +32,9 @@ define(['jquery', ParamModel, GoatUtils, UserAndInfoView, - MenuButtonView + MenuButtonView, + LessonInfoModel, + TitleView ) { 'use strict' @@ -42,13 +46,13 @@ define(['jquery', _.extend(Controller.prototype,Backbone.Events); this.start = function() { - this.listenTo(this.lessonContent,'contentLoaded',this.onContentLoaded); - //'static' elements of page/app + this.listenTo(this.lessonContent,'content:loaded',this.onContentLoaded); this.userAndInfoView = new UserAndInfoView(); this.menuButtonView = new MenuButtonView(); }; - //load View, which can pull data + this.loadLesson = function(scr,menu,stage) { + this.titleView = new TitleView(); this.helpsLoaded = {}; this.lessonContent.loadData({ 'screen': scr, @@ -59,64 +63,54 @@ define(['jquery', this.solutionView = {}; this.sourceView = {}; this.lessonHintView = {}; - this.screen = scr; //needed anymore? + this.screen = scr; this.menu = menu; - // - }; - this.onContentLoaded = function() { - this.helpControlsView = null; - this.lessonView.model = this.lessonContent; - this.lessonView.render(); - //load title view (initially hidden) << //TODO: currently handled via menu click but need to be able to handle via routed request - //plan view (initially hidden) - this.planView = new PlanView(); - this.listenToOnce(this.planView,'plan:loaded',this.areHelpsReady); - //solution view (initially hidden) - this.solutionView = new SolutionView(); - this.listenToOnce(this.solutionView,'solution:loaded',this.areHelpsReady); - //source (initially hidden) - this.sourceView = new SourceView(); - this.listenToOnce(this.sourceView,'source:loaded',this.areHelpsReady); - //load help controls view (contextul to what helps are available) - this.lessonHintView = new HintView(); - this.listenToOnce(this.lessonHintView,'hints:loaded',this.areHelpsReady); - // - this.cookieView = new CookieView(); - // parameter model & view - //TODO: instantiate model with values (not sure why was not working before) - var paramModel = new ParamModel({ + this.onInfoLoaded = function() { + this.helpControlsView = new HelpControlsView({ + hasPlan:this.lessonInfoModel.get('hasPlan'), + hasSolution:this.lessonInfoModel.get('hasSolution'), + hasSource:this.lessonInfoModel.get('hasSource'), + hasHints:(this.lessonInfoModel.get('numberHints') > 0), }); - paramModel.set('screenParam',this.lessonContent.get('screenParam')); - paramModel.set('menuParam',this.lessonContent.get('menuParam')); - paramModel.set('stageParam',this.lessonContent.get('stageParam')); - this.paramView = new ParamView({model:paramModel}); - $('.lesson-help').hide(); - this.trigger('menu:reload'); + this.listenTo(this.helpControlsView,'plan:show',this.hideShowHelps); + this.listenTo(this.helpControlsView,'solution:show',this.hideShowHelps); + this.listenTo(this.helpControlsView,'hints:show',this.onShowHints) + this.listenTo(this.helpControlsView,'source:show',this.hideShowHelps); + this.listenTo(this.helpControlsView,'lesson:restart',this.restartLesson); + + this.helpControlsView.render(); + + this.titleView.render(this.lessonInfoModel.get('lessonTitle')); }; - this.areHelpsReady = function (curHelp) { - //TODO: significantly refactor (remove) this once LessonInfoService can be used to support lazy loading - this.addCurHelpState(curHelp); - // check if all are ready - if (this.helpsLoaded['hints'] && this.helpsLoaded['plan'] && this.helpsLoaded['solution'] && this.helpsLoaded['source'] && !this.helpControlsView) { - - this.helpControlsView = new HelpControlsView({ - hasPlan:(this.planView.model.get('content') !== null), - hasSolution:(this.solutionView.model.get('content') !== null), - hasSource:(this.sourceView.model.get('content') !== null), - hasHints:(this.lessonHintView.collection.length > 0), - }); - this.helpControlsView.render(); - - this.listenTo(this.helpControlsView,'plan:show',this.hideShowHelps); - this.listenTo(this.helpControlsView,'solution:show',this.hideShowHelps); - this.listenTo(this.helpControlsView,'hints:show',this.onShowHints) - this.listenTo(this.helpControlsView,'source:show',this.hideShowHelps); - this.listenTo(this.helpControlsView,'lesson:restart',this.restartLesson); - } + this.onContentLoaded = function(loadHelps) { + this.lessonInfoModel = new LessonInfoModel(); + this.listenTo(this.lessonInfoModel,'info:loaded',this.onInfoLoaded); //TODO onInfoLoaded function to handle title view and helpview + + if (loadHelps) { + this.helpControlsView = null; + this.lessonView.model = this.lessonContent; + this.lessonView.render(); + //load title view (initially hidden) << //TODO: currently handled via menu click but need to be able to handle via routed request + this.planView = new PlanView(); + this.solutionView = new SolutionView(); + this.sourceView = new SourceView(); + this.lessonHintView = new HintView(); + this.cookieView = new CookieView(); + // parameter model & view + //TODO: instantiate model with values (not sure why was not working before) + var paramModel = new ParamModel({}); + paramModel.set('screenParam',this.lessonContent.get('screenParam')); + paramModel.set('menuParam',this.lessonContent.get('menuParam')); + paramModel.set('stageParam',this.lessonContent.get('stageParam')); + this.paramView = new ParamView({model:paramModel}); + + $('.lesson-help').hide(); + } + this.trigger('menu:reload'); }; this.addCurHelpState = function (curHelp) { diff --git a/webgoat-container/src/main/webapp/js/goatApp/controller/MenuController.js b/webgoat-container/src/main/webapp/js/goatApp/controller/MenuController.js index f6da24e3e..d9df4e021 100644 --- a/webgoat-container/src/main/webapp/js/goatApp/controller/MenuController.js +++ b/webgoat-container/src/main/webapp/js/goatApp/controller/MenuController.js @@ -11,22 +11,15 @@ define(['jquery', _.extend(Controller.prototype,Backbone.Events); options = options || {}; this.menuView = options.menuView; - this.titleView = options.titleView; this.initMenu = function() { - this.listenTo(this.menuView,'lesson:click',this.renderTitle); + //this.listenTo(this.menuView,'lesson:click',this.renderTitle); } this.updateMenu = function(){ this.menuView.updateMenu(); - }, - - //TODO: move title rendering into lessonContent/View pipeline once data can support it - this.renderTitle = function(title) { - this.titleView.render(title); } - }; return Controller; diff --git a/webgoat-container/src/main/webapp/js/goatApp/model/LessonContentModel.js b/webgoat-container/src/main/webapp/js/goatApp/model/LessonContentModel.js index f0f0f1dbe..e588447b2 100644 --- a/webgoat-container/src/main/webapp/js/goatApp/model/LessonContentModel.js +++ b/webgoat-container/src/main/webapp/js/goatApp/model/LessonContentModel.js @@ -29,10 +29,15 @@ define(['jquery', self.setContent(data); }); }, - setContent: function(content) { + + setContent: function(content, loadHelps) { + if (typeof loadHelps === 'undefined') { + loadHelps = true; + } this.set('content',content); - this.trigger('contentLoaded'); + this.trigger('content:loaded',this,loadHelps); }, + fetch: function (options) { options = options || {}; return Backbone.Model.prototype.fetch.call(this, _.extend({ dataType: "html"}, options)); diff --git a/webgoat-container/src/main/webapp/js/goatApp/model/LessonInfoModel.js b/webgoat-container/src/main/webapp/js/goatApp/model/LessonInfoModel.js new file mode 100644 index 000000000..edbf3daff --- /dev/null +++ b/webgoat-container/src/main/webapp/js/goatApp/model/LessonInfoModel.js @@ -0,0 +1,21 @@ +define(['jquery', + 'underscore', + 'backbone'], + function($, + _, + Backbone){ + + return Backbone.Model.extend({ + url:'service/lessoninfo.mvc', + + initialize: function (options) { + this.fetch().then(this.infoLoaded.bind(this)); + }, + + infoLoaded: function(data) { + console.log (data); + this.trigger('info:loaded',this,data); + } + + }); +}); \ No newline at end of file diff --git a/webgoat-container/src/main/webapp/js/goatApp/view/GoatRouter.js b/webgoat-container/src/main/webapp/js/goatApp/view/GoatRouter.js index 8e42fd0eb..c31f35785 100644 --- a/webgoat-container/src/main/webapp/js/goatApp/view/GoatRouter.js +++ b/webgoat-container/src/main/webapp/js/goatApp/view/GoatRouter.js @@ -4,19 +4,22 @@ define(['jquery', 'goatApp/controller/LessonController', 'goatApp/controller/MenuController', 'goatApp/view/LessonContentView', - 'goatApp/view/MenuView', - 'goatApp/view/TitleView' -], function ($,_,Backbone,LessonController,MenuController,LessonContentView,MenuView,TitleView) { - + 'goatApp/view/MenuView' + ], function ($, + _, + Backbone, + LessonController, + MenuController, + LessonContentView, + MenuView) { + var lessonView = new LessonContentView(); var menuView = new MenuView(); - var titleView = new TitleView(); var GoatAppRouter = Backbone.Router.extend({ routes: { - //#.... 'welcome':'welcomeRoute', - 'attack/:scr/:menu(/:stage)':'attackRoute' // + 'attack/:scr/:menu(/:stage)':'attackRoute', }, lessonController: new LessonController({ @@ -24,8 +27,7 @@ define(['jquery', }), menuController: new MenuController({ - menuView:menuView, - titleView:titleView + menuView:menuView }), init:function() { @@ -39,7 +41,7 @@ define(['jquery', //update menu }); goatRouter.on('route:welcomeRoute', function() { - alert('welcome route'); + this.lessonController.loadWelcome(); }); Backbone.history.start(); diff --git a/webgoat-container/src/main/webapp/js/goatApp/view/TitleView.js b/webgoat-container/src/main/webapp/js/goatApp/view/TitleView.js index 4b3126ec7..5df9353f0 100644 --- a/webgoat-container/src/main/webapp/js/goatApp/view/TitleView.js +++ b/webgoat-container/src/main/webapp/js/goatApp/view/TitleView.js @@ -7,7 +7,6 @@ function($,_,Backbone) { render:function(title) { var lessonTitleEl = $('

',{id:'lesson-title',text:title}); this.$el.html(lessonTitleEl); - //this.$el.append(lessonTitleEl); } }); }); \ No newline at end of file diff --git a/webgoat-container/src/main/webapp/js/libs/text.js b/webgoat-container/src/main/webapp/js/libs/text.js new file mode 100644 index 000000000..4c311edce --- /dev/null +++ b/webgoat-container/src/main/webapp/js/libs/text.js @@ -0,0 +1,391 @@ +/** + * @license RequireJS text 2.0.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/requirejs/text for details + */ +/*jslint regexp: true */ +/*global require, XMLHttpRequest, ActiveXObject, + define, window, process, Packages, + java, location, Components, FileUtils */ + +define(['module'], function (module) { + 'use strict'; + + var text, fs, Cc, Ci, xpcIsWindows, + progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], + xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, + bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, + hasLocation = typeof location !== 'undefined' && location.href, + defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), + defaultHostName = hasLocation && location.hostname, + defaultPort = hasLocation && (location.port || undefined), + buildMap = {}, + masterConfig = (module.config && module.config()) || {}; + + text = { + version: '2.0.14', + + strip: function (content) { + //Strips declarations so that external SVG and XML + //documents can be added to a document without worry. Also, if the string + //is an HTML document, only the part inside the body tag is returned. + if (content) { + content = content.replace(xmlRegExp, ""); + var matches = content.match(bodyRegExp); + if (matches) { + content = matches[1]; + } + } else { + content = ""; + } + return content; + }, + + jsEscape: function (content) { + return content.replace(/(['\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r") + .replace(/[\u2028]/g, "\\u2028") + .replace(/[\u2029]/g, "\\u2029"); + }, + + createXhr: masterConfig.createXhr || function () { + //Would love to dump the ActiveX crap in here. Need IE 6 to die first. + var xhr, i, progId; + if (typeof XMLHttpRequest !== "undefined") { + return new XMLHttpRequest(); + } else if (typeof ActiveXObject !== "undefined") { + for (i = 0; i < 3; i += 1) { + progId = progIds[i]; + try { + xhr = new ActiveXObject(progId); + } catch (e) {} + + if (xhr) { + progIds = [progId]; // so faster next time + break; + } + } + } + + return xhr; + }, + + /** + * Parses a resource name into its component parts. Resource names + * look like: module/name.ext!strip, where the !strip part is + * optional. + * @param {String} name the resource name + * @returns {Object} with properties "moduleName", "ext" and "strip" + * where strip is a boolean. + */ + parseName: function (name) { + var modName, ext, temp, + strip = false, + index = name.lastIndexOf("."), + isRelative = name.indexOf('./') === 0 || + name.indexOf('../') === 0; + + if (index !== -1 && (!isRelative || index > 1)) { + modName = name.substring(0, index); + ext = name.substring(index + 1); + } else { + modName = name; + } + + temp = ext || modName; + index = temp.indexOf("!"); + if (index !== -1) { + //Pull off the strip arg. + strip = temp.substring(index + 1) === "strip"; + temp = temp.substring(0, index); + if (ext) { + ext = temp; + } else { + modName = temp; + } + } + + return { + moduleName: modName, + ext: ext, + strip: strip + }; + }, + + xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, + + /** + * Is an URL on another domain. Only works for browser use, returns + * false in non-browser environments. Only used to know if an + * optimized .js version of a text resource should be loaded + * instead. + * @param {String} url + * @returns Boolean + */ + useXhr: function (url, protocol, hostname, port) { + var uProtocol, uHostName, uPort, + match = text.xdRegExp.exec(url); + if (!match) { + return true; + } + uProtocol = match[2]; + uHostName = match[3]; + + uHostName = uHostName.split(':'); + uPort = uHostName[1]; + uHostName = uHostName[0]; + + return (!uProtocol || uProtocol === protocol) && + (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && + ((!uPort && !uHostName) || uPort === port); + }, + + finishLoad: function (name, strip, content, onLoad) { + content = strip ? text.strip(content) : content; + if (masterConfig.isBuild) { + buildMap[name] = content; + } + onLoad(content); + }, + + load: function (name, req, onLoad, config) { + //Name has format: some.module.filext!strip + //The strip part is optional. + //if strip is present, then that means only get the string contents + //inside a body tag in an HTML string. For XML/SVG content it means + //removing the declarations so the content can be inserted + //into the current doc without problems. + + // Do not bother with the work if a build and text will + // not be inlined. + if (config && config.isBuild && !config.inlineText) { + onLoad(); + return; + } + + masterConfig.isBuild = config && config.isBuild; + + var parsed = text.parseName(name), + nonStripName = parsed.moduleName + + (parsed.ext ? '.' + parsed.ext : ''), + url = req.toUrl(nonStripName), + useXhr = (masterConfig.useXhr) || + text.useXhr; + + // Do not load if it is an empty: url + if (url.indexOf('empty:') === 0) { + onLoad(); + return; + } + + //Load the text. Use XHR if possible and in a browser. + if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { + text.get(url, function (content) { + text.finishLoad(name, parsed.strip, content, onLoad); + }, function (err) { + if (onLoad.error) { + onLoad.error(err); + } + }); + } else { + //Need to fetch the resource across domains. Assume + //the resource has been optimized into a JS module. Fetch + //by the module name + extension, but do not include the + //!strip part to avoid file system issues. + req([nonStripName], function (content) { + text.finishLoad(parsed.moduleName + '.' + parsed.ext, + parsed.strip, content, onLoad); + }); + } + }, + + write: function (pluginName, moduleName, write, config) { + if (buildMap.hasOwnProperty(moduleName)) { + var content = text.jsEscape(buildMap[moduleName]); + write.asModule(pluginName + "!" + moduleName, + "define(function () { return '" + + content + + "';});\n"); + } + }, + + writeFile: function (pluginName, moduleName, req, write, config) { + var parsed = text.parseName(moduleName), + extPart = parsed.ext ? '.' + parsed.ext : '', + nonStripName = parsed.moduleName + extPart, + //Use a '.js' file name so that it indicates it is a + //script that can be loaded across domains. + fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; + + //Leverage own load() method to load plugin value, but only + //write out values that do not have the strip argument, + //to avoid any potential issues with ! in file names. + text.load(nonStripName, req, function (value) { + //Use own write() method to construct full module value. + //But need to create shell that translates writeFile's + //write() to the right interface. + var textWrite = function (contents) { + return write(fileName, contents); + }; + textWrite.asModule = function (moduleName, contents) { + return write.asModule(moduleName, fileName, contents); + }; + + text.write(pluginName, nonStripName, textWrite, config); + }, config); + } + }; + + if (masterConfig.env === 'node' || (!masterConfig.env && + typeof process !== "undefined" && + process.versions && + !!process.versions.node && + !process.versions['node-webkit'] && + !process.versions['atom-shell'])) { + //Using special require.nodeRequire, something added by r.js. + fs = require.nodeRequire('fs'); + + text.get = function (url, callback, errback) { + try { + var file = fs.readFileSync(url, 'utf8'); + //Remove BOM (Byte Mark Order) from utf8 files if it is there. + if (file[0] === '\uFEFF') { + file = file.substring(1); + } + callback(file); + } catch (e) { + if (errback) { + errback(e); + } + } + }; + } else if (masterConfig.env === 'xhr' || (!masterConfig.env && + text.createXhr())) { + text.get = function (url, callback, errback, headers) { + var xhr = text.createXhr(), header; + xhr.open('GET', url, true); + + //Allow plugins direct access to xhr headers + if (headers) { + for (header in headers) { + if (headers.hasOwnProperty(header)) { + xhr.setRequestHeader(header.toLowerCase(), headers[header]); + } + } + } + + //Allow overrides specified in config + if (masterConfig.onXhr) { + masterConfig.onXhr(xhr, url); + } + + xhr.onreadystatechange = function (evt) { + var status, err; + //Do not explicitly handle errors, those should be + //visible via console output in the browser. + if (xhr.readyState === 4) { + status = xhr.status || 0; + if (status > 399 && status < 600) { + //An http 4xx or 5xx error. Signal an error. + err = new Error(url + ' HTTP status: ' + status); + err.xhr = xhr; + if (errback) { + errback(err); + } + } else { + callback(xhr.responseText); + } + + if (masterConfig.onXhrComplete) { + masterConfig.onXhrComplete(xhr, url); + } + } + }; + xhr.send(null); + }; + } else if (masterConfig.env === 'rhino' || (!masterConfig.env && + typeof Packages !== 'undefined' && typeof java !== 'undefined')) { + //Why Java, why is this so awkward? + text.get = function (url, callback) { + var stringBuffer, line, + encoding = "utf-8", + file = new java.io.File(url), + lineSeparator = java.lang.System.getProperty("line.separator"), + input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), + content = ''; + try { + stringBuffer = new java.lang.StringBuffer(); + line = input.readLine(); + + // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 + // http://www.unicode.org/faq/utf_bom.html + + // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 + if (line && line.length() && line.charAt(0) === 0xfeff) { + // Eat the BOM, since we've already found the encoding on this file, + // and we plan to concatenating this buffer with others; the BOM should + // only appear at the top of a file. + line = line.substring(1); + } + + if (line !== null) { + stringBuffer.append(line); + } + + while ((line = input.readLine()) !== null) { + stringBuffer.append(lineSeparator); + stringBuffer.append(line); + } + //Make sure we return a JavaScript string and not a Java string. + content = String(stringBuffer.toString()); //String + } finally { + input.close(); + } + callback(content); + }; + } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && + typeof Components !== 'undefined' && Components.classes && + Components.interfaces)) { + //Avert your gaze! + Cc = Components.classes; + Ci = Components.interfaces; + Components.utils['import']('resource://gre/modules/FileUtils.jsm'); + xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); + + text.get = function (url, callback) { + var inStream, convertStream, fileObj, + readData = {}; + + if (xpcIsWindows) { + url = url.replace(/\//g, '\\'); + } + + fileObj = new FileUtils.File(url); + + //XPCOM, you so crazy + try { + inStream = Cc['@mozilla.org/network/file-input-stream;1'] + .createInstance(Ci.nsIFileInputStream); + inStream.init(fileObj, 1, 0, false); + + convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] + .createInstance(Ci.nsIConverterInputStream); + convertStream.init(inStream, "utf-8", inStream.available(), + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + + convertStream.readString(inStream.available(), readData); + convertStream.close(); + inStream.close(); + callback(readData.value); + } catch (e) { + throw new Error((fileObj && fileObj.path || '') + ': ' + e); + } + }; + } + return text; +}); diff --git a/webgoat-container/src/main/webapp/js/main.js b/webgoat-container/src/main/webapp/js/main.js index c13226939..64f4ea68c 100644 --- a/webgoat-container/src/main/webapp/js/main.js +++ b/webgoat-container/src/main/webapp/js/main.js @@ -15,7 +15,8 @@ require.config({ paths: { jquery: 'libs/jquery-1.10.2.min', underscore: 'libs/underscore-min', - backbone: 'libs/backbone-min' + backbone: 'libs/backbone-min', + templates: 'goatApp/templates' } , shim: { From dc57827cfcef9d37472acefffc4fc1383ed4273b Mon Sep 17 00:00:00 2001 From: Jason White Date: Mon, 21 Sep 2015 21:24:10 -0400 Subject: [PATCH 2/2] #97, Hint controls for CHALLENGE Category lessons --- .../owasp/webgoat/lessons/model/LessonInfoModel.java | 10 +++------- .../java/org/owasp/webgoat/service/HintService.java | 6 ++++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonInfoModel.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonInfoModel.java index ccafa2cea..e76d3e141 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonInfoModel.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/model/LessonInfoModel.java @@ -1,6 +1,7 @@ package org.owasp.webgoat.lessons.model; import org.owasp.webgoat.lessons.AbstractLesson; +import org.owasp.webgoat.lessons.Category; import org.owasp.webgoat.session.WebSession; /** @@ -20,20 +21,15 @@ public class LessonInfoModel { public LessonInfoModel(WebSession webSession) { AbstractLesson lesson = webSession.getCurrentLesson(); //TODO make these first class citizens of the lesson itself; and stop passing the session all over -// this.source = (lesson.getSource(webSession)); -// this.plan = (lesson.getPage(webSession)); -// this.solution = (lesson.getSolution(webSession)); - this.hasSource = !lesson.getSource(webSession).contains("Could not find the source file or source file does not exist"); this.hasPlan = !lesson.getSource(webSession).contains("Could not find lesson plan"); this.hasSolution = !lesson.getSolution(webSession).contains("Could not find the solution file or solution file does not exist"); this.lessonTitle = lesson.getTitle(); + this.numberHints = lesson.getHintCount(webSession); - - if (this.numberHints == 1 && lesson.getHint(webSession,0).equals("Hint: There are no hints defined.")){ + if (lesson.getCategory().equals(Category.CHALLENGE) || this.numberHints < 1 || lesson.getHint(webSession,0).equals("Hint: There are no hints defined.")) { this.numberHints = 0; } - System.out.println("*** numHints = " + this.numberHints); } // GETTERS diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/HintService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/HintService.java index 76793ad65..20cd36691 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/HintService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/HintService.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpSession; import org.owasp.webgoat.lessons.AbstractLesson; +import org.owasp.webgoat.lessons.Category; import org.owasp.webgoat.lessons.model.Hint; import org.owasp.webgoat.session.WebSession; import org.springframework.stereotype.Controller; @@ -40,11 +41,12 @@ public class HintService extends BaseService { if (l == null) { return listHints; } - List hints; - hints = l.getHintsPublic(ws); + List hints = (l.getCategory().equals(Category.CHALLENGE)) ? null : l.getHintsPublic(ws); + if (hints == null) { return listHints; } + int idx = 0; for (String h : hints) { Hint hint = new Hint();