juice-shop
120 строк · 4.3 Кб
1/*
2* Copyright (c) 2014-2024 Bjoern Kimminich & the OWASP Juice Shop contributors.
3* SPDX-License-Identifier: MIT
4*/
5
6import { type NextFunction, type Request, type Response } from 'express'7import fs from 'fs'8import yaml from 'js-yaml'9import { getCodeChallenges } from '../lib/codingChallenges'10import * as accuracy from '../lib/accuracy'11import * as utils from '../lib/utils'12
13const challengeUtils = require('../lib/challengeUtils')14
15interface SnippetRequestBody {16challenge: string17}
18
19interface VerdictRequestBody {20selectedLines: number[]21key: string22}
23
24const setStatusCode = (error: any) => {25switch (error.name) {26case 'BrokenBoundary':27return 42228default:29return 20030}31}
32
33export const retrieveCodeSnippet = async (challengeKey: string) => {34const codeChallenges = await getCodeChallenges()35if (codeChallenges.has(challengeKey)) {36return codeChallenges.get(challengeKey) ?? null37}38return null39}
40
41exports.serveCodeSnippet = () => async (req: Request<SnippetRequestBody, Record<string, unknown>, Record<string, unknown>>, res: Response, next: NextFunction) => {42try {43const snippetData = await retrieveCodeSnippet(req.params.challenge)44if (snippetData == null) {45res.status(404).json({ status: 'error', error: `No code challenge for challenge key: ${req.params.challenge}` })46return47}48res.status(200).json({ snippet: snippetData.snippet })49} catch (error) {50const statusCode = setStatusCode(error)51res.status(statusCode).json({ status: 'error', error: utils.getErrorMessage(error) })52}53}
54
55export const retrieveChallengesWithCodeSnippet = async () => {56const codeChallenges = await getCodeChallenges()57return [...codeChallenges.keys()]58}
59
60exports.serveChallengesWithCodeSnippet = () => async (req: Request, res: Response, next: NextFunction) => {61const codingChallenges = await retrieveChallengesWithCodeSnippet()62res.json({ challenges: codingChallenges })63}
64
65export const getVerdict = (vulnLines: number[], neutralLines: number[], selectedLines: number[]) => {66if (selectedLines === undefined) return false67if (vulnLines.length > selectedLines.length) return false68if (!vulnLines.every(e => selectedLines.includes(e))) return false69const okLines = [...vulnLines, ...neutralLines]70const notOkLines = selectedLines.filter(x => !okLines.includes(x))71return notOkLines.length === 072}
73
74exports.checkVulnLines = () => async (req: Request<Record<string, unknown>, Record<string, unknown>, VerdictRequestBody>, res: Response, next: NextFunction) => {75const key = req.body.key76let snippetData77try {78snippetData = await retrieveCodeSnippet(key)79if (snippetData == null) {80res.status(404).json({ status: 'error', error: `No code challenge for challenge key: ${key}` })81return82}83} catch (error) {84const statusCode = setStatusCode(error)85res.status(statusCode).json({ status: 'error', error: utils.getErrorMessage(error) })86return87}88const vulnLines: number[] = snippetData.vulnLines89const neutralLines: number[] = snippetData.neutralLines90const selectedLines: number[] = req.body.selectedLines91const verdict = getVerdict(vulnLines, neutralLines, selectedLines)92let hint93if (fs.existsSync('./data/static/codefixes/' + key + '.info.yml')) {94const codingChallengeInfos = yaml.load(fs.readFileSync('./data/static/codefixes/' + key + '.info.yml', 'utf8'))95if (codingChallengeInfos?.hints) {96if (accuracy.getFindItAttempts(key) > codingChallengeInfos.hints.length) {97if (vulnLines.length === 1) {98hint = res.__('Line {{vulnLine}} is responsible for this vulnerability or security flaw. Select it and submit to proceed.', { vulnLine: vulnLines[0].toString() })99} else {100hint = res.__('Lines {{vulnLines}} are responsible for this vulnerability or security flaw. Select them and submit to proceed.', { vulnLines: vulnLines.toString() })101}102} else {103const nextHint = codingChallengeInfos.hints[accuracy.getFindItAttempts(key) - 1] // -1 prevents after first attempt104if (nextHint) hint = res.__(nextHint)105}106}107}108if (verdict) {109await challengeUtils.solveFindIt(key)110res.status(200).json({111verdict: true112})113} else {114accuracy.storeFindItVerdict(key, false)115res.status(200).json({116verdict: false,117hint
118})119}120}
121