Skip to content

Commit

Permalink
Mask sensitive fields on http urls
Browse files Browse the repository at this point in the history
  • Loading branch information
yuqu committed Jan 17, 2020
1 parent 4a4d555 commit a0530c8
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class ZeplinApi {
validateStatus: (status: number): boolean => status === MOVED_TEMPORARILY
});

const [, responseQueryParams] = response.data.split("?");
const [, responseQueryParams] = response.headers.location.split("?");

const responseParams = new URLSearchParams(responseQueryParams);

Expand Down
37 changes: 32 additions & 5 deletions src/api/interceptors.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
import { AxiosRequestConfig, AxiosResponse } from "axios";
import maskJson from "mask-json";
import logger from "../util/logger";
import { URL } from "url";
import { MOVED_TEMPORARILY } from "http-status-codes";

const blacklist = ["password", "Zeplin-Access-Token", "Zeplin-Token"];
const blacklist = ["password", "token", "Zeplin-Access-Token", "Zeplin-Token", "Location"];

const mask = maskJson(blacklist);
const blacklistedQueryParams = ["access_token"];

const mask = maskJson(blacklist, {
ignoreCase: true
});

const maskUrl = (_url: string | undefined): string | undefined => {
let masked = _url;
try {
if (_url) {
const url = new URL(_url);

blacklistedQueryParams
.filter(param => url.searchParams.has(param))
.forEach(param => url.searchParams.set(param, "--REDACTED--"));

masked = url.toString();
}
} catch {
// Ignore
}
return masked;
};

const requestLogger = (request: AxiosRequestConfig): AxiosRequestConfig => {
const { url, method, data, headers } = request;
let httpLog = `HTTP Request: ${method} ${url}`;
let httpLog = `HTTP Request: ${method} ${maskUrl(url)}`;

if (headers) {
httpLog = httpLog.concat(`, Headers: ${JSON.stringify(mask(headers))}`);
Expand All @@ -31,14 +55,17 @@ const responseLogger = (response: AxiosResponse): AxiosResponse => {
data
} = response;

let httpLog = `HTTP Response: ${method} ${url}`
let httpLog = `HTTP Response: ${method} ${maskUrl(url)}`
.concat(`, Status: ${status}-${statusText}`);

if (headers) {
httpLog = httpLog.concat(`, Headers: ${JSON.stringify(mask(headers))}`);
}
if (data) {
httpLog = httpLog.concat(`, Body: ${JSON.stringify(mask(data))}`);
// 302 may contain sensitive query params
if (status !== MOVED_TEMPORARILY) {
httpLog = httpLog.concat(`, Body: ${JSON.stringify(mask(data))}`);
}
}

logger.http(httpLog);
Expand Down
2 changes: 1 addition & 1 deletion src/types/mask-json.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type FunctionReturnsSameType = <T>(object: T) => T;
declare module "mask-json" {
function maskJson(
collection: Array<string>,
opts?: { ignoreCase: boolean; replacement: string }
opts?: { ignoreCase?: boolean; replacement?: string }
): FunctionReturnsSameType;

export = maskJson;
Expand Down
6 changes: 5 additions & 1 deletion test/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ describe("ZeplinApi", () => {
it("returns token response when HTTP request succeeds", async () => {
const zeplinApi = new ZeplinApi();

mocked(Axios.get).mockResolvedValueOnce({ data: "url:port?access_token=wowmuchaccesstoken" });
mocked(Axios.get).mockResolvedValueOnce({
headers: {
location: "url:port?access_token=wowmuchaccesstoken"
}
});

await expect(zeplinApi.generateToken(samples.loginResponse.token))
.resolves
Expand Down

0 comments on commit a0530c8

Please sign in to comment.