From b4cc27c761c83e0620b08d096466b9505949b478 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 27 Mar 2016 17:46:06 +0200 Subject: [PATCH 1/3] Lesson completed message is now within js which makes it possible to show the Congratulation message after the CSRF link has been clicked. Same as marking the lesson complete green checkbox --- .../owasp/webgoat/lessons/LessonAdapter.java | 2 +- .../webgoat/lessons/RandomLessonAdapter.java | 7 ++- .../owasp/webgoat/service/DummyService.java | 57 ------------------- .../service/LessonCompletedService.java | 54 ++++++++++++++++++ .../main/webapp/WEB-INF/pages/main_new.jsp | 1 + .../js/goatApp/controller/LessonController.js | 12 +++- 6 files changed, 70 insertions(+), 63 deletions(-) delete mode 100644 webgoat-container/src/main/java/org/owasp/webgoat/service/DummyService.java create mode 100644 webgoat-container/src/main/java/org/owasp/webgoat/service/LessonCompletedService.java diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/LessonAdapter.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/LessonAdapter.java index 2f3bf9d11..cf64c520b 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/LessonAdapter.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/LessonAdapter.java @@ -250,7 +250,7 @@ public abstract class LessonAdapter extends AbstractLesson { protected Element makeSuccess(WebSession s) { getLessonTracker(s).setCompleted(true); - s.setMessage(getLabelManager().get("LessonCompleted")); + //s.setMessage(getLabelManager().get("LessonCompleted")); return (null); } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/RandomLessonAdapter.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/RandomLessonAdapter.java index 97f222fb2..e4a815f1c 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/RandomLessonAdapter.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/RandomLessonAdapter.java @@ -1,14 +1,15 @@ package org.owasp.webgoat.lessons; -import java.sql.Connection; -import java.sql.SQLException; import org.owasp.webgoat.session.CreateDB; import org.owasp.webgoat.session.DatabaseUtilities; import org.owasp.webgoat.session.LessonTracker; import org.owasp.webgoat.session.RandomLessonTracker; import org.owasp.webgoat.session.WebSession; +import java.sql.Connection; +import java.sql.SQLException; + /** *

Abstract RandomLessonAdapter class.

@@ -75,7 +76,7 @@ public abstract class RandomLessonAdapter extends LessonAdapter lt.setStageComplete(stage, true); if (lt.getCompleted()) { - s.setMessage("Congratulations, you have completed this lab"); + //s.setMessage("Congratulations, you have completed this lab"); } else { diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/DummyService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/DummyService.java deleted file mode 100644 index 61dfaba8a..000000000 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/DummyService.java +++ /dev/null @@ -1,57 +0,0 @@ -/*************************************************************************************************** - * - * - * 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. - * - */ -package org.owasp.webgoat.service; - -import java.util.ArrayList; -import java.util.List; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -/** - *

DummyService class.

- * - * @author rlawson - * @version $Id: $Id - */ -@Controller -public class DummyService extends BaseService{ - - /** - *

firstNames.

- * - * @return a {@link java.util.List} object. - */ - @RequestMapping(value = "/first.mvc", produces = "application/json") - public @ResponseBody - List firstNames() { - List test = new ArrayList(); - test.add("one"); - test.add("two)"); - return test; - } -} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonCompletedService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonCompletedService.java new file mode 100644 index 000000000..668cddd5e --- /dev/null +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonCompletedService.java @@ -0,0 +1,54 @@ +package org.owasp.webgoat.service; + +import com.google.common.collect.Maps; +import org.owasp.webgoat.lessons.AbstractLesson; +import org.owasp.webgoat.lessons.RandomLessonAdapter; +import org.owasp.webgoat.lessons.model.LessonInfoModel; +import org.owasp.webgoat.session.WebSession; +import org.owasp.webgoat.util.LabelManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpSession; +import java.util.Map; + +@Controller +/** + *

LessonCompletedService class.

+ * + * @author webgoat + */ +public class LessonCompletedService extends BaseService { + + private static final Logger logger = LoggerFactory.getLogger(LessonMenuService.class); + private LabelManager labelManager; + + @Autowired + public LessonCompletedService(final LabelManager labelManager) { + this.labelManager = labelManager; + } + + /** + *

getLessonCompletedService.

+ * + * @param session a {@link HttpSession} object. + * @return a {@link LessonInfoModel} object. + */ + @RequestMapping(value = "/lessoncompleted.mvc", produces = "application/json") + @ResponseBody + public Map getLessonInfo(HttpSession session) { + WebSession webSession = getWebSession(session); + AbstractLesson lesson = webSession.getCurrentLesson(); + boolean lessonCompleted = lesson.isCompleted(webSession); + String successMessage = lesson instanceof RandomLessonAdapter ? "Congratulations, you have completed this lab" : labelManager + .get("LessonCompleted"); + Map json = Maps.newHashMap(); + json.put("lessonCompleted", lessonCompleted); + json.put("successMessage", successMessage); + return json; + } +} 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 990737a3e..8065739c3 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 @@ -121,6 +121,7 @@
+
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 43964bbe4..2d664d63c 100644 --- a/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js +++ b/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js @@ -15,7 +15,9 @@ define(['jquery', 'goatApp/view/UserAndInfoView', 'goatApp/view/MenuButtonView', 'goatApp/model/LessonInfoModel', - 'goatApp/view/TitleView' + 'goatApp/view/TitleView', + 'goatApp/model/LessonCompletedModel', + 'goatApp/view/LessonCompletedView' ], function($, _, @@ -34,13 +36,18 @@ define(['jquery', UserAndInfoView, MenuButtonView, LessonInfoModel, - TitleView + TitleView, + LessonCompletedModel, + LessonCompletedView + ) { 'use strict' var Controller = function(options) { this.lessonContent = new LessonContentModel(); + this.lessonCompletedModel = new LessonCompletedModel(); + this.lessonCompletedView = new LessonCompletedView(this.lessonCompletedModel); this.lessonView = options.lessonView; _.extend(Controller.prototype,Backbone.Events); @@ -127,6 +134,7 @@ define(['jquery', $('.lesson-help').hide(); } this.trigger('menu:reload'); + this.lessonCompletedModel.completed(); }; this.addCurHelpState = function (curHelp) { From 60192db92166085414986c41451aeacd7c2a1322 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 28 Mar 2016 08:37:18 +0200 Subject: [PATCH 2/3] Renamed everything to 'progress' instead of 'completed' --- ...ervice.java => LessonProgressService.java} | 10 +++---- .../main/webapp/WEB-INF/pages/main_new.jsp | 2 +- .../js/goatApp/controller/LessonController.js | 14 +++++----- .../js/goatApp/model/LessonProgressModel.js | 13 ++++++++++ .../js/goatApp/view/LessonProgressView.js | 26 +++++++++++++++++++ 5 files changed, 52 insertions(+), 13 deletions(-) rename webgoat-container/src/main/java/org/owasp/webgoat/service/{LessonCompletedService.java => LessonProgressService.java} (84%) create mode 100644 webgoat-container/src/main/webapp/js/goatApp/model/LessonProgressModel.js create mode 100644 webgoat-container/src/main/webapp/js/goatApp/view/LessonProgressView.js diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonCompletedService.java b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonProgressService.java similarity index 84% rename from webgoat-container/src/main/java/org/owasp/webgoat/service/LessonCompletedService.java rename to webgoat-container/src/main/java/org/owasp/webgoat/service/LessonProgressService.java index 668cddd5e..23701894d 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonCompletedService.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/service/LessonProgressService.java @@ -18,27 +18,27 @@ import java.util.Map; @Controller /** - *

LessonCompletedService class.

+ *

LessonProgressService class.

* * @author webgoat */ -public class LessonCompletedService extends BaseService { +public class LessonProgressService extends BaseService { private static final Logger logger = LoggerFactory.getLogger(LessonMenuService.class); private LabelManager labelManager; @Autowired - public LessonCompletedService(final LabelManager labelManager) { + public LessonProgressService(final LabelManager labelManager) { this.labelManager = labelManager; } /** - *

getLessonCompletedService.

+ *

LessonProgressService.

* * @param session a {@link HttpSession} object. * @return a {@link LessonInfoModel} object. */ - @RequestMapping(value = "/lessoncompleted.mvc", produces = "application/json") + @RequestMapping(value = "/lessonprogress.mvc", produces = "application/json") @ResponseBody public Map getLessonInfo(HttpSession session) { WebSession webSession = getWebSession(session); 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 8065739c3..356706e1b 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 @@ -121,7 +121,7 @@
-
+
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 2d664d63c..06440f32a 100644 --- a/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js +++ b/webgoat-container/src/main/webapp/js/goatApp/controller/LessonController.js @@ -16,8 +16,8 @@ define(['jquery', 'goatApp/view/MenuButtonView', 'goatApp/model/LessonInfoModel', 'goatApp/view/TitleView', - 'goatApp/model/LessonCompletedModel', - 'goatApp/view/LessonCompletedView' + 'goatApp/model/LessonProgressModel', + 'goatApp/view/LessonProgressView' ], function($, _, @@ -37,8 +37,8 @@ define(['jquery', MenuButtonView, LessonInfoModel, TitleView, - LessonCompletedModel, - LessonCompletedView + LessonProgressModel, + LessonProgressView ) { 'use strict' @@ -46,8 +46,8 @@ define(['jquery', var Controller = function(options) { this.lessonContent = new LessonContentModel(); - this.lessonCompletedModel = new LessonCompletedModel(); - this.lessonCompletedView = new LessonCompletedView(this.lessonCompletedModel); + this.lessonProgressModel = new LessonProgressModel(); + this.lessonProgressView = new LessonProgressView(this.lessonProgressModel); this.lessonView = options.lessonView; _.extend(Controller.prototype,Backbone.Events); @@ -134,7 +134,7 @@ define(['jquery', $('.lesson-help').hide(); } this.trigger('menu:reload'); - this.lessonCompletedModel.completed(); + this.lessonProgressModel.completed(); }; this.addCurHelpState = function (curHelp) { diff --git a/webgoat-container/src/main/webapp/js/goatApp/model/LessonProgressModel.js b/webgoat-container/src/main/webapp/js/goatApp/model/LessonProgressModel.js new file mode 100644 index 000000000..2582583ac --- /dev/null +++ b/webgoat-container/src/main/webapp/js/goatApp/model/LessonProgressModel.js @@ -0,0 +1,13 @@ +define(['jquery', + 'underscore', + 'backbone'], + function ($, + _, + Backbone) { + return Backbone.Model.extend({ + url: 'service/lessonprogress.mvc', + completed: function () { + this.fetch(); + } + }); + }); \ No newline at end of file diff --git a/webgoat-container/src/main/webapp/js/goatApp/view/LessonProgressView.js b/webgoat-container/src/main/webapp/js/goatApp/view/LessonProgressView.js new file mode 100644 index 000000000..0c3e892b1 --- /dev/null +++ b/webgoat-container/src/main/webapp/js/goatApp/view/LessonProgressView.js @@ -0,0 +1,26 @@ +define(['jquery', + 'underscore', + 'backbone', + 'goatApp/model/LessonProgressModel'], + function ($, + _, + Backbone, + LessonProgressModel) { + return Backbone.View.extend({ + el: '#lesson-progress', + initialize: function (lessonProgressModel) { + this.model = lessonProgressModel; + + if (this.model) { + this.listenTo(this.model, 'change', this.render); + } + }, + render: function () { + if (this.model.get("lessonCompleted")) { + this.$el.html(this.model.get('successMessage')); + } else { + this.$el.html(""); + } + } + }); + }); \ No newline at end of file From 015dcbde38a520474f0ed9e73e7ef3255aa75be6 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 28 Mar 2016 08:38:25 +0200 Subject: [PATCH 3/3] Added image for Github developers documentation about lesson progress implementation. --- .../documentation/csrf-lesson.gliffy | 1 + .../documentation/csrf-lessons.png | Bin 0 -> 16587 bytes 2 files changed, 1 insertion(+) create mode 100644 webgoat-container/documentation/csrf-lesson.gliffy create mode 100644 webgoat-container/documentation/csrf-lessons.png diff --git a/webgoat-container/documentation/csrf-lesson.gliffy b/webgoat-container/documentation/csrf-lesson.gliffy new file mode 100644 index 000000000..364f3802f --- /dev/null +++ b/webgoat-container/documentation/csrf-lesson.gliffy @@ -0,0 +1 @@ +{"contentType":"application/gliffy+json","version":"1.1","metadata":{"title":"untitled","revision":0,"exportBorder":false},"embeddedResources":{"index":0,"resources":[]},"stage":{"objects":[{"x":201,"y":233,"rotation":0,"id":22,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":22,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[301.0066444449358,0]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":24,"uid":null,"width":118,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

images gets reloaded

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":499,"y":200,"rotation":0,"id":18,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":18,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0],[-304.00164473239283,-1.1368683772161603e-13]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":19,"uid":null,"width":132,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

Message gets displayed

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":204,"y":174,"rotation":0,"id":15,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":15,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-4.000000000000028,-0.8629150101523919],[296,-0.8629150101523919]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":16,"uid":null,"width":112,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

User clicks message

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":499,"y":137,"rotation":0,"id":11,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":11,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[1,3],[-299,3]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":14,"uid":null,"width":133,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

Messages are displayed

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":205,"y":119,"rotation":0,"id":7,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":7,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[-5,-2.137084989847608],[295,-2.137084989847608]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":10,"uid":null,"width":117,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

Users types message

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":40,"y":90,"rotation":0,"id":0,"uid":"com.gliffy.shape.basic.basic_v1.default.square","width":160,"height":160,"lockAspectRatio":true,"lockShape":false,"order":0,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":5,"uid":null,"width":156,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

CSRF-Lesson

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":500,"y":90,"rotation":0,"id":4,"uid":"com.gliffy.shape.basic.basic_v1.default.square","width":160,"height":160,"lockAspectRatio":true,"lockShape":false,"order":1,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":6,"uid":null,"width":156,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

WebGoat-Server

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]}],"background":"#FFFFFF","width":660,"height":250,"maxWidth":5000,"maxHeight":5000,"nodeIndex":25,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":true,"drawingGuidesOn":true,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"shapeStyles":{"com.gliffy.shape.basic.basic_v1.default":{"fill":"#FFFFFF","stroke":"#333333","strokeWidth":2}},"lineStyles":{"global":{"endArrow":1}},"textStyles":{},"themeData":null}} \ No newline at end of file diff --git a/webgoat-container/documentation/csrf-lessons.png b/webgoat-container/documentation/csrf-lessons.png new file mode 100644 index 0000000000000000000000000000000000000000..6360d337dc19ffd512b01a0469f7fd96a9c3853b GIT binary patch literal 16587 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 literal 0 HcmV?d00001