juice-shop-ctf-cli
Advanced tools
Comparing version 0.3.0 to 0.3.1
#!/usr/bin/env node | ||
'use strict' | ||
var lib = require('../index') | ||
var juiceShopCtfCli = require('../index') | ||
try { | ||
lib.juiceShopCtfCli() | ||
} catch (error) { | ||
console.error(error.message) | ||
} | ||
juiceShopCtfCli() |
# Contributing [](https://github.com/bkimminich/juice-shop-ctf/graphs/contributors) [](https://huboard.com/bkimminich/juice-shop-ctf) | ||
[](https://travis-ci.org/bkimminich/juice-shop-ctf) | ||
[](https://coveralls.io/github/bkimminich/juice-shop-ctf?branch=master) | ||
[](https://gemnasium.com/github.com/bkimminich/juice-shop-ctf) | ||
[](https://www.npmjs.com/package/juice-shop-ctf-cli) | ||
[](https://www.npmjs.com/package/juice-shop-ctf-cli) | ||
Found a bug? Got an idea for enhancement? Improvement for cheating | ||
@@ -28,8 +34,26 @@ prevention? | ||
## Unit & end-to-end Tests | ||
There is a full suite containing | ||
* independent unit tests for each module | ||
* an e2e test simulating real input to the CLI | ||
``` | ||
npm test | ||
``` | ||
> The test suite currently runs unit and e2e tests in the same step. | ||
> This is not best practice when it comes to software testing as it | ||
> slows down the execution of the suite in total considerably and | ||
> requires a working internet connection. On the plus side you can be | ||
> _really sure_ that the program works as intended when `npm test` | ||
> passes and still reports 100% code coverage. | ||
### JavaScript Standard Style Guide | ||
Since v2.7.0 the `npm test` script verifies code complicance with the | ||
`standard` style before running the tests. If PRs deviate from this | ||
coding style, they will now immediately fail their build and will not be | ||
merged until compliant. | ||
The `npm test` script verifies code complicance with the `standard` | ||
style before running the tests. If PRs deviate from this coding style, | ||
they will now immediately fail their build and will not be merged until | ||
compliant. | ||
@@ -41,2 +65,18 @@ [](https://github.com/feross/standard) | ||
> style issues automatically without breaking your code. You might need | ||
> to `npm install -g standard` first. | ||
> to `npm i -g standard` first. | ||
## Mutation Tests | ||
The [mutation tests](https://en.wikipedia.org/wiki/Mutation_testing) | ||
ensure the quality of the unit test suite by making small changes to the | ||
code that should cause one or more tests to fail. If none does this | ||
"mutated line" is not properly covered by meaningful assertions. | ||
``` | ||
npm run stryker | ||
``` | ||
> Only the unit tests are covered by mutation tests. For the end-to-end | ||
> tests this would not be suitable for performance and concurrency | ||
> reasons. | ||
109
index.js
'use strict' | ||
var Promise = require('bluebird') | ||
var inquirer = require('inquirer') | ||
var request = require('request-promise') | ||
var jsSHA = require('jssha') | ||
var ProgressBar = require('progress') | ||
var fs = require('fs') | ||
var path = require('path') | ||
var colors = require('colors') // eslint-disable-line no-unused-vars | ||
var secretKey = require('./lib/secretKey') | ||
var fetchChallenges = require('./lib/fetchChallenges') | ||
var generateSql = require('./lib/generateSql') | ||
var writeOutput = require('./lib/writeOutput') | ||
@@ -22,3 +20,3 @@ var juiceShopCtfCli = function () { | ||
name: 'ctfKey', | ||
message: 'HMAC key <or> URL to ctf.key file?', | ||
message: 'Secret key <or> URL to ctf.key file?', | ||
default: 'https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key' | ||
@@ -44,5 +42,5 @@ }, | ||
console.log() | ||
fetchHmacKey(answers.ctfKey).then(function (hmacKey) { | ||
secretKey(answers.ctfKey).then(function (secretKey) { | ||
fetchChallenges(answers.juiceShopUrl).then(function (challenges) { | ||
generateSql(challenges, answers.deleteBeforeInsert, answers.selectAfterInsert, hmacKey).then(function (sql) { | ||
generateSql(challenges, answers.deleteBeforeInsert, answers.selectAfterInsert, secretKey).then(function (sql) { | ||
writeOutput(sql) | ||
@@ -55,93 +53,2 @@ }) | ||
function fetchHmacKey (ctfKey) { | ||
return new Promise(function (resolve) { | ||
if (ctfKey && isUrl(ctfKey)) { | ||
request(ctfKey) | ||
.then(function (body) { | ||
resolve(body) | ||
}).catch(function (error) { | ||
console.log('Failed'.red + ' to fetch HMAC key from URL! ' + error) | ||
}) | ||
} else { | ||
resolve(ctfKey) | ||
} | ||
}) | ||
} | ||
function fetchChallenges (juiceShopUrl) { | ||
return new Promise(function (resolve) { | ||
request({ url: juiceShopUrl + '/api/Challenges', json: true }).then(function (body) { | ||
resolve(body.data) | ||
}).catch(function (error) { | ||
console.log('Failed'.red + ' to fetch challenges from API! ' + error) | ||
}) | ||
}) | ||
} | ||
function generateSql (challenges, prependDelete, appendSelect, hmacKey, progressBar) { | ||
return new Promise(function (resolve) { | ||
var sql = '' | ||
if (prependDelete) { | ||
sql = sql + 'DELETE FROM challenges;\n\r' | ||
} | ||
var bar = progressBar || new ProgressBar('Generating INSERT statements [:bar] :percent', { total: Object.keys(challenges).length }) | ||
for (var key in challenges) { | ||
bar.tick() | ||
if (challenges.hasOwnProperty(key)) { | ||
var challenge = challenges[key] | ||
sql = sql + 'INSERT INTO challenges (id, name, description, value, category, flags, hidden) VALUES (' | ||
sql = sql + '' + challenge.id + ', ' | ||
sql = sql + '"' + challenge.name + '", ' | ||
sql = sql + '"' + challenge.description.replace(/"/g, '""') + ' (Difficulty Level: ' + challenge.difficulty + ')", ' | ||
sql = sql + '"' + calculateScore(challenge.difficulty) + '", ' | ||
sql = sql + '"' + challenge.category + '", ' | ||
sql = sql + '"[{""flag"": ""' + toHmac(challenge.name, hmacKey) + '"", ""type"": 0}]", ' | ||
sql = sql + '0);\n\r' | ||
} | ||
} | ||
if (appendSelect) { | ||
sql = sql + 'SELECT * FROM challenges;\n\r' | ||
} | ||
resolve(sql) | ||
}) | ||
} | ||
function writeOutput (sql) { | ||
fs.writeFile('insert-ctfd-challenges.sql', sql, function (error) { | ||
if (error) { | ||
console.log('Failed'.red + ' to write output to file! ' + error) | ||
} else { | ||
console.log('SQL written to ' + path.resolve('insert-ctfd-challenges.sql').green) | ||
console.log() | ||
console.log('For a step-by-step guide to apply the INSERT statements to ' + 'CTFd'.bold + ', please refer to') | ||
console.log('https://github.com/bkimminich/juice-shop-ctf/blob/master/CTFd/GenerateCTFdInserts.html#L80'.gray) // TODO Refer to Markdown doc on GitHub instead | ||
} | ||
}) | ||
} | ||
function toHmac (text, theSecretKey) { | ||
var shaObj = new jsSHA('SHA-1', 'TEXT') // eslint-disable-line new-cap | ||
shaObj.setHMACKey(theSecretKey, 'TEXT') | ||
shaObj.update(text) | ||
return shaObj.getHMAC('HEX') | ||
} | ||
function isUrl (text) { | ||
return text.match(/^(http|localhost|[0-9][0-9]?[0-9]?\.)/) !== null | ||
} | ||
/* This is a default multiplier whereby the score will be = (difficulty x multiplier) | ||
i.e. using these multipliers will create challenges with the following scores: | ||
* = 100 | ||
** = 250 | ||
*** = 450 | ||
**** = 700 | ||
***** = 1000 | ||
I think it is fair to assume that completing a 5* task will be 10 times harder than a 1* task. | ||
*/ | ||
var multiplier = [ 100, 125, 150, 175, 200 ] | ||
function calculateScore (difficulty) { | ||
return difficulty * multiplier[ difficulty - 1 ] | ||
} | ||
exports.juiceShopCtfCli = juiceShopCtfCli | ||
module.exports = juiceShopCtfCli |
{ | ||
"name": "juice-shop-ctf-cli", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "Command line client to generate INSERT statements for CTFd with the OWASP Juice Shop challenges", | ||
@@ -58,2 +58,3 @@ "author": "Bjoern Kimminich <bjoern.kimminich@owasp.org> (https://www.owasp.org/index.php/User:Bjoern_Kimminich)", | ||
"devDependencies": { | ||
"ava": "^0.18.1", | ||
"chai": "^3.5.0", | ||
@@ -63,2 +64,4 @@ "chai-as-promised": "^6.0.0", | ||
"coveralls": "^2.11.16", | ||
"inquirer-test": "^1.0.5", | ||
"lockfile": "^1.0.3", | ||
"mocha": "^3.2.0", | ||
@@ -65,0 +68,0 @@ "nyc": "^10.1.2", |
@@ -5,2 +5,3 @@ #  OWASP Juice Shop CTF [](https://www.owasp.org/index.php/OWASP_Project_Inventory#tab=Incubator_Projects) [](https://github.com/bkimminich/juice-shop-ctf/releases/latest) [](https://twitter.com/owasp_juiceshop) | ||
[](https://coveralls.io/github/bkimminich/juice-shop-ctf?branch=master) | ||
[](https://gemnasium.com/github.com/bkimminich/juice-shop-ctf) | ||
[](https://www.npmjs.com/package/juice-shop-ctf-cli) | ||
@@ -13,2 +14,4 @@ [](https://www.npmjs.com/package/juice-shop-ctf-cli) | ||
 | ||
## Installation | ||
@@ -28,6 +31,20 @@ | ||
Then simply follow the instructions of the command line tool. Finally, use the generated `insert-ctfd-challenges.sql` file to populate the CTFd database: | ||
Then simply follow the instructions of the command line tool. Finally, apply the generated `insert-ctfd-challenges.sql` following the steps described in the next section. | ||
 | ||
#### Setting up [CTFd](https://ctfd.io) and populating its database | ||
1. Setup [Docker host and Docker compose](https://docs.docker.com/compose/install/). | ||
2. Follow steps 2-4 from [the CTFd Docker setup](https://github.com/isislab/CTFd/wiki/Deployment#docker) to download the source code, create containers and start them. | ||
3. After running `docker-compose up` from previous step, you should be able to browse to your CTFd instance UI (`<<docker host IP>>:8000` by default) and create an admin user and CTF name. | ||
4. Once you have done this, run `docker-compose down` or use `Ctrl-C` to shut down CTFd. Note: Unlike a usual Docker container, data will persist even afterwards. | ||
5. Add the following section to the `docker-compose.yml` file and then run `docker-compose up` again: | ||
``` | ||
ports: | ||
- "3306:3306" | ||
``` | ||
6. You can then use your favourite MySQL client to connect to the CTFd database (default credentials are root with no password) and run the `INSERT` statement you created. | ||
7. When that is done, browse back to your CTFd instance UI and check everything has worked correctly. | ||
8. If everything has worked, do another `docker-compose down`, remove the ports section you added to `docker-compose.yml` and then do `docker-compose up` again and you are ready to go! | ||
### Alternative approach without node.js | ||
@@ -34,0 +51,0 @@ |
@@ -7,3 +7,4 @@ // stryker.conf.js | ||
{ pattern: 'index.js', mutated: true, included: false }, | ||
'test/**/*.js' | ||
{ pattern: 'lib/**/*.js', mutated: true, included: false }, | ||
'test/**/*-spec.js' | ||
], | ||
@@ -10,0 +11,0 @@ testRunner: 'mocha', |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
55050
26
374
112
15
2