Skip to content

Commit

Permalink
[Console] Encode pathname (#122080)
Browse files Browse the repository at this point in the history
* Fix encoding pathname

* Add a test case


Co-authored-by: Muhammad Ibragimov <[email protected]>
  • Loading branch information
mibragimov and Muhammad Ibragimov authored Jan 28, 2022
1 parent d4398fc commit 0dcdc8a
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 3 deletions.
37 changes: 36 additions & 1 deletion src/plugins/console/server/lib/proxy_request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import http, { ClientRequest } from 'http';
import * as sinon from 'sinon';
import { proxyRequest } from './proxy_request';
import { URL } from 'url';
import { URL, URLSearchParams } from 'url';
import { fail } from 'assert';

describe(`Console's send request`, () => {
Expand Down Expand Up @@ -101,4 +101,39 @@ describe(`Console's send request`, () => {
'transfer-encoding': 'chunked',
});
});

it('should decode percent-encoded uri and encode it correctly', async () => {
fakeRequest = {
abort: sinon.stub(),
on() {},
once(event: string, fn: (v: string) => void) {
if (event === 'response') {
return fn('done');
}
},
} as any;

const uri = new URL(
`http://noone.nowhere.none/%{[@metadata][beat]}-%{[@metadata][version]}-2020.08.23`
);

const result = await proxyRequest({
agent: null as any,
headers: {},
method: 'get',
payload: null as any,
timeout: 30000,
uri,
});

expect(result).toEqual('done');

const decoded = new URLSearchParams(`path=${uri.pathname}`).get('path');
const encoded = decoded
?.split('/')
.map((str) => encodeURIComponent(str))
.join('/');
const [httpRequestOptions] = stub.firstCall.args;
expect((httpRequestOptions as any).path).toEqual(encoded);
});
});
19 changes: 17 additions & 2 deletions src/plugins/console/server/lib/proxy_request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import https from 'https';
import net from 'net';
import stream from 'stream';
import Boom from '@hapi/boom';
import { URL } from 'url';
import { URL, URLSearchParams } from 'url';

interface Args {
method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head';
Expand All @@ -30,6 +30,20 @@ interface Args {
const sanitizeHostname = (hostName: string): string =>
hostName.trim().replace(/^\[/, '').replace(/\]$/, '');

/**
* Node URL percent-encodes any invalid characters in the pathname which results a 400 bad request error.
* We need to decode the percent-encoded pathname, and encode it correctly with encodeURIComponent
*/

const encodePathname = (pathname: string) => {
const decodedPath = new URLSearchParams(`path=${pathname}`).get('path') ?? '';

return decodedPath
.split('/')
.map((str) => encodeURIComponent(str))
.join('/');
};

// We use a modified version of Hapi's Wreck because Hapi, Axios, and Superagent don't support GET requests
// with bodies, but ES APIs do. Similarly with DELETE requests with bodies. Another library, `request`
// diverged too much from current behaviour.
Expand All @@ -44,6 +58,7 @@ export const proxyRequest = ({
}: Args) => {
const { hostname, port, protocol, pathname, search } = uri;
const client = uri.protocol === 'https:' ? https : http;
const encodedPath = encodePathname(pathname);
let resolved = false;

let resolve: (res: http.IncomingMessage) => void;
Expand All @@ -66,7 +81,7 @@ export const proxyRequest = ({
host: sanitizeHostname(hostname),
port: port === '' ? undefined : parseInt(port, 10),
protocol,
path: `${pathname}${search || ''}`,
path: `${encodedPath}${search || ''}`,
headers: {
...finalUserHeaders,
'content-type': 'application/json',
Expand Down

0 comments on commit 0dcdc8a

Please sign in to comment.