@govuk-pay/pay-js-commons
Version:
Reusable js scripts for GOV.UK Pay Node.js projects
88 lines (87 loc) • 3.66 kB
JavaScript
;
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
};