You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 7-8.RSVP
Socket
Socket
Sign inDemoInstall

mockttp

Package Overview
Dependencies
Maintainers
1
Versions
122
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.3 to 1.0.4

4

custom-typings/node-type-extensions.d.ts

@@ -90,2 +90,6 @@ // There's a few places where we attach extra data to some node objects during

}
// A constant symbol used in-band in HTTP/2 header objects to list the headers
// that shouldn't/weren't automatically compressed. Only defined in Node 15+.
export const sensitiveHeaders: Symbol | undefined;
}

23

dist/rules/handlers.js

@@ -18,2 +18,3 @@ "use strict";

const http = require("http");
const http2 = require("http2");
const https = require("https");

@@ -352,3 +353,3 @@ const h2Client = require("http2-wrapper");

const invalidHeaders = _(modifiedHeaders)
.pickBy((value, name) => name.startsWith(':') &&
.pickBy((value, name) => name.toString().startsWith(':') &&
// We allow returning a preexisting header value - that's ignored

@@ -450,5 +451,3 @@ // silently, so that mutating & returning the provided headers is always safe.

const completedRequest = yield request_utils_1.waitForCompletedRequest(clientReq);
const modifiedReq = yield this.beforeRequest(Object.assign(completedRequest, {
headers: _.clone(completedRequest.headers) // Clone headers so we can ignore mutations
}));
const modifiedReq = yield this.beforeRequest(Object.assign(Object.assign({}, completedRequest), { headers: _.clone(completedRequest.headers) }));
if (modifiedReq.response) {

@@ -466,3 +465,3 @@ // The callback has provided a full response: don't passthrough at all, just use it.

headersManuallyModified = !!modifiedReq.headers;
validateCustomHeaders(clientReq.headers, modifiedReq.headers, OVERRIDABLE_REQUEST_PSEUDOHEADERS // These are handled by getCorrectPseudoheaders above
validateCustomHeaders(completedRequest.headers, modifiedReq.headers, OVERRIDABLE_REQUEST_PSEUDOHEADERS // These are handled by getCorrectPseudoheaders above
);

@@ -529,3 +528,3 @@ if (modifiedReq.json) {

// We drop all incoming pseudoheaders, and regenerate them (except legally modified ones)
headers = _.pickBy(headers, (value, key) => !key.startsWith(':') ||
headers = _.pickBy(headers, (value, key) => !key.toString().startsWith(':') ||
(headersManuallyModified &&

@@ -558,2 +557,3 @@ OVERRIDABLE_REQUEST_PSEUDOHEADERS.includes(key)));

body = yield request_utils_1.streamToBuffer(serverRes);
const cleanHeaders = request_utils_1.cleanUpHeaders(serverHeaders);
modifiedRes = yield this.beforeResponse({

@@ -563,6 +563,6 @@ id: clientReq.id,

statusMessage: serverRes.statusMessage,
headers: _.clone(serverHeaders),
headers: _.clone(cleanHeaders),
body: request_utils_1.buildBodyReader(body, serverHeaders)
});
validateCustomHeaders(serverHeaders, modifiedRes.headers);
validateCustomHeaders(cleanHeaders, modifiedRes.headers);
serverStatusCode = modifiedRes.statusCode ||

@@ -595,6 +595,7 @@ modifiedRes.status ||

const headerValue = serverHeaders[header];
if (headerValue === undefined)
if (headerValue === undefined ||
header === http2.sensitiveHeaders ||
header === ':status' // H2 status gets set by writeHead below
)
return;
if (header === ':status')
return; // H2 status gets set by writeHead below
try {

@@ -601,0 +602,0 @@ clientRes.setHeader(header, headerValue);

@@ -90,4 +90,5 @@ "use strict";

const host = req.headers[':authority'] || req.headers['host'];
const absoluteUrl = `${req.protocol}://${host}${req.path}`;
if (!req.headers[':path']) {
req.url = new url.URL(req.url, `${req.protocol}://${host}`).toString();
req.url = new url.URL(absoluteUrl).toString();
}

@@ -99,3 +100,3 @@ else {

Object.defineProperty(req, 'url', {
value: new url.URL(req.url, `${req.protocol}://${host}`).toString()
value: new url.URL(absoluteUrl).toString()
});

@@ -102,0 +103,0 @@ }

@@ -31,4 +31,22 @@ /**

}) => (req: http.IncomingMessage, _res: http.ServerResponse, next: () => void) => void;
/**
* Translate from internal header representations (basically Node's header representations) to a
* mildly more consistent & simplified model that we expose externally: numbers as strings, and
* no sensitiveHeaders symbol for HTTP/2.
*/
export declare function cleanUpHeaders(headers: Headers): {};
/**
* Build an initiated request: the external representation of a request
* that's just started.
*/
export declare function buildInitiatedRequest(request: OngoingRequest): InitiatedRequest;
/**
* Build an aborted request: the external representation of a request
* that's been aborted.
*/
export declare function buildAbortedRequest(request: OngoingRequest): InitiatedRequest;
/**
* Build a completed request: the external representation of a request
* that's been completely received (but not necessarily replied to).
*/
export declare function waitForCompletedRequest(request: OngoingRequest): Promise<CompletedRequest>;

@@ -38,2 +56,6 @@ export declare function trackResponse(response: http.ServerResponse, timingEvents: TimingEvents, tags: string[], options: {

}): OngoingResponse;
/**
* Build a completed response: the external representation of a response
* that's been completely written out and sent back to the client.
*/
export declare function waitForCompletedResponse(response: OngoingResponse): Promise<CompletedResponse>;

@@ -40,0 +62,0 @@ export declare function tryToParseHttp(input: Buffer, socket: net.Socket): PartiallyParsedHttpRequest;

@@ -17,2 +17,3 @@ "use strict";

const tls_1 = require("tls");
const http2 = require("http2");
const stream = require("stream");

@@ -77,3 +78,3 @@ const querystring = require("querystring");

const h1Headers = _.omitBy(h2Headers, (_value, key) => {
return key.startsWith(':');
return key === http2.sensitiveHeaders || key.toString().startsWith(':');
});

@@ -264,9 +265,27 @@ if (!h1Headers['host'] && h2Headers[':authority']) {

};
/**
* Translate from internal header representations (basically Node's header representations) to a
* mildly more consistent & simplified model that we expose externally: numbers as strings, and
* no sensitiveHeaders symbol for HTTP/2.
*/
function cleanUpHeaders(headers) {
return _.mapValues(_.omit(headers, ...(http2.sensitiveHeaders ? [http2.sensitiveHeaders] : [])), (headerValue) => _.isNumber(headerValue) ? headerValue.toString() : headerValue);
}
exports.cleanUpHeaders = cleanUpHeaders;
/**
* Build an initiated request: the external representation of a request
* that's just started.
*/
function buildInitiatedRequest(request) {
return Object.assign(Object.assign({}, _.pick(request, 'id', 'matchedRuleId', 'protocol', 'httpVersion', 'method', 'url', 'path', 'hostname', 'headers', 'tags')), { timingEvents: request.timingEvents });
return Object.assign(Object.assign({}, _.pick(request, 'id', 'matchedRuleId', 'protocol', 'httpVersion', 'method', 'url', 'path', 'hostname', 'headers', 'tags')), { headers: cleanUpHeaders(request.headers), timingEvents: request.timingEvents });
}
exports.buildInitiatedRequest = buildInitiatedRequest;
/**
* Build an aborted request: the external representation of a request
* that's been aborted.
*/
function buildAbortedRequest(request) {
const requestData = buildInitiatedRequest(request);
return Object.assign(requestData, {
headers: cleanUpHeaders(request.headers),
// Exists for backward compat: really Abort events should have no body at all

@@ -277,2 +296,6 @@ body: exports.buildBodyReader(Buffer.alloc(0), {})

exports.buildAbortedRequest = buildAbortedRequest;
/**
* Build a completed request: the external representation of a request
* that's been completely received (but not necessarily replied to).
*/
function waitForCompletedRequest(request) {

@@ -283,3 +306,3 @@ return __awaiter(this, void 0, void 0, function* () {

const requestData = buildInitiatedRequest(request);
return Object.assign(requestData, { body });
return Object.assign(requestData, { body, headers: cleanUpHeaders(request.headers) });
});

@@ -334,2 +357,6 @@ }

exports.trackResponse = trackResponse;
/**
* Build a completed response: the external representation of a response
* that's been completely written out and sent back to the client.
*/
function waitForCompletedResponse(response) {

@@ -346,7 +373,3 @@ return __awaiter(this, void 0, void 0, function* () {

]).assign({
headers: _.mapValues(response.getHeaders(), (headerValue) => {
return _.isNumber(headerValue)
? headerValue.toString() // HTTP :status sneaks in as a number
: headerValue;
}),
headers: cleanUpHeaders(response.getHeaders()),
body: body

@@ -353,0 +376,0 @@ }).valueOf();

{
"name": "mockttp",
"version": "1.0.3",
"version": "1.0.4",
"description": "Mock HTTP server for testing HTTP clients and stubbing webservices",

@@ -31,3 +31,4 @@ "main": "dist/main.js",

"build:src": "tsc && cp src/standalone/schema.gql dist/standalone/schema.gql && chmod +x ./dist/standalone/standalone-bin.js",
"build:doc": "typedoc --ignoreCompilerErrors --excludeExternals --readme none --exclude '**/+(main-browser|standalone-bin).ts' --out typedoc/ src/ custom-typings/",
"build:doc": "typedoc --ignoreCompilerErrors --excludeExternals --readme none --exclude '**/+(main-browser|standalone-bin).ts' --out typedoc/ src/ custom-typings/ ",
"postbuild:doc": "touch ./typedoc/.nojekyll",
"test": "npm run build && npm run test:node && npm run test:browser",

@@ -38,3 +39,3 @@ "test:node": "NODE_EXTRA_CA_CERTS=./test/fixtures/test-ca.pem mocha -r ts-node/register 'test/**/*.spec.ts'",

"standalone": "npm run build && node -e 'require(\".\").getStandalone({ debug: true }).start()'",
"ci": "npm run test && catch-uncommitted",
"ci-tests": "npm run test && catch-uncommitted",
"prepack": "npm run build"

@@ -41,0 +42,0 @@ },

@@ -1,2 +0,2 @@

# Mockttp [![Travis Build Status](https://img.shields.io/travis/httptoolkit/mockttp.svg)](https://travis-ci.org/httptoolkit/mockttp) [![Available on NPM](https://img.shields.io/npm/v/mockttp.svg)](https://npmjs.com/package/mockttp) [![Try Mockttp on RunKit](https://badge.runkitcdn.com/mockttp.svg)](https://npm.runkit.com/mockttp)
# Mockttp [![Build Status](https://github.com/httptoolkit/mockttp/workflows/CI/badge.svg)](https://github.com/httptoolkit/mockttp/actions) [![Available on NPM](https://img.shields.io/npm/v/mockttp.svg)](https://npmjs.com/package/mockttp) [![Try Mockttp on RunKit](https://badge.runkitcdn.com/mockttp.svg)](https://npm.runkit.com/mockttp)

@@ -3,0 +3,0 @@ > _Part of [HTTP Toolkit](https://httptoolkit.tech): powerful tools for building, testing & debugging HTTP(S)_

@@ -9,2 +9,3 @@ /**

import http = require('http');
import http2 = require('http2');
import https = require('https');

@@ -27,3 +28,4 @@ import * as h2Client from 'http2-wrapper';

h2HeadersToH1,
isAbsoluteUrl
isAbsoluteUrl,
cleanUpHeaders
} from '../util/request-utils';

@@ -580,3 +582,3 @@ import { isLocalPortActive } from '../util/socket-util';

.pickBy((value, name) =>
name.startsWith(':') &&
name.toString().startsWith(':') &&
// We allow returning a preexisting header value - that's ignored

@@ -706,7 +708,6 @@ // silently, so that mutating & returning the provided headers is always safe.

const completedRequest = await waitForCompletedRequest(clientReq);
const modifiedReq = await this.beforeRequest(
Object.assign(completedRequest, {
headers: _.clone(completedRequest.headers) // Clone headers so we can ignore mutations
})
);
const modifiedReq = await this.beforeRequest({
...completedRequest,
headers: _.clone(completedRequest.headers)
});

@@ -732,3 +733,3 @@ if (modifiedReq.response) {

validateCustomHeaders(
clientReq.headers,
completedRequest.headers,
modifiedReq.headers,

@@ -812,3 +813,3 @@ OVERRIDABLE_REQUEST_PSEUDOHEADERS // These are handled by getCorrectPseudoheaders above

headers = _.pickBy(headers, (value, key) =>
!key.startsWith(':') ||
!key.toString().startsWith(':') ||
(headersManuallyModified &&

@@ -852,2 +853,3 @@ OVERRIDABLE_REQUEST_PSEUDOHEADERS.includes(key as any)

body = await streamToBuffer(serverRes);
const cleanHeaders = cleanUpHeaders(serverHeaders);

@@ -858,7 +860,7 @@ modifiedRes = await this.beforeResponse({

statusMessage: serverRes.statusMessage,
headers: _.clone(serverHeaders),
headers: _.clone(cleanHeaders),
body: buildBodyReader(body, serverHeaders)
});
validateCustomHeaders(serverHeaders, modifiedRes.headers);
validateCustomHeaders(cleanHeaders, modifiedRes.headers);

@@ -899,4 +901,7 @@ serverStatusCode = modifiedRes.statusCode ||

const headerValue = serverHeaders[header];
if (headerValue === undefined) return;
if (header === ':status') return; // H2 status gets set by writeHead below
if (
headerValue === undefined ||
(header as unknown) === http2.sensitiveHeaders ||
header === ':status' // H2 status gets set by writeHead below
) return;

@@ -903,0 +908,0 @@ try {

@@ -112,6 +112,8 @@ /**

req.path = req.url;
const host = req.headers[':authority'] || req.headers['host'];
const absoluteUrl = `${req.protocol}://${host}${req.path}`;
if (!req.headers[':path']) {
req.url = new url.URL(req.url!, `${req.protocol}://${host}`).toString();
req.url = new url.URL(absoluteUrl).toString();
} else {

@@ -122,3 +124,3 @@ // Node's HTTP/2 compat logic maps .url to headers[':path']. We want them to

Object.defineProperty(req, 'url', {
value: new url.URL(req.url!, `${req.protocol}://${host}`).toString()
value: new url.URL(absoluteUrl).toString()
});

@@ -125,0 +127,0 @@ }

@@ -99,4 +99,4 @@ /**

export function h2HeadersToH1(h2Headers: Headers): Headers {
const h1Headers = _.omitBy(h2Headers, (_value, key) => {
return key.startsWith(':')
const h1Headers = _.omitBy(h2Headers, (_value, key: string | Symbol) => {
return key === http2.sensitiveHeaders || key.toString().startsWith(':')
});

@@ -321,2 +321,19 @@

/**
* Translate from internal header representations (basically Node's header representations) to a
* mildly more consistent & simplified model that we expose externally: numbers as strings, and
* no sensitiveHeaders symbol for HTTP/2.
*/
export function cleanUpHeaders(headers: Headers) {
return _.mapValues(
_.omit(headers, ...(http2.sensitiveHeaders ? [http2.sensitiveHeaders as any] : [])),
(headerValue: undefined | string | string[] | number) =>
_.isNumber(headerValue) ? headerValue.toString() : headerValue
);
}
/**
* Build an initiated request: the external representation of a request
* that's just started.
*/
export function buildInitiatedRequest(request: OngoingRequest): InitiatedRequest {

@@ -336,2 +353,3 @@ return {

),
headers: cleanUpHeaders(request.headers),
timingEvents: request.timingEvents

@@ -341,5 +359,10 @@ };

/**
* Build an aborted request: the external representation of a request
* that's been aborted.
*/
export function buildAbortedRequest(request: OngoingRequest): InitiatedRequest {
const requestData = buildInitiatedRequest(request);
return Object.assign(requestData, {
headers: cleanUpHeaders(request.headers),
// Exists for backward compat: really Abort events should have no body at all

@@ -350,2 +373,6 @@ body: buildBodyReader(Buffer.alloc(0), {})

/**
* Build a completed request: the external representation of a request
* that's been completely received (but not necessarily replied to).
*/
export async function waitForCompletedRequest(request: OngoingRequest): Promise<CompletedRequest> {

@@ -356,3 +383,3 @@ const body = await waitForBody(request.body, request.headers);

const requestData = buildInitiatedRequest(request);
return Object.assign(requestData, { body });
return Object.assign(requestData, { body, headers: cleanUpHeaders(request.headers) });
}

@@ -427,2 +454,6 @@

/**
* Build a completed response: the external representation of a response
* that's been completely written out and sent back to the client.
*/
export async function waitForCompletedResponse(response: OngoingResponse): Promise<CompletedResponse> {

@@ -439,7 +470,3 @@ const body = await waitForBody(response.body, response.getHeaders());

]).assign({
headers: _.mapValues(response.getHeaders(), (headerValue) => {
return _.isNumber(headerValue)
? headerValue.toString() // HTTP :status sneaks in as a number
: headerValue;
}) as Headers,
headers: cleanUpHeaders(response.getHeaders()),
body: body

@@ -446,0 +473,0 @@ }).valueOf();

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc