exact.js 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.checkExact = checkExact;
  4. const base_1 = require("../base");
  5. const chain_1 = require("../chain");
  6. const context_1 = require("../context");
  7. const field_selection_1 = require("../field-selection");
  8. const utils_1 = require("../utils");
  9. /**
  10. * Checks whether the request contains exactly only those fields that have been validated.
  11. *
  12. * Unknown fields, if found, will generate an error of type `unknown_fields`.
  13. *
  14. * @param chains either a single chain, an array of chains, or a mixed array of chains and array of chains.
  15. * This means that all of the below are valid:
  16. * ```
  17. * checkExact(check('foo'))
  18. * checkExact([check('foo'), check('bar')])
  19. * checkExact([check('foo'), check('bar')])
  20. * checkExact(checkSchema({ ... }))
  21. * checkExact([checkSchema({ ... }), check('foo')])
  22. * ```
  23. * @param opts
  24. */
  25. function checkExact(chains, opts) {
  26. // Don't include all locations by default. Browsers will add cookies and headers that the user
  27. // might not want to validate, which would be a footgun.
  28. const locations = opts?.locations || ['body', 'params', 'query'];
  29. const chainsArr = Array.isArray(chains) ? chains.flat() : chains ? [chains] : [];
  30. const run = async (req) => {
  31. const internalReq = req;
  32. const fieldsByLocation = new Map();
  33. await (0, utils_1.runAllChains)(req, chainsArr);
  34. // The chains above will have added contexts to the request
  35. (internalReq[base_1.contextsKey] || []).forEach(context => {
  36. context.locations.forEach(location => {
  37. if (!locations.includes(location)) {
  38. return;
  39. }
  40. const locationFields = fieldsByLocation.get(location) || [];
  41. locationFields.push(...context.fields);
  42. fieldsByLocation.set(location, locationFields);
  43. });
  44. });
  45. // when none of the chains matched anything, then everything is unknown.
  46. if (!fieldsByLocation.size) {
  47. locations.forEach(location => fieldsByLocation.set(location, []));
  48. }
  49. let unknownFields = [];
  50. for (const [location, fields] of fieldsByLocation.entries()) {
  51. unknownFields = unknownFields.concat((0, field_selection_1.selectUnknownFields)(req, fields, [location]));
  52. }
  53. const context = new context_1.Context([], [], [], false, false);
  54. if (unknownFields.length) {
  55. context.addError({
  56. type: 'unknown_fields',
  57. req,
  58. message: opts?.message || 'Unknown field(s)',
  59. fields: unknownFields,
  60. });
  61. }
  62. internalReq[base_1.contextsKey] = internalReq[base_1.contextsKey] || [];
  63. internalReq[base_1.contextsKey].push(context);
  64. return new chain_1.ResultWithContextImpl(context);
  65. };
  66. const middleware = (req, _res, next) => run(req).then(() => next(), next);
  67. return Object.assign(middleware, { run });
  68. }