RestrictionsPlugin.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Ivan Kopeykin @vankop
  4. */
  5. "use strict";
  6. const { isInside, normalize } = require("./util/path");
  7. /** @typedef {import("./Resolver")} Resolver */
  8. /** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
  9. /**
  10. * @typedef {object} PathRestriction
  11. * @property {"path"} type type of the restriction
  12. * @property {string} rule normalized path the request has to be inside of
  13. */
  14. /**
  15. * @typedef {object} RegExpRestriction
  16. * @property {"regexp"} type type of the restriction
  17. * @property {RegExp} rule pattern the request has to match
  18. */
  19. /** @typedef {PathRestriction | RegExpRestriction} Restriction */
  20. module.exports = class RestrictionsPlugin {
  21. /**
  22. * @param {string | ResolveStepHook} source source
  23. * @param {Set<string | RegExp>} restrictions restrictions
  24. */
  25. constructor(source, restrictions) {
  26. this.source = source;
  27. this.restrictions = restrictions;
  28. // Restrictions never change, so bringing them into the shape requests
  29. // arrive in is done once here instead of on every request.
  30. /** @type {Restriction[]} */
  31. this._restrictions = [];
  32. for (const rule of restrictions) {
  33. this._restrictions.push(
  34. typeof rule === "string"
  35. ? { type: "path", rule: normalize(rule) }
  36. : { type: "regexp", rule },
  37. );
  38. }
  39. }
  40. /**
  41. * @param {Resolver} resolver the resolver
  42. * @returns {void}
  43. */
  44. apply(resolver) {
  45. resolver
  46. .getHook(this.source)
  47. .tapAsync("RestrictionsPlugin", (request, resolveContext, callback) => {
  48. if (typeof request.path === "string") {
  49. const { path } = request;
  50. for (const restriction of this._restrictions) {
  51. if (restriction.type === "path") {
  52. if (isInside(restriction.rule, path)) continue;
  53. if (resolveContext.log) {
  54. resolveContext.log(
  55. `${path} is not inside of the restriction ${restriction.rule}`,
  56. );
  57. }
  58. } else {
  59. if (restriction.rule.test(path)) continue;
  60. if (resolveContext.log) {
  61. resolveContext.log(
  62. `${path} doesn't match the restriction ${restriction.rule}`,
  63. );
  64. }
  65. }
  66. // Target existed (FileExistsPlugin already passed) but is
  67. // outside the jail; signal ExportsFieldPlugin to fall back.
  68. if (request.__restrictionsMarker) {
  69. request.__restrictionsMarker.blocked = true;
  70. }
  71. return callback(null, null);
  72. }
  73. }
  74. callback();
  75. });
  76. }
  77. };