-
Notifications
You must be signed in to change notification settings - Fork 4
Add login functionality for Juice Shop #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||
| /* | ||||||
| * Copyright (c) 2014-2026 Bjoern Kimminich & the OWASP Juice Shop contributors. | ||||||
| * SPDX-License-Identifier: MIT | ||||||
| */ | ||||||
| import { type Request, type Response, type NextFunction } from 'express' | ||||||
| import config from 'config' | ||||||
|
|
||||||
| import * as challengeUtils from '../lib/challengeUtils' | ||||||
| import { challenges, users } from '../data/datacache' | ||||||
| import { BasketModel } from '../models/basket' | ||||||
| import * as security from '../lib/insecurity' | ||||||
| import { UserModel } from '../models/user' | ||||||
| import * as models from '../models/index' | ||||||
| import { type User } from '../data/types' | ||||||
| import * as utils from '../lib/utils' | ||||||
|
|
||||||
| // vuln-code-snippet start loginAdminChallenge loginBenderChallenge loginJimChallenge | ||||||
| export function login () { | ||||||
| function afterLogin (user: User, res: Response, next: NextFunction) { | ||||||
| verifyPostLoginChallenges(user) // vuln-code-snippet hide-line | ||||||
| BasketModel.findOrCreate({ where: { UserId: user.id } }) | ||||||
| .then(([basket]: [BasketModel, boolean]) => { | ||||||
| const authenticatedUser = { data: user, bid: basket.id } // keep track of original basket | ||||||
| const token = security.authorize(authenticatedUser) | ||||||
| security.authenticatedUsers.put(token, authenticatedUser) | ||||||
| res.json({ authentication: { token, bid: basket.id, umail: user.email } }) | ||||||
| }).catch((error: Error) => { | ||||||
| next(error) | ||||||
| }) | ||||||
| } | ||||||
|
|
||||||
| return (req: Request, res: Response, next: NextFunction) => { | ||||||
| verifyPreLoginChallenges(req) // vuln-code-snippet hide-line | ||||||
| models.sequelize.query(`SELECT * FROM Users WHERE email = '${req.body.email || ''}' AND password = '${security.hash(req.body.password || '')}' AND deletedAt IS NULL`, { model: UserModel, plain: true }) // vuln-code-snippet vuln-line loginAdminChallenge loginBenderChallenge loginJimChallenge | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [Bearer] <javascript_lang_sql_injection> reported by reviewdog 🐶 Unsanitized input in SQL queryDescriptionUsing unsanitized data, such as user input or request data, or externally influenced data passed to a function, in SQL query exposes your application to SQL injection attacks. This vulnerability arises when externally controlled data is directly included in SQL statements without proper sanitation, allowing attackers to manipulate queries and access or modify data. Remediations
ReferencesThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential SQL injection via string-based query concatenation - critical severity
Suggested change
Reply |
||||||
| .then((authenticatedUser) => { // vuln-code-snippet neutral-line loginAdminChallenge loginBenderChallenge loginJimChallenge | ||||||
| const user = utils.queryResultToJson(authenticatedUser) | ||||||
| if (user.data?.id && user.data.totpSecret !== '') { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚫 [Bearer] <javascript_lang_observable_timing> reported by reviewdog 🐶 Observable Timing DiscrepancyDescriptionObservable Timing Discrepancy occurs when the time it takes for certain operations to complete can be measured and observed by attackers. This vulnerability is particularly concerning when operations involve sensitive information, such as password checks or secret comparisons. If attackers can analyze how long these operations take, they might be able to deduce confidential details, putting your data at risk. Remediations
References |
||||||
| res.status(401).json({ | ||||||
| status: 'totp_token_required', | ||||||
| data: { | ||||||
| tmpToken: security.authorize({ | ||||||
| userId: user.data.id, | ||||||
| type: 'password_valid_needs_second_factor_token' | ||||||
| }) | ||||||
| } | ||||||
| }) | ||||||
| } else if (user.data?.id) { | ||||||
| afterLogin(user.data, res, next) | ||||||
| } else { | ||||||
| res.status(401).send(res.__('Invalid email or password.')) | ||||||
| } | ||||||
| }).catch((error: Error) => { | ||||||
| next(error) | ||||||
| }) | ||||||
| } | ||||||
| // vuln-code-snippet end loginAdminChallenge loginBenderChallenge loginJimChallenge | ||||||
|
|
||||||
| function verifyPreLoginChallenges (req: Request) { | ||||||
| challengeUtils.solveIf(challenges.weakPasswordChallenge, () => { return req.body.email === 'admin@' + config.get<string>('application.domain') && req.body.password === 'admin123' }) | ||||||
| challengeUtils.solveIf(challenges.loginSupportChallenge, () => { return req.body.email === 'support@' + config.get<string>('application.domain') && req.body.password === 'J6aVjTgOpRs@?5l!Zkq2AYnCE@RF$P' }) | ||||||
| challengeUtils.solveIf(challenges.loginRapperChallenge, () => { return req.body.email === 'mc.safesearch@' + config.get<string>('application.domain') && req.body.password === 'Mr. N00dles' }) | ||||||
| challengeUtils.solveIf(challenges.loginAmyChallenge, () => { return req.body.email === 'amy@' + config.get<string>('application.domain') && req.body.password === 'K1f.....................' }) | ||||||
| challengeUtils.solveIf(challenges.dlpPasswordSprayingChallenge, () => { return req.body.email === 'J12934@' + config.get<string>('application.domain') && req.body.password === '0Y8rMnww$*9VFYE§59-!Fg1L6t&6lB' }) | ||||||
| challengeUtils.solveIf(challenges.oauthUserPasswordChallenge, () => { return req.body.email === 'bjoern.kimminich@gmail.com' && req.body.password === 'bW9jLmxpYW1nQGhjaW5pbW1pay5ucmVvamI=' }) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exposed secret in login-juice-shop.js - high severity Reply |
||||||
| challengeUtils.solveIf(challenges.exposedCredentialsChallenge, () => { return req.body.email === 'testing@' + config.get<string>('application.domain') && req.body.password === 'IamUsedForTesting' }) | ||||||
| } | ||||||
|
|
||||||
| function verifyPostLoginChallenges (user: User) { | ||||||
| challengeUtils.solveIf(challenges.loginAdminChallenge, () => { return user.id === users.admin.id }) | ||||||
| challengeUtils.solveIf(challenges.loginJimChallenge, () => { return user.id === users.jim.id }) | ||||||
| challengeUtils.solveIf(challenges.loginBenderChallenge, () => { return user.id === users.bender.id }) | ||||||
| challengeUtils.solveIf(challenges.ghostLoginChallenge, () => { return user.id === users.chris.id }) | ||||||
| if (challengeUtils.notSolved(challenges.ephemeralAccountantChallenge) && user.email === 'acc0unt4nt@' + config.get<string>('application.domain') && user.role === 'accounting') { | ||||||
| UserModel.count({ where: { email: 'acc0unt4nt@' + config.get<string>('application.domain') } }).then((count: number) => { | ||||||
| if (count === 0) { | ||||||
| challengeUtils.solve(challenges.ephemeralAccountantChallenge) | ||||||
| } | ||||||
| }).catch(() => { | ||||||
| throw new Error('Unable to verify challenges! Try again') | ||||||
| }) | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔵 Low - The new Juice Shop login flow is dead code in this application
As committed, this handler will never execute because the Express app still binds
/logintoroutes.loginHandlerand nothing importslogin-juice-shop.js. Even if a later edit tried to wire it in, this file cannot load in the current codebase because the project is CommonJS and the referenced../lib,../data, and../modelstrees do not exist here, so the PR does not actually add the login functionality it claims to introduce.Show fix
Replace this transplanted Juice Shop module with a handler that matches this repository's runtime and data model, then explicitly mount it from
app.js(orroutes/index.js) and add a route-level test that exercises/login. If the new flow is not ready, remove this file from the PR to avoid a misleading dead-code feature addition.More info - Reply on this comment to give feedback or ignore the issue.