From 32815e83c91570c73aea168f10c0e5bfb1446412 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 13 Oct 2023 09:26:56 +0200 Subject: [PATCH] Redact diagnostics file --- .../lib/writeChromaticDiagnostics.test.ts | 25 +++++++++++++++++++ node-src/lib/writeChromaticDiagnostics.ts | 16 ++++++++++-- node-src/main.test.ts | 2 +- 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 node-src/lib/writeChromaticDiagnostics.test.ts diff --git a/node-src/lib/writeChromaticDiagnostics.test.ts b/node-src/lib/writeChromaticDiagnostics.test.ts new file mode 100644 index 000000000..c7c0a88c3 --- /dev/null +++ b/node-src/lib/writeChromaticDiagnostics.test.ts @@ -0,0 +1,25 @@ +import { describe, it, expect } from 'vitest'; +import { getDiagnostics } from './writeChromaticDiagnostics'; + +describe('getDiagnostics', () => { + it('returns context object', () => { + const ctx = { build: { number: 1 } }; + expect(getDiagnostics(ctx as any)).toEqual(ctx); + }); + + it('omits certain fields', () => { + const ctx = { argv: [], client: {}, env: {}, log: {}, pkg: {}, title: {} }; + expect(getDiagnostics(ctx as any)).toEqual({}); + }); + + it('redacts sensitive fields', () => { + const ctx = { + build: { number: 1, reportToken: 'foo' }, + flags: { projectToken: 'bar' }, + }; + expect(getDiagnostics(ctx as any)).toEqual({ + build: { number: 1, reportToken: undefined }, + flags: { projectToken: undefined }, + }); + }); +}); diff --git a/node-src/lib/writeChromaticDiagnostics.ts b/node-src/lib/writeChromaticDiagnostics.ts index b003bda8e..67f3772da 100644 --- a/node-src/lib/writeChromaticDiagnostics.ts +++ b/node-src/lib/writeChromaticDiagnostics.ts @@ -6,11 +6,23 @@ const { writeFile } = jsonfile; export const CHROMATIC_DIAGNOSTICS_FILE = 'chromatic-diagnostics.json'; +const redact = (value: T, ...fields: string[]): T => { + if (value === null || typeof value !== 'object') return value; + if (Array.isArray(value)) return value.map((item) => redact(item, ...fields)) as T; + const obj = { ...value }; + for (const key in obj) obj[key] = fields.includes(key) ? undefined : redact(obj[key], ...fields); + return obj; +}; + export function getDiagnostics(ctx: Context) { + // Drop some fields that are not useful to have and redact sensitive fields const { argv, client, env, help, http, log, pkg, title, ...rest } = ctx; - return Object.keys(rest) + const data = redact(rest, 'projectToken', 'reportToken'); + + // Sort top-level fields alphabetically + return Object.keys(data) .sort((a, b) => a.localeCompare(b)) - .reduce((acc, key) => ({ ...acc, [key]: rest[key] }), {}); + .reduce((acc, key) => ({ ...acc, [key]: data[key] }), {}); } // Extract important information from ctx, sort it and output into a json file diff --git a/node-src/main.test.ts b/node-src/main.test.ts index 95929ced2..75e59e59e 100644 --- a/node-src/main.test.ts +++ b/node-src/main.test.ts @@ -651,7 +651,7 @@ describe('runAll', () => { diagnostics: true, }), options: expect.objectContaining({ - projectToken: 'asdf1234', + projectToken: undefined, // redacted }), }), { spaces: 2 }