Skip to content

Commit

Permalink
Merge pull request #132 from ashishmadeti/main
Browse files Browse the repository at this point in the history
Allow setting `ignoreHostHttpsErrors` to a boolean value
  • Loading branch information
pimterry committed Feb 24, 2023
2 parents 009f22c + d448ad8 commit a9509b3
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 28 deletions.
24 changes: 23 additions & 1 deletion src/rules/passthrough-handling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,26 @@ export function getContentLengthAfterModification(
}

return lengthOverride;
}
}

// Function to check if we should skip https errors for the current hostname and port,
// based on the given config
export function shouldUseStrictHttps(
hostname: string,
port: string,
ignoreHostHttpsErrors: string[] | boolean
) {
let skipHttpsErrors = false;

if (ignoreHostHttpsErrors === true) {
// Ignore cert errors if `ignoreHostHttpsErrors` is set to true, or
skipHttpsErrors = true;
} else if (Array.isArray(ignoreHostHttpsErrors) && (
// if the whole hostname or host+port is whitelisted
_.includes(ignoreHostHttpsErrors, hostname) ||
_.includes(ignoreHostHttpsErrors, `${hostname}:${port}`)
)) {
skipHttpsErrors = true;
}
return !skipHttpsErrors;
}
16 changes: 9 additions & 7 deletions src/rules/requests/request-handler-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,11 @@ export interface PassThroughHandlerOptions {

/**
* A list of hostnames for which server certificate and TLS version errors
* should be ignored (none, by default).
* should be ignored (none, by default). If set to 'true', ignore HTTPS errors
* for all hosts. (WARNING: Use this at your own risk. This can open your
* application to MITM attacks)
*/
ignoreHostHttpsErrors?: string[];
ignoreHostHttpsErrors?: string[] | boolean;

/**
* An array of additional certificates, which should be trusted as certificate
Expand Down Expand Up @@ -746,7 +748,7 @@ export interface SerializedPassThroughData {
forwardToLocation?: string;
forwarding?: ForwardingOptions;
proxyConfig?: SerializedProxyConfig;
ignoreHostCertificateErrors?: string[]; // Doesn't match option name, backward compat
ignoreHostCertificateErrors?: string[] | boolean; // Doesn't match option name, backward compat
extraCACertificates?: Array<{ cert: string } | { certPath: string }>;
clientCertificateHostMap?: { [host: string]: { pfx: string, passphrase?: string } };
lookupOptions?: PassThroughLookupOptions;
Expand Down Expand Up @@ -800,7 +802,7 @@ export class PassThroughHandlerDefinition extends Serializable implements Reques

public readonly forwarding?: ForwardingOptions;

public readonly ignoreHostHttpsErrors: string[] = [];
public readonly ignoreHostHttpsErrors: string[] | boolean = [];
public readonly clientCertificateHostMap: {
[host: string]: { pfx: Buffer, passphrase?: string }
};
Expand Down Expand Up @@ -846,8 +848,8 @@ export class PassThroughHandlerDefinition extends Serializable implements Reques
this.forwarding = forwarding;

this.ignoreHostHttpsErrors = options.ignoreHostHttpsErrors || [];
if (!Array.isArray(this.ignoreHostHttpsErrors)) {
throw new Error("ignoreHostHttpsErrors must be an array");
if (!Array.isArray(this.ignoreHostHttpsErrors) && typeof this.ignoreHostHttpsErrors !== 'boolean') {
throw new Error("ignoreHostHttpsErrors must be an array or a boolean");
}

this.lookupOptions = options.lookupOptions;
Expand Down Expand Up @@ -1102,4 +1104,4 @@ export const HandlerDefinitionLookup = {
'reset-connection': ResetConnectionHandlerDefinition,
'timeout': TimeoutHandlerDefinition,
'json-rpc-response': JsonRpcResponseHandlerDefinition
}
}
11 changes: 6 additions & 5 deletions src/rules/requests/request-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ import {
getH2HeadersAfterModification,
OVERRIDABLE_REQUEST_PSEUDOHEADERS,
buildOverriddenBody,
UPSTREAM_TLS_OPTIONS
UPSTREAM_TLS_OPTIONS,
shouldUseStrictHttps
} from '../passthrough-handling';

import {
Expand Down Expand Up @@ -632,9 +633,9 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {

const hostWithPort = `${hostname}:${port}`

// Ignore cert errors if the host+port or whole hostname is whitelisted
const strictHttpsChecks = !_.includes(this.ignoreHostHttpsErrors, hostname) &&
!_.includes(this.ignoreHostHttpsErrors, hostWithPort);
const strictHttpsChecks = shouldUseStrictHttps(
hostname as string, port as string, this.ignoreHostHttpsErrors
);

// Use a client cert if it's listed for the host+port or whole hostname
const clientCert = this.clientCertificateHostMap[hostWithPort] ||
Expand Down Expand Up @@ -1211,4 +1212,4 @@ export const HandlerLookup: typeof HandlerDefinitionLookup = {
'reset-connection': ResetConnectionHandler,
'timeout': TimeoutHandler,
'json-rpc-response': JsonRpcResponseHandler
}
}
19 changes: 10 additions & 9 deletions src/rules/websockets/websocket-handler-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ export interface PassThroughWebSocketHandlerOptions {

/**
* A list of hostnames for which server certificate and TLS version errors
* should be ignored (none, by default).
* should be ignored (none, by default). If set to 'true', ignore HTTPS errors
* for all hosts. (WARNING: Use this at your own risk. This can open your
* application to MITM attacks)
*/
ignoreHostHttpsErrors?: string[];
ignoreHostHttpsErrors?: string[] | boolean;

/**
* An array of additional certificates, which should be trusted as certificate
Expand Down Expand Up @@ -99,7 +101,7 @@ export interface SerializedPassThroughWebSocketData {
forwarding?: ForwardingOptions;
lookupOptions?: PassThroughLookupOptions;
proxyConfig?: SerializedProxyConfig;
ignoreHostCertificateErrors?: string[]; // Doesn't match option name, backward compat
ignoreHostCertificateErrors?: string[] | boolean; // Doesn't match option name, backward compat
extraCACertificates?: Array<{ cert: string } | { certPath: string }>;
}

Expand All @@ -111,17 +113,16 @@ export class PassThroughWebSocketHandlerDefinition extends Serializable implemen
public readonly proxyConfig?: ProxyConfig;

public readonly forwarding?: ForwardingOptions;
public readonly ignoreHostHttpsErrors: string[] = [];
public readonly ignoreHostHttpsErrors: string[] | boolean = [];

public readonly extraCACertificates: Array<{ cert: string | Buffer } | { certPath: string }> = [];

constructor(options: PassThroughWebSocketHandlerOptions = {}) {
super();

this.ignoreHostHttpsErrors = options.ignoreHostHttpsErrors ||
[];
if (!Array.isArray(this.ignoreHostHttpsErrors)) {
throw new Error("ignoreHostHttpsErrors must be an array");
this.ignoreHostHttpsErrors = options.ignoreHostHttpsErrors || [];
if (!Array.isArray(this.ignoreHostHttpsErrors) && typeof this.ignoreHostHttpsErrors !== 'boolean') {
throw new Error("ignoreHostHttpsErrors must be an array or a boolean");
}

// If a location is provided, and it's not a bare hostname, it must be parseable
Expand Down Expand Up @@ -227,4 +228,4 @@ export const WsHandlerDefinitionLookup = {
'close-connection': CloseConnectionHandlerDefinition,
'reset-connection': ResetConnectionHandlerDefinition,
'timeout': TimeoutHandlerDefinition
};
};
15 changes: 10 additions & 5 deletions src/rules/websockets/websocket-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ import { MaybePromise } from '../../util/type-utils';
import { getAgent } from '../http-agents';
import { ProxySettingSource } from '../proxy-config';
import { assertParamDereferenced, RuleParameters } from '../rule-parameters';
import { UPSTREAM_TLS_OPTIONS } from '../passthrough-handling';
import {
UPSTREAM_TLS_OPTIONS,
shouldUseStrictHttps
} from '../passthrough-handling';

import {
EchoWebSocketHandlerDefinition,
Expand Down Expand Up @@ -311,10 +314,12 @@ export class PassThroughWebSocketHandler extends PassThroughWebSocketHandlerDefi
incomingSocket: net.Socket,
head: Buffer
) {
// Skip cert checks if the host or host+port are whitelisted
const parsedUrl = url.parse(wsUrl);
const checkServerCertificate = !_.includes(this.ignoreHostHttpsErrors, parsedUrl.hostname) &&
!_.includes(this.ignoreHostHttpsErrors, parsedUrl.host);
const checkServerCertificate = shouldUseStrictHttps(
parsedUrl.hostname as string,
parsedUrl.port as string,
this.ignoreHostHttpsErrors
);

const trustedCerts = await this.trustedCACertificates();
const caConfig = trustedCerts
Expand Down Expand Up @@ -471,4 +476,4 @@ export const WsHandlerLookup: typeof WsHandlerDefinitionLookup = {
'close-connection': CloseConnectionHandler,
'reset-connection': ResetConnectionHandler,
'timeout': TimeoutHandler
};
};
17 changes: 16 additions & 1 deletion test/integration/proxying/https-proxying.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,21 @@ nodeOnly(() => {
expect(response.statusCode).to.equal(502);
});

it("should allow passing through requests if all the hosts are whitelisted", async () => {
await badServer.forAnyRequest().thenReply(200);

await server.forAnyRequest().thenPassThrough({
ignoreHostHttpsErrors: true
});

let response = await request.get(badServer.url, {
resolveWithFullResponse: true,
simple: false
});

expect(response.statusCode).to.equal(200);
});

it("should allow passing through requests if the certificate is specifically listed", async () => {
await badServer.forAnyRequest().thenReply(200);

Expand Down Expand Up @@ -1030,4 +1045,4 @@ nodeOnly(() => {
});
});
});
});
});

0 comments on commit a9509b3

Please sign in to comment.