Socket
Socket
Sign inDemoInstall

webpack-dev-middleware

Package Overview
Dependencies
83
Maintainers
4
Versions
113
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 6.1.1 to 6.1.2

14

dist/middleware.js

@@ -12,3 +12,4 @@ "use strict";

setStatusCode,
send
send,
sendError
} = require("./utils/compatibleAPI");

@@ -75,2 +76,4 @@ const ready = require("./utils/ready");

async function processRequest() {
/** @type {import("./utils/getFilenameFromUrl").Extra} */
const extra = {};
const filename = getFilenameFromUrl(context, /** @type {string} */req.url);

@@ -81,2 +84,11 @@ if (!filename) {

}
if (extra.errorCode) {
if (extra.errorCode === 403) {
context.logger.error(`Malicious path "${filename}".`);
}
sendError(req, res, extra.errorCode, {
modifyResponseData: context.options.modifyResponseData
});
return;
}
let {

@@ -83,0 +95,0 @@ headers

@@ -137,2 +137,144 @@ "use strict";

}
/**
* @template {ServerResponse} Response
* @param {Response} res
*/
function clearHeadersForResponse(res) {
const headers = getHeaderNames(res);
for (let i = 0; i < headers.length; i++) {
res.removeHeader(headers[i]);
}
}
/**
* @template {ServerResponse} Response
* @param {Response} res
* @param {Record<string, number | string | string[] | undefined>} headers
*/
function setHeadersForResponse(res, headers) {
const keys = Object.keys(headers);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = headers[key];
if (typeof value !== "undefined") {
setHeaderForResponse(res, key, value);
}
}
}
const matchHtmlRegExp = /["'&<>]/;
/**
* @param {string} string raw HTML
* @returns {string} escaped HTML
*/
function escapeHtml(string) {
const str = `${string}`;
const match = matchHtmlRegExp.exec(str);
if (!match) {
return str;
}
let escape;
let html = "";
let index = 0;
let lastIndex = 0;
for (({
index
} = match); index < str.length; index++) {
switch (str.charCodeAt(index)) {
// "
case 34:
escape = "&quot;";
break;
// &
case 38:
escape = "&amp;";
break;
// '
case 39:
escape = "&#39;";
break;
// <
case 60:
escape = "&lt;";
break;
// >
case 62:
escape = "&gt;";
break;
default:
// eslint-disable-next-line no-continue
continue;
}
if (lastIndex !== index) {
html += str.substring(lastIndex, index);
}
lastIndex = index + 1;
html += escape;
}
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}
/** @type {Record<number, string>} */
const statuses = {
400: "Bad Request",
403: "Forbidden",
404: "Not Found",
416: "Range Not Satisfiable",
500: "Internal Server Error"
};
/**
* @template {IncomingMessage} Request
* @template {ServerResponse} Response
* @typedef {Object} SendOptions send error options
* @property {Record<string, number | string | string[] | undefined>=} headers headers
* @property {import("../index").ModifyResponseData<Request, Response>=} modifyResponseData modify response data callback
* @property {import("../index").OutputFileSystem} outputFileSystem modify response data callback
*/
/**
* @template {IncomingMessage} Request
* @template {ServerResponse} Response
* @param {Request} req response
* @param {Response} res response
* @param {number} status status
* @param {Partial<SendOptions<Request, Response>>=} options options
* @returns {void}
*/
function sendError(req, res, status, options) {
const content = statuses[status] || String(status);
let document = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>${escapeHtml(content)}</pre>
</body>
</html>`;
// Clear existing headers
clearHeadersForResponse(res);
if (options && options.headers) {
setHeadersForResponse(res, options.headers);
}
// Send basic response
setStatusCode(res, status);
setHeaderForResponse(res, "Content-Type", "text/html; charset=utf-8");
setHeaderForResponse(res, "Content-Security-Policy", "default-src 'none'");
setHeaderForResponse(res, "X-Content-Type-Options", "nosniff");
let byteLength = Buffer.byteLength(document);
if (options && options.modifyResponseData) {
({
data: document,
byteLength
} = /** @type {{data: string, byteLength: number }} */
options.modifyResponseData(req, res, document, byteLength));
}
setHeaderForResponse(res, "Content-Length", byteLength);
res.end(document);
}
module.exports = {

@@ -144,3 +286,4 @@ getHeaderNames,

setStatusCode,
send
send,
sendError
};

@@ -16,4 +16,6 @@ "use strict";

/**
* @template T
* @param {Function} fn
* @param {{ cache?: Map<any, any> }} [cache]
* @param {{ cache?: Map<string, { data: T }> } | undefined} cache
* @param {(value: T) => T} callback
* @returns {any}

@@ -24,3 +26,3 @@ */

cache = new Map()
} = {}) => {
} = {}, callback) => {
/**

@@ -36,3 +38,4 @@ * @param {any} arguments_

}
const result = fn.apply(void 0, arguments_);
let result = fn.apply(void 0, arguments_);
result = callback(result);
cache.set(key, {

@@ -46,5 +49,32 @@ data: result

};
const memoizedParse = mem(parse);
// eslint-disable-next-line no-undefined
const memoizedParse = mem(parse, undefined, value => {
if (value.pathname) {
// eslint-disable-next-line no-param-reassign
value.pathname = decode(value.pathname);
}
return value;
});
const UP_PATH_REGEXP = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
/**
* @typedef {Object} Extra
* @property {import("fs").Stats=} stats
* @property {number=} errorCode
*/
/**
* decodeURIComponent.
*
* Allows V8 to only deoptimize this fn instead of all of send().
*
* @param {string} input
* @returns {string}
*/
function decode(input) {
return querystring.unescape(input);
}
/**
* @template {IncomingMessage} Request

@@ -54,5 +84,6 @@ * @template {ServerResponse} Response

* @param {string} url
* @param {Extra=} extra
* @returns {string | undefined}
*/
function getFilenameFromUrl(context, url) {
function getFilenameFromUrl(context, url, extra = {}) {
const {

@@ -62,3 +93,6 @@ options

const paths = getPaths(context);
/** @type {string | undefined} */
let foundFilename;
/** @type {URL} */
let urlObject;

@@ -75,3 +109,5 @@ try {

} of paths) {
/** @type {string | undefined} */
let filename;
/** @type {URL} */
let publicPathObject;

@@ -84,14 +120,31 @@ try {

}
if (urlObject.pathname && urlObject.pathname.startsWith(publicPathObject.pathname)) {
filename = outputPath;
const {
pathname
} = urlObject;
const {
pathname: publicPathPathname
} = publicPathObject;
if (pathname && pathname.startsWith(publicPathPathname)) {
// Null byte(s)
if (pathname.includes("\0")) {
// eslint-disable-next-line no-param-reassign
extra.errorCode = 400;
return;
}
// ".." is malicious
if (UP_PATH_REGEXP.test(path.normalize(`./${pathname}`))) {
// eslint-disable-next-line no-param-reassign
extra.errorCode = 403;
return;
}
// Strip the `pathname` property from the `publicPath` option from the start of requested url
// `/complex/foo.js` => `foo.js`
const pathname = urlObject.pathname.slice(publicPathObject.pathname.length);
if (pathname) {
filename = path.join(outputPath, querystring.unescape(pathname));
}
let fsStats;
// and add outputPath
// `foo.js` => `/home/user/my-project/dist/foo.js`
filename = path.join(outputPath, pathname.slice(publicPathPathname.length));
try {
fsStats = /** @type {import("fs").statSync} */
// eslint-disable-next-line no-param-reassign
extra.stats = /** @type {import("fs").statSync} */
context.outputFileSystem.statSync(filename);

@@ -102,10 +155,10 @@ } catch (_ignoreError) {

}
if (fsStats.isFile()) {
if (extra.stats.isFile()) {
foundFilename = filename;
break;
} else if (fsStats.isDirectory() && (typeof options.index === "undefined" || options.index)) {
} else if (extra.stats.isDirectory() && (typeof options.index === "undefined" || options.index)) {
const indexValue = typeof options.index === "undefined" || typeof options.index === "boolean" ? "index.html" : options.index;
filename = path.join(filename, indexValue);
try {
fsStats = /** @type {import("fs").statSync} */
extra.stats = /** @type {import("fs").statSync} */
context.outputFileSystem.statSync(filename);

@@ -116,3 +169,3 @@ } catch (__ignoreError) {

}
if (fsStats.isFile()) {
if (extra.stats.isFile()) {
foundFilename = filename;

@@ -119,0 +172,0 @@ break;

2

package.json
{
"name": "webpack-dev-middleware",
"version": "6.1.1",
"version": "6.1.2",
"description": "A development middleware for webpack",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -13,2 +13,24 @@ /// <reference types="node" />

};
/**
* send error options
*/
export type SendOptions<
Request_1 extends import("http").IncomingMessage,
Response_1 extends import("../index.js").ServerResponse
> = {
/**
* headers
*/
headers?: Record<string, number | string | string[] | undefined> | undefined;
/**
* modify response data callback
*/
modifyResponseData?:
| import("../index").ModifyResponseData<Request, Response>
| undefined;
/**
* modify response data callback
*/
outputFileSystem: import("../index").OutputFileSystem;
};
/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */

@@ -88,1 +110,27 @@ /** @typedef {import("../index.js").ServerResponse} ServerResponse */

): void;
/**
* @template {IncomingMessage} Request
* @template {ServerResponse} Response
* @typedef {Object} SendOptions send error options
* @property {Record<string, number | string | string[] | undefined>=} headers headers
* @property {import("../index").ModifyResponseData<Request, Response>=} modifyResponseData modify response data callback
* @property {import("../index").OutputFileSystem} outputFileSystem modify response data callback
*/
/**
* @template {IncomingMessage} Request
* @template {ServerResponse} Response
* @param {Request} req response
* @param {Response} res response
* @param {number} status status
* @param {Partial<SendOptions<Request, Response>>=} options options
* @returns {void}
*/
export function sendError<
Request_1 extends import("http").IncomingMessage,
Response_1 extends import("../index.js").ServerResponse
>(
req: Request_1,
res: Response_1,
status: number,
options?: Partial<SendOptions<Request_1, Response_1>> | undefined
): void;

@@ -8,2 +8,3 @@ /// <reference types="node" />

* @param {string} url
* @param {Extra=} extra
* @returns {string | undefined}

@@ -16,8 +17,13 @@ */

context: import("../index.js").Context<Request_1, Response_1>,
url: string
url: string,
extra?: Extra | undefined
): string | undefined;
declare namespace getFilenameFromUrl {
export { IncomingMessage, ServerResponse };
export { Extra, IncomingMessage, ServerResponse };
}
type Extra = {
stats?: import("fs").Stats | undefined;
errorCode?: number | undefined;
};
type IncomingMessage = import("../index.js").IncomingMessage;
type ServerResponse = import("../index.js").ServerResponse;
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc