UNPKG

@govuk-pay/pay-js-commons

Version:

Reusable js scripts for GOV.UK Pay Node.js projects

88 lines (87 loc) 3.66 kB
"use strict"; var _require = require('express-rate-limit'), rateLimit = _require.rateLimit; var hasSubstr = function hasSubstr(lookUpStrings, targetString) { return lookUpStrings.some(function (str) { return targetString.toLowerCase().includes(str.toLowerCase()); }); }; var rateLimitMiddleware = rateLimit({ windowMs: 5 * 60 * 1000, // 5 minutes limit: 10, // Limit each IP to 10 requests per window standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header legacyHeaders: false // Disable the `X-RateLimit-*` headers. }); var requestParseMiddleware = function requestParseMiddleware(maxPayloadBytes, express) { return function (req, res, next) { if (req.is('application/json') || req.is('application/reports+json') || req.is('application/csp-report')) { express.json({ type: ['application/json', 'application/reports+json', 'application/csp-report'], limit: maxPayloadBytes // limit body payload to maxPayloadBytes, validated prior to parsing })(req, res, next); } else { return res.status(400).end(); } }; }; var detectErrorsMiddleware = function detectErrorsMiddleware(logger) { return function (err, req, res, next) { if (err) { if (err.type === 'entity.too.large') logger.info('CSP violation request payload exceeds maximum size'); if (err.type === 'entity.parse.failed') logger.info('CSP violation request payload did not match expected content type'); return res.status(400).end(); } next(); }; }; var captureEventMiddleware = function captureEventMiddleware(ignoredStrings, logger, Sentry) { return function (req, res) { var reports = undefined; if (Array.isArray(req.body) && req.body.length > 0) { reports = req.body.filter(function (report) { return report.type === 'csp-violation'; }); // new style reporting-api, can be batched into multiple reports } else if (req.body['csp-report'] !== undefined) { reports = [{ body: req.body['csp-report'] }]; // old style report-uri } var userAgent = req.headers['user-agent']; if (reports !== undefined) { reports.forEach(function (report) { var _body$blockedUri, _body$violatedDirect; var body = report.body; var blockedUri = (_body$blockedUri = body['blocked-uri']) !== null && _body$blockedUri !== void 0 ? _body$blockedUri : body['blockedURL']; var violatedDirective = (_body$violatedDirect = body['violated-directive']) !== null && _body$violatedDirect !== void 0 ? _body$violatedDirect : body['effectiveDirective']; // https://www.w3.org/TR/CSP3/#dom-securitypolicyviolationevent-violateddirective if (violatedDirective === undefined || blockedUri === undefined) { logger.info('CSP violation report is invalid'); return res.status(400).end(); } else { if (hasSubstr(ignoredStrings, blockedUri)) return res.status(204).end(); Sentry.captureEvent({ message: "Blocked ".concat(violatedDirective, " from ").concat(blockedUri), level: 'warning', extra: { cspReport: body, userAgent: userAgent } }); } }); } else { logger.info('CSP violation report missing'); return res.status(400).end(); } return res.status(204).end(); }; }; module.exports = { hasSubstr: hasSubstr, rateLimitMiddleware: rateLimitMiddleware, captureEventMiddleware: captureEventMiddleware, requestParseMiddleware: requestParseMiddleware, detectErrorsMiddleware: detectErrorsMiddleware };