overleaf/services/web/test/acceptance/src/HaveIBeenPwnedApiTests.js

220 lines
6.5 KiB
JavaScript
Raw Normal View History

const Settings = require('@overleaf/settings')
const { expect } = require('chai')
const User = require('./helpers/User').promises
const MockHaveIBeenPwnedApiClass = require('./mocks/MockHaveIBeenPwnedApi')
const { db } = require('../../../app/src/infrastructure/mongodb')
const { getMetric } = require('./helpers/metrics').promises
const sleep = require('util').promisify(setTimeout)
let MockHaveIBeenPwnedApi
before(function () {
MockHaveIBeenPwnedApi = MockHaveIBeenPwnedApiClass.instance()
})
async function letPasswordCheckRunInBackground() {
await sleep(200)
}
async function getMetricReUsed() {
return getMetric(
line => line.includes('password_re_use') && line.includes('re-used')
)
}
async function getMetricUnique() {
return getMetric(
line => line.includes('password_re_use') && line.includes('unique')
)
}
async function getMetricFailure() {
return getMetric(
line => line.includes('password_re_use') && line.includes('failure')
)
}
let user, previous
async function resetPassword(password) {
await user.getCsrfToken()
await user.doRequest('POST', {
url: '/user/password/reset',
form: {
email: user.email,
},
})
const token = (
await db.tokens.findOne({
'data.user_id': user._id.toString(),
})
).token
await user.doRequest('GET', {
url: `/user/password/set?passwordResetToken=${token}&email=${user.email}`,
})
await user.doRequest('POST', {
url: '/user/password/set',
form: {
passwordResetToken: token,
password,
},
})
}
describe('HaveIBeenPwnedApi', function () {
before(function () {
Settings.apis.haveIBeenPwned.enabled = true
})
after(function () {
Settings.apis.haveIBeenPwned.enabled = false
})
describe('login with weak password', function () {
beforeEach(function () {
user = new User()
user.password = 'aLeakedPassword42'
// echo -n aLeakedPassword42 | sha1sum
MockHaveIBeenPwnedApi.addPasswordByHash(
'D1ABBDEEE70CBE8BBCE5D9D039C53C0CE91C0C16'
)
})
beforeEach('create the user', async function () {
await user.ensureUserExists()
await letPasswordCheckRunInBackground()
})
beforeEach('fetch previous count', async function () {
previous = await getMetricReUsed()
})
beforeEach('login', async function () {
await user.loginNoUpdate()
await letPasswordCheckRunInBackground()
})
it('should track the weak password', async function () {
const after = await getMetricReUsed()
expect(after).to.equal(previous + 1)
})
})
describe('login with strong password', function () {
beforeEach(function () {
user = new User()
user.password = 'this-is-a-strong-password'
})
beforeEach('create the user', async function () {
await user.ensureUserExists()
await letPasswordCheckRunInBackground()
})
beforeEach('fetch previous count', async function () {
previous = await getMetricUnique()
})
beforeEach('login', async function () {
await user.loginNoUpdate()
await letPasswordCheckRunInBackground()
})
it('should track the strong password', async function () {
const after = await getMetricUnique()
expect(after).to.equal(previous + 1)
})
})
describe('when the api is producing garbage', function () {
beforeEach(function () {
user = new User()
user.password = 'trigger-garbage-output'
})
beforeEach('create the user', async function () {
await user.ensureUserExists()
await letPasswordCheckRunInBackground()
})
beforeEach('fetch previous count', async function () {
previous = await getMetricFailure()
})
beforeEach('login', async function () {
await user.loginNoUpdate()
await letPasswordCheckRunInBackground()
})
it('should track the failure to collect a score', async function () {
const after = await getMetricFailure()
expect(after).to.equal(previous + 1)
})
})
describe('login attempt with weak password', function () {
beforeEach(function () {
user = new User()
// echo -n aLeakedPassword42 | sha1sum
MockHaveIBeenPwnedApi.addPasswordByHash(
'D1ABBDEEE70CBE8BBCE5D9D039C53C0CE91C0C16'
)
})
beforeEach('create the user', async function () {
await user.ensureUserExists()
await letPasswordCheckRunInBackground()
})
beforeEach('fetch previous counts', async function () {
previous = {
reUsed: await getMetricReUsed(),
unique: await getMetricUnique(),
failure: await getMetricFailure(),
}
})
beforeEach('login', async function () {
await user.loginWithEmailPassword(user.email, 'aLeakedPassword42')
await letPasswordCheckRunInBackground()
})
it('should not increment any counter', async function () {
expect(previous).to.deep.equal({
reUsed: await getMetricReUsed(),
unique: await getMetricUnique(),
failure: await getMetricFailure(),
})
})
})
describe('password reset with a weak password', function () {
beforeEach(function () {
user = new User()
// echo -n aLeakedPassword42 | sha1sum
MockHaveIBeenPwnedApi.addPasswordByHash(
'D1ABBDEEE70CBE8BBCE5D9D039C53C0CE91C0C16'
)
})
beforeEach('create the user', async function () {
await user.ensureUserExists()
await letPasswordCheckRunInBackground()
})
beforeEach('fetch previous count', async function () {
previous = await getMetricReUsed()
})
beforeEach('set password', async function () {
await resetPassword('aLeakedPassword42')
await letPasswordCheckRunInBackground()
})
it('should track the weak password', async function () {
const after = await getMetricReUsed()
expect(after).to.equal(previous + 1)
})
})
describe('password reset with a strong password', function () {
beforeEach(function () {
user = new User()
})
beforeEach('create the user', async function () {
await user.ensureUserExists()
await letPasswordCheckRunInBackground()
})
beforeEach('fetch previous count', async function () {
previous = await getMetricUnique()
})
beforeEach('set password', async function () {
await resetPassword('a-strong-new-password')
await letPasswordCheckRunInBackground()
})
it('should track the strong password', async function () {
const after = await getMetricUnique()
expect(after).to.equal(previous + 1)
})
})
})