fix #444: check bearer token on /hibp/notify
This commit is contained in:
Родитель
700dc29f4a
Коммит
bf446223ad
|
@ -53,3 +53,5 @@ HIBP_KANON_API_TOKEN=""
|
|||
HIBP_THROTTLE_DELAY=2000
|
||||
# Max number of times to try an HIBP request before throwing error
|
||||
HIBP_THROTTLE_MAX_TRIES=5
|
||||
# Authorization token for HIBP to present to /hibp/notify endpoint
|
||||
HIBP_NOTIFY_TOKEN="unsafe-default-token-for-dev"
|
||||
|
|
|
@ -33,6 +33,7 @@ const kEnvironmentVariables = [
|
|||
"HIBP_RELOAD_BREACHES_TIMER",
|
||||
"HIBP_THROTTLE_DELAY",
|
||||
"HIBP_THROTTLE_MAX_TRIES",
|
||||
"HIBP_NOTIFY_TOKEN",
|
||||
"DATABASE_URL",
|
||||
"SERVER_URL",
|
||||
"DELETE_UNVERIFIED_SUBSCRIBERS_TIMER",
|
||||
|
|
|
@ -13,6 +13,10 @@ const log = mozlog("controllers.hibp");
|
|||
|
||||
|
||||
async function notify (req, res) {
|
||||
if (!req.token || req.token !== AppConstants.HIBP_NOTIFY_TOKEN) {
|
||||
const errorMessage = "HIBP notify endpoint requires valid authorization token.";
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
if(!["breachName", "hashPrefix", "hashSuffixes"].every(req.body.hasOwnProperty, req.body)) {
|
||||
throw new Error("Breach notification requires breachName, hashPrefix, and hashSuffixes.");
|
||||
}
|
||||
|
|
|
@ -2883,6 +2883,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"express-bearer-token": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/express-bearer-token/-/express-bearer-token-2.2.0.tgz",
|
||||
"integrity": "sha512-w4A62k6modqSlE/CJzurj2YwisoVHWqrXiNs5P+CZD7MFORbcsOBbpjD3SGGKEW44R46Kps3QopaICE6p7dORw=="
|
||||
},
|
||||
"express-handlebars": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.0.0.tgz",
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"dompurify": "^1.0.4",
|
||||
"dotenv": "^5.0.1",
|
||||
"express": "^4.16.2",
|
||||
"express-bearer-token": "^2.2.0",
|
||||
"express-handlebars": "^3.0.0",
|
||||
"git-rev-sync": "^1.12.0",
|
||||
"got": "^8.3.1",
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
const express = require("express");
|
||||
const bodyParser = require("body-parser");
|
||||
|
||||
const bearerToken = require("express-bearer-token");
|
||||
|
||||
const {asyncMiddleware} = require("../middleware");
|
||||
const {notify, breaches} = require("../controllers/hibp");
|
||||
|
||||
|
@ -10,6 +12,7 @@ const {notify, breaches} = require("../controllers/hibp");
|
|||
const router = express.Router();
|
||||
const jsonParser = bodyParser.json();
|
||||
|
||||
router.use("/notify", bearerToken());
|
||||
router.post("/notify", jsonParser, asyncMiddleware(notify));
|
||||
router.get("/breaches", jsonParser, asyncMiddleware(breaches));
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
const AppConstants = require("../../app-constants");
|
||||
const HIBPLib = require("../../hibp");
|
||||
const hibp = require("../../controllers/hibp");
|
||||
const EmailUtils = require("../../email-utils");
|
||||
|
@ -9,6 +10,32 @@ const { testBreaches } = require("../test-breaches");
|
|||
require("../resetDB");
|
||||
|
||||
|
||||
test("notify POST without token should throw error", async() => {
|
||||
const testEmail = "victim@spoofattack.com";
|
||||
const testHash = sha1(testEmail);
|
||||
const testPrefix = testHash.slice(0, 6).toUpperCase();
|
||||
const testSuffix = testHash.slice(6).toUpperCase();
|
||||
|
||||
const mockRequest = { body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "SomeSensitiveBreach" } };
|
||||
const mockResponse = { status: jest.fn(), json: jest.fn() };
|
||||
|
||||
await expect(hibp.notify(mockRequest, mockResponse)).rejects.toThrow("HIBP notify endpoint requires valid authorization token.");
|
||||
});
|
||||
|
||||
|
||||
test("notify POST with invalid token should throw error", async() => {
|
||||
const testEmail = "victim@spoofattack.com";
|
||||
const testHash = sha1(testEmail);
|
||||
const testPrefix = testHash.slice(0, 6).toUpperCase();
|
||||
const testSuffix = testHash.slice(6).toUpperCase();
|
||||
|
||||
const mockRequest = { token: "token-that-doesnt-match-AppConstants", body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "SomeSensitiveBreach" } };
|
||||
const mockResponse = { status: jest.fn(), json: jest.fn() };
|
||||
|
||||
await expect(hibp.notify(mockRequest, mockResponse)).rejects.toThrow("HIBP notify endpoint requires valid authorization token.");
|
||||
});
|
||||
|
||||
|
||||
test("notify POST with breach, subscriber hash prefix and suffixes should call sendEmail and respond with 200", async () => {
|
||||
jest.mock("../../email-utils");
|
||||
EmailUtils.sendEmail = jest.fn();
|
||||
|
@ -19,7 +46,7 @@ test("notify POST with breach, subscriber hash prefix and suffixes should call s
|
|||
|
||||
HIBPLib.getUnsafeBreachesForEmail = jest.fn();
|
||||
|
||||
const mockRequest = { body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "Test" }, app: { locals: { breaches: testBreaches } } };
|
||||
const mockRequest = { token: AppConstants.HIBP_NOTIFY_TOKEN, body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "Test" }, app: { locals: { breaches: testBreaches } } };
|
||||
const mockResponse = { status: jest.fn(), json: jest.fn() };
|
||||
|
||||
await hibp.notify(mockRequest, mockResponse);
|
||||
|
@ -47,7 +74,7 @@ test("notify POST with unknown breach should throw error", async () => {
|
|||
const testPrefix = testHash.slice(0, 6).toUpperCase();
|
||||
const testSuffix = testHash.slice(6).toUpperCase();
|
||||
|
||||
const mockRequest = { body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "Test" }, app: { locals: { breaches: [] } } };
|
||||
const mockRequest = { token: AppConstants.HIBP_NOTIFY_TOKEN, body: { hashPrefix: testPrefix, hashSuffixes: [testSuffix], breachName: "Test" }, app: { locals: { breaches: [] } } };
|
||||
const mockResponse = { status: jest.fn(), json: jest.fn() };
|
||||
|
||||
await expect(hibp.notify(mockRequest, mockResponse)).rejects.toThrow("Unrecognized breach: test");
|
||||
|
|
Загрузка…
Ссылка в новой задаче