OWASP Juice Shop

Achieving sustainability for open source projects

https://www.owasp.org/index.php/OWASP_Juice_Shop_Project

TechUp Presentation by Björn Kimminich / @bkimminich

Chapter One

WAT?

OWASP Juice Shop...

...is an intentionally insecure webapp written in Node.js, Express and AngularJS. It contains 43+ hacking challenges of varying difficulty tracked on a score board.

OWASP

The Open Web Application Security Project is a 501(c)(3) worldwide not-for-profit charitable organization focused on improving the security of software. Our mission is to make software security visible, so that individuals and organizations are able to make informed decisions.

Why the name "Juice Shop"?!?

Translating "dump" or "useless outfit" into German yields "Saftladen" which can be reverse-translated word by word into "juice shop". Hence the project name.
That the initials "JS" match with those of "Javascript" was purely coincidental!

Fully functional* online shop

*with the minor exception that you cannot actually buy anything

Modern Web-Architecture

Javascript all the way from UI to REST API

OWASP Juice Shop CTF...

...is a commandline tool written in Node.js and published on NPM.js. It helps you to set up and host CTF events on the CTFd platform with the Juice Shop challenges.

CTF

A Capture the Flag is a special kind of information security competition. Jeopardy-style CTFs have a couple of questions (tasks) in range of categories. For example, Web, Forensic, Crypto, Binary or something else. Teams can gain some points for every solved task.

Frictionless CTF-Events

All participants use individual Juice Shop instances anywhere, sharing only the flag code-ctfKey and a central score server.

Setup Wizard

Run juice-shop-ctf on the command line and let a wizard create SQL statements to apply to CTFd's database

Chapter One+

WSIC?

🤷

(Why should I care?)

Usage Stats

Since its move into the OWASP project portfolio in September 2016, Juice Shop is increasingly used, cloned, forked & contributed to:

Juice Shop

CTF Extension

Stake Holders of Juice Shop

OWASP Juice Shop is supposed to reliably fulfill the demands of:

  • Security Trainers 📛
  • University Lecturers 🏫
  • Security Tool Vendors 💸
  • Corporate Security Teams 👔
  • Hackers looking for a challenge 💪
  • Web Application Security Novices 🐤
  • ...

And last but not least...

GitHub is (part of) your résumé

If you publish 🍝-code as open source, why should anyone believe you'll do any better in your daily work?

Personal note: Today recruiters have no clue what to make of GitHub profiles, but you can assume they will learn one day! This will hopefully be the same happy day they stop offering me e.g. Python Lead Programmer (m/f) jobs, although I have not committed a single line of Python code on GitHub. The same goes for Ruby on Rails. And C#.

Chapter Two

Open Source Antipatterns

Barren README

An empty or lackluster front-page radiates the impression that nobody takes serious care of the project.

Bad Example

Worse Example

Worst Example

Good Example

Even Better (🤔) Example

Pile of Issues

Open issues pile up in the ticketing system, most of them unanswered and unattended.

A pile of unattended issues

(🤔)

Really a 💩-pile of unattended issues

Counter: Use  labels  properly

Agile Counter: Kanban Board

https://waffle.io/bkimminich/juice-shop

PR Disaster

Pull Requests are routinely ignored or flogged to death.

Ignored Pull Requests

(🤔)

Example for a PR flogged to death*

https://github.com/zaproxy/zaproxy/pull/2837
*Disclaimer: This example should by no means imply that the ZAP team is routinely flogging PRs to death! On the contrary, they offer a helping hand to even those who totally ignore their written contribution requirements!

Good Counter: Contribution Guide (ZAP)

https://github.com/zaproxy/zaproxy/blob/develop/CONTRIBUTING.md#guidelines-for-pull-request-pr-submission-and-processing

Contribution Guide (Juice Shop)

https://bkimminich.gitbooks.io/pwning-owasp-juice-shop/content/part3/contribution.html

Auto Counter: 👊-enforced Coding Style

http://standardjs.com

Solecistic Versioning

The Semantic Versioning syntax (MAJOR.MINOR.PATCH) is used while its semantics are not adhered to.

Bad Example: AngularJS 1.x

Good Example: AngularJS 2+

Juice Shop takes it serious

For a tiny incompatible configuration-file change Juice Shop went from 2.26.0 to 3.0.0

Better: Make it backward compatible

This configuration-change from 4.0.0 to 4.1.0 was kept compatible using e.g. programmatic defaults

Major Zero

Not even the original author thinks that the project is mature enough to release a 1.x version from it.

Example: z85-cli (😜)

Chapter Three

My little OSS Sustainability Toolbox

Git Flow

The master branch stores the official release history, while development happens on the develop branch.

Git Flow ⁒ Feature Branches

Clean Code


Suprise, surprise!

Clean Javascript! (😮)


'use strict'

var path = require('path')
var utils = require('../lib/utils')
var challenges = require('../data/datacache').challenges

exports = module.exports = function servePremiumContent () {
  return function (req, res) {
    if (utils.notSolved(challenges.premiumPaywallChallenge)) {
      utils.solve(challenges.premiumPaywallChallenge)
    }
    res.sendFile(path.resolve(__dirname, '../app/private/under-construction.gif'))
  }
}
				

Well, never mind...


fs.copy('app/index.template.html', 'app/index.html', {overwrite: true}, function () {
      if (config.get('application.logo')) {
        var logo = config.get('application.logo')
        if (utils.startsWith(logo, 'http')) {
          var logoPath = logo
          logo = decodeURIComponent(logo.substring(logo.lastIndexOf('/') + 1))
          utils.downloadToFile(logoPath, 'app/public/images/' + logo)
        }
        var logoImageTag = ''
        replace({ regex: //, replacement: logoImageTag, paths: ['app/index.html'], recursive: false, silent: true })
      }
      if (config.get('application.theme')) {
        var themeCss = 'bower_components/bootswatch/' + config.get('application.theme') + '/bootstrap.min.css'
        replace({ regex: /bower_components\/bootswatch\/.*\/bootstrap\.min\.css/, replacement: themeCss, paths: ['app/index.html'], recursive: false, silent: true })
      }
    })
				

...it's still Javascript at the end of the day! (🤐)

Test Automation

Example: UI Unit Test


it('should hold anonymous placeholder for email if current user is not logged in',
  inject(function () {
      $httpBackend.whenGET('/rest/user/whoami').respond(200, {user: {}})

      $httpBackend.flush()

      expect(scope.userEmail).toBe('anonymous')
  })
)
				

Example: API Integration Tests


frisby.create('GET security question returns nothing for an unknown email address')
  .get(REST_URL + '/user/security-question?email=horst@unknown-us.er')
  .expectStatus(200)
  .expectJSON({})
  .toss()
				

frisby.create('DELETE existing product is forbidden via public API')
  .delete(API_URL + '/Products/1')
  .expectStatus(401)
  .toss()
				

Example: Challenge E2E Test


describe('challenge "loginAdmin"', function () {
  it('should log in Admin with SQLI attack on email field using "\' or 1=1--"', function () {
    email.sendKeys('\' or 1=1--')
    password.sendKeys('a')
    loginButton.click()

    expect(browser.getLocationAbsUrl()).toMatch(/\/search/)
  })
  protractor.expect.challengeSolved({challenge: 'Login Admin'})
})
				

Example: Checking if a challenge is solved


protractor.expect = {
  challengeSolved: function (context) {
    describe('(shared)', function () {
      beforeEach(function () {
        browser.get('/#/score-board')
      })

      it("challenge '" + context.challenge + "' must be solved on score board", function () {
        expect(element(by.id(context.challenge + '.solved')).getAttribute('class'))
	  .not.toMatch('ng-hide')
        expect(element(by.id(context.challenge + '.notSolved')).getAttribute('class'))
	  .toMatch('ng-hide')
      })
    })
  }
}
				

What is a realistic goal for Test Coverage ?

100%

(😨)

Juice Shop Test Coverage

Having all tests pass after a commit means, it's safe to merge PRs or publish a new release!
Juice Shop CTF Extension

Mutation Testing


Reality check: Do your tests actually test stuff?

Example: Report for Juice Shop CTF

A realistic goal for Mutation Coverage ?

100%

(😱)

Juice Shop Mutation Coverage

Juice Shop CTF Extension

92%*

98%



*of the UI unit tests!

(😏)

CI/CD

Chapter undefined

Live Release

What could possibly go wrong?

Release Dashboard

Release Checklist

  1. Create draft release v4.2.0 on GitHub✔️
  2. git checkout master✔️
  3. git merge develop✔️
  4. git tag v4.2.0 -s -m "v4.2.0"
  5. git push --follow-tags

Chapter 3 (continued)

My little OSS Sustainability Toolbox

Quality Metrics

Example: Code Climate Stats & Issues

Example: Coveralls 😡 at CTF-Extension

And now for the most important metric of all...

🚚

🚚 Truck Factor

The number of team members that would have to be run over by a truck to effectively kill the project.

For us as a logistics company this 🚚=💀 metaphor sucks...

So, let's change the metaphor into...

🌴

🌴 Hawaii Factor

The number of team members that would have to leave for permanent vacation on Hawaii to effectively retire the project.

The term "Hawaii Factor" was (to my knowledge) first proposed by Timo Pagel at AppSecEU 2017 in Belfast.

What is a realistic goal for Hawaii Factor ?

Meaning: If the whole team leaves, a new developer can take over and would be able to maintain & continue working on the project!

Dependency Control

Example: Gemnasium Dashboard

Chapter 4.1

New Open Source Antipatterns

Badge Barrage

The front-page is overcrowded with (mostly useless) information and status badges.

Overexaggerated Example

Bad Example

Content Duplication

Fail to maintain the same information & documentation in multiple places consistently.

Example: Project Introduction

Reference over duplication when possible

Pick a single point of truth

Coin Flip CI

✔️✔️❌✔️❌✔️. With no code changes in between.

I wonder how our Live Release is doing...

🤞🙈😱🔮

Free beer

Some services might be free of charge for OSS projects but come with some other hidden costs or annoyance.

Contributor Laurels

Not giving enough credit to contributors.

Easy Counter: Make them visible

Some contribs deserve bonus visibility


Josh Grossman (CTFd SQLs🚩)

Timo Pagel (Themes🎭/XSS-Demo🎶)

Jannik Hollenbach (CTF🌟/Docker📦)

Some contribs deserve bonus visibility


Josh Grossman (CTFd SQLs🚩)

Timo Pagel (Themes🎭/XSS-Demo🎶)

Jannik Hollenbach (CTF🌟/Docker📦)

git commits are not everything!

Juice Shop's Crowdin Translators

https://crowdin.com/project/owasp-juice-shop

Bloggers and Podcasters

https://github.com/bkimminich/juice-shop#references

Conferences & Meetups

https://github.com/bkimminich/juice-shop#references

Lectures & Trainings

https://github.com/bkimminich/juice-shop#references

Not to forget some very unique contributions...

3D-printed Keychain

by Viktor Lindström

Painted Artwork

by Laura Kimminich

How to counter "Contributor Laurels" antipattern?

Stickers

for small contributions & to give out at events like this one

Shirts & Swag

for larger or regular contributions

People love this kind of stuff, trust me!

People love this kind of stuff, trust me!

People love this kind of stuff, trust me!

People love this kind of stuff, trust me!

People love this kind of stuff, trust me!

People love this kind of stuff, trust me!

People love this kind of stuff, trust me!

Chapter NaN

Release Outcome

✔️=😎 or ❌=😭?

Epic 😎/😭 Dashboard


		if (buildOutcome === '😎') { // show the following text
				

One last manual step later...


		} else {
		  /* skip this and the next slide */
		}
				

The new release is live

Copyright (c) 2017 Björn Kimminich

Created with reveal.js - The HTML Presentation Framework