UNPKG

juice-shop-ctf-cli

Version:

Capture-the-Flag (CTF) environment setup tools for OWASP Juice Shop

163 lines (149 loc) 10.9 kB
/* * Copyright (c) 2016-2025 Bjoern Kimminich & the OWASP Juice Shop contributors. * SPDX-License-Identifier: MIT */ const Promise = require('bluebird') const chai = require('chai') chai.use(require('chai-things')) chai.use(require('chai-subset')) chai.use(require('chai-as-promised')) const expect = chai.expect const generateData = require('../../lib/generators/ctfd') const options = require('../../lib/options') const defaultOptions = { insertHints: options.noTextHints, insertHintUrls: options.noHintUrls, ctfKey: '', vulnSnippets: {} } describe('Generated CTFd data', () => { let challenges beforeEach(() => { challenges = { c1: { id: 1, key: 'k1', name: 'c1', description: 'C1', difficulty: 1, category: '1', tags: 'foo,bar' }, c2: { id: 2, key: 'k2', name: 'c2', description: 'C2', difficulty: 2, category: '2', tags: null }, c3: { id: 3, key: 'k3', name: 'c3', description: 'C3', difficulty: 3, category: '2', tags: 'foo' }, c4: { id: 4, key: 'k4', name: 'c4', description: 'C4', difficulty: 4, category: '3', tags: null }, c5: { id: 5, key: 'k5', name: 'c5', description: 'C5', difficulty: 5, category: '1', tags: 'foo,bar,baz' } } }) it('should consist of one object pushed into result per challenge', function () { return expect(generateData(challenges, defaultOptions)).to.eventually.deep.include.members([ { name: 'c1', description: '"C1 (Difficulty Level: 1)"', category: '1', value: 100, type: 'standard', state: 'visible', max_attempts: 0, flags: '958c64658383140e7d08d5dee091009cc0eafc1f', tags: '"foo,bar"', hints: '', type_data: '' }, { name: 'c2', description: '"C2 (Difficulty Level: 2)"', category: '2', value: 250, type: 'standard', state: 'visible', max_attempts: 0, flags: '49294e8b829f5b053f748facad22825ccb4bf420', tags: '', hints: '', type_data: '' }, { name: 'c3', description: '"C3 (Difficulty Level: 3)"', category: '2', value: 450, type: 'standard', state: 'visible', max_attempts: 0, flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '', type_data: '' }, { name: 'c4', description: '"C4 (Difficulty Level: 4)"', category: '3', value: 700, type: 'standard', state: 'visible', max_attempts: 0, flags: '4e2b98db86cc32c56cba287db411198534af4ab6', tags: '', hints: '', type_data: '' }, { name: 'c5', description: '"C5 (Difficulty Level: 5)"', category: '1', value: 1000, type: 'standard', state: 'visible', max_attempts: 0, flags: '554df67c6c0b6a99efecaec4fe2ced73b7b5be60', tags: '"foo,bar,baz"', hints: '', type_data: '' } ] ) }) it('should be empty when given no challenges', () => expect(generateData({}, defaultOptions)).to.eventually.deep.equal([]) ) it('should log generator error to console', () => expect(generateData({ c1: undefined }, defaultOptions)).to.be.rejectedWith('Failed to generate challenge data! Cannot read properties of undefined (reading') ) it('should fill the hint property for a single text hint defined on a challenge', () => { challenges.c3.hint = 'hint' return Promise.all([ expect(generateData(challenges, { insertHints: options.freeTextHints, insertHintUrls: options.noHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.deep.include( { name: 'c3', description: '"C3 (Difficulty Level: 3)"', category: '2', value: 450, type: 'standard', state: 'visible', max_attempts: 0, flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '"[{""content"":""hint"",""cost"":0}]"', type_data: '' } ), expect(generateData(challenges, { insertHints: options.paidTextHints, insertHintUrls: options.noHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.deep.include( { name: 'c3', description: '"C3 (Difficulty Level: 3)"', category: '2', value: 450, type: 'standard', state: 'visible', max_attempts: 0, flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '"[{""content"":""hint"",""cost"":45}]"', type_data: '' } ) ]) }) it('should fill the hint property for a single hint URL defined on a challenge', () => { challenges.c3.hintUrl = 'hintUrl' return Promise.all([ expect(generateData(challenges, { insertHints: options.noTextHints, insertHintUrls: options.freeHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.deep.include( { name: 'c3', description: '"C3 (Difficulty Level: 3)"', category: '2', value: 450, type: 'standard', state: 'visible', max_attempts: 0, flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '"[{""content"":""hintUrl"",""cost"":0}]"', type_data: '' } ), expect(generateData(challenges, { insertHints: options.noTextHints, insertHintUrls: options.paidHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.deep.include( { name: 'c3', description: '"C3 (Difficulty Level: 3)"', category: '2', value: 450, type: 'standard', state: 'visible', max_attempts: 0, flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '"[{""content"":""hintUrl"",""cost"":90}]"', type_data: '' } ) ]) }) it('should push an object each into hints.results for a challenge with text hint and hint URL defined with prerequisite relationship', () => { challenges.c3.hint = 'hint' challenges.c3.hintUrl = 'hintUrl' return Promise.all([ expect(generateData(challenges, { insertHints: options.freeTextHints, insertHintUrls: options.freeHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.deep.include( { name: 'c3', description: '"C3 (Difficulty Level: 3)"', value: 450, category: '2', state: 'visible', max_attempts: 0, type: 'standard', type_data: '', flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '"[{""content"":""hint"",""cost"":0},{""content"":""hintUrl"",""cost"":0}]"' } ), expect(generateData(challenges, { insertHints: options.paidTextHints, insertHintUrls: options.freeHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.deep.include( { name: 'c3', description: '"C3 (Difficulty Level: 3)"', value: 450, category: '2', state: 'visible', max_attempts: 0, type: 'standard', type_data: '', flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '"[{""content"":""hint"",""cost"":45},{""content"":""hintUrl"",""cost"":0}]"' } ), expect(generateData(challenges, { insertHints: options.freeTextHints, insertHintUrls: options.paidHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.deep.include( { name: 'c3', description: '"C3 (Difficulty Level: 3)"', value: 450, category: '2', state: 'visible', max_attempts: 0, type: 'standard', type_data: '', flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '"[{""content"":""hint"",""cost"":0},{""content"":""hintUrl"",""cost"":90}]"' } ), expect(generateData(challenges, { insertHints: options.paidTextHints, insertHintUrls: options.paidHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.deep.include( { name: 'c3', description: '"C3 (Difficulty Level: 3)"', value: 450, category: '2', state: 'visible', max_attempts: 0, type: 'standard', type_data: '', flags: 'aae3acb6eff2000c0e12af0d0d875d0bdbf4ca81', tags: '"foo"', hints: '"[{""content"":""hint"",""cost"":45},{""content"":""hintUrl"",""cost"":90}]"' } ) ]) }) it('should not insert a text hint when corresponding hint option is not chosen', () => { challenges.c1.hint = 'hint' challenges.c2.hint = 'hint' return expect(generateData(challenges, defaultOptions)).to.eventually.deep.include.members([ { name: 'c1', description: '"C1 (Difficulty Level: 1)"', category: '1', value: 100, type: 'standard', state: 'visible', max_attempts: 0, flags: '958c64658383140e7d08d5dee091009cc0eafc1f', tags: '"foo,bar"', hints: '', type_data: '' }, { name: 'c2', description: '"C2 (Difficulty Level: 2)"', category: '2', value: 250, type: 'standard', state: 'visible', max_attempts: 0, flags: '49294e8b829f5b053f748facad22825ccb4bf420', tags: '', hints: '', type_data: '' } ] ) }) it('should not insert a hint URL when corresponding hint option is not chosen', () => { challenges.c1.hintUrl = 'hintUrl' challenges.c2.hintUrl = 'hintUrl' return expect(generateData(challenges, defaultOptions)).to.eventually.deep.include.members([ { name: 'c1', description: '"C1 (Difficulty Level: 1)"', category: '1', value: 100, type: 'standard', state: 'visible', max_attempts: 0, flags: '958c64658383140e7d08d5dee091009cc0eafc1f', tags: '"foo,bar"', hints: '', type_data: '' }, { name: 'c2', description: '"C2 (Difficulty Level: 2)"', category: '2', value: 250, type: 'standard', state: 'visible', max_attempts: 0, flags: '49294e8b829f5b053f748facad22825ccb4bf420', tags: '', hints: '', type_data: '' } ] ) }) xit('should not insert a text hint for challenges that do not have a hint defined', () => { challenges.c1.hint = 'hint' challenges.c2.hint = 'hint' challenges.c3.hint = undefined challenges.c4.hint = 'hint' challenges.c5.hint = 'hint' return Promise.all([ expect(generateData(challenges, { insertHints: options.freeTextHints, insertHintUrls: options.noHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.not.deep.include( { hints: { results: [ { id: 3, challenge_id: 3, content: 'hint', cost: 0, type: 'standard' } ] } }), expect(generateData(challenges, { insertHints: options.paidTextHints, insertHintUrls: options.noHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.not.deep.include( { hints: { results: [ { id: 3, challenge_id: 3, content: 'hint', cost: 45, type: 'standard' } ] } }) ]) }) xit('should not insert a hint URL for challenges that do not have a hint defined', () => { challenges.c1.hintUrl = 'hintUrl' challenges.c2.hintUrl = 'hintUrl' challenges.c3.hintUrl = undefined challenges.c4.hintUrl = 'hintUrl' challenges.c5.hintUrl = 'hintUrl' return Promise.all([ expect(generateData(challenges, { insertHints: options.freeTextHints, insertHintUrls: options.noHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.not.deep.include( { hints: { results: [ { id: 3, challenge_id: 3, content: 'hintUrl', cost: 0, type: 'standard' } ] } }), expect(generateData(challenges, { insertHints: options.paidTextHints, insertHintUrls: options.noHintUrls, ctfKey: '', vulnSnippets: {} })).to.eventually.not.deep.include( { hints: { results: [ { id: 3, challenge_id: 3, content: 'hintUrl', cost: 90, type: 'standard' } ] } }) ]) }) })