mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
[web] add support for regex based allow-list for skipping captcha (#24266)
* [web] double down on validating email addresses * [web] normalize emails in captcha middleware * [web] add support for regex based allow-list for skipping captcha * [web] skip captcha for trusted users on all actions GitOrigin-RevId: a994ebf6b74e80f462d2dab1fe5113bbffa676a9
This commit is contained in:
@@ -6,6 +6,7 @@ const OError = require('@overleaf/o-error')
|
||||
const DeviceHistory = require('./DeviceHistory')
|
||||
const AuthenticationController = require('../Authentication/AuthenticationController')
|
||||
const { expressify } = require('@overleaf/promise-utils')
|
||||
const EmailsHelper = require('../Helpers/EmailHelper')
|
||||
|
||||
function respondInvalidCaptcha(req, res) {
|
||||
res.status(400).json({
|
||||
@@ -41,9 +42,11 @@ async function canSkipCaptcha(req, res) {
|
||||
|
||||
function validateCaptcha(action) {
|
||||
return expressify(async function (req, res, next) {
|
||||
const email = EmailsHelper.parseEmail(req.body?.email)
|
||||
const trustedUser =
|
||||
req.body?.email &&
|
||||
Settings.recaptcha.trustedUsers.includes(req.body.email)
|
||||
email &&
|
||||
(Settings.recaptcha.trustedUsers.includes(email) ||
|
||||
Settings.recaptcha.trustedUsersRegex?.test(email))
|
||||
if (!Settings.recaptcha?.siteKey || Settings.recaptcha.disabled[action]) {
|
||||
if (action === 'login') {
|
||||
AuthenticationController.setAuditInfo(req, { captcha: 'disabled' })
|
||||
@@ -51,15 +54,17 @@ function validateCaptcha(action) {
|
||||
Metrics.inc('captcha', 1, { path: action, status: 'disabled' })
|
||||
return next()
|
||||
}
|
||||
if (trustedUser && action === 'login') {
|
||||
AuthenticationController.setAuditInfo(req, { captcha: 'trusted' })
|
||||
if (trustedUser) {
|
||||
if (action === 'login') {
|
||||
AuthenticationController.setAuditInfo(req, { captcha: 'trusted' })
|
||||
}
|
||||
Metrics.inc('captcha', 1, { path: action, status: 'trusted' })
|
||||
return next()
|
||||
}
|
||||
const reCaptchaResponse = req.body['g-recaptcha-response']
|
||||
if (action === 'login') {
|
||||
await initializeDeviceHistory(req)
|
||||
const fromKnownDevice = req.deviceHistory.has(req.body?.email)
|
||||
const fromKnownDevice = req.deviceHistory.has(email)
|
||||
AuthenticationController.setAuditInfo(req, { fromKnownDevice })
|
||||
if (!reCaptchaResponse && fromKnownDevice) {
|
||||
// The user has previously logged in from this device, which required
|
||||
|
||||
@@ -11,7 +11,7 @@ function getDomain(email) {
|
||||
}
|
||||
|
||||
function parseEmail(email, parseRfcAddress = false) {
|
||||
if (email == null) {
|
||||
if (typeof email !== 'string' || !email) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -779,6 +779,10 @@ module.exports = {
|
||||
.split(',')
|
||||
.map(x => x.trim())
|
||||
.filter(x => x !== ''),
|
||||
trustedUsersRegex: process.env.CAPTCHA_TRUSTED_USERS_REGEX
|
||||
? // Enforce matching of the entire input.
|
||||
new RegExp(`^${process.env.CAPTCHA_TRUSTED_USERS_REGEX}$`)
|
||||
: null,
|
||||
disabled: {
|
||||
invite: true,
|
||||
login: true,
|
||||
|
||||
@@ -18,6 +18,24 @@ describe('EmailHelper', function () {
|
||||
expect(parseEmail(address, true)).to.equal(expected)
|
||||
})
|
||||
|
||||
it('should return null for garbage input', function () {
|
||||
const cases = [
|
||||
undefined,
|
||||
null,
|
||||
'',
|
||||
42,
|
||||
['test@example.com'],
|
||||
{},
|
||||
{ length: 42 },
|
||||
{ trim: true, match: true },
|
||||
{ toString: true },
|
||||
]
|
||||
for (const input of cases) {
|
||||
expect(parseEmail(input)).to.equal(null, input)
|
||||
expect(parseEmail(input, true)).to.equal(null, input)
|
||||
}
|
||||
})
|
||||
|
||||
it('should return null for an invalid single email', function () {
|
||||
const address = 'testexample.com'
|
||||
expect(parseEmail(address)).to.equal(null)
|
||||
|
||||
Reference in New Issue
Block a user