chore: improve data obfuscation (#1143)

This commit is contained in:
Andrew Tatomyr
2023-07-06 11:32:54 +03:00
committed by GitHub
parent 60ed5a48fc
commit ab82560217
7 changed files with 68 additions and 22 deletions

View File

@@ -29,4 +29,6 @@ RUN npm cache clean --force && rm -rf /build
WORKDIR /spec WORKDIR /spec
ENTRYPOINT [ "openapi" ] ENTRYPOINT [ "redocly" ]
ENV REDOCLY_ENVIRONMENT=docker

View File

@@ -14,12 +14,12 @@ module.exports = {
'packages/core/': { 'packages/core/': {
statements: 80, statements: 80,
branches: 71, branches: 71,
functions: 69, functions: 70,
lines: 80, lines: 80,
}, },
'packages/cli/': { 'packages/cli/': {
statements: 55, statements: 55,
branches: 46, branches: 47,
functions: 55, functions: 55,
lines: 55, lines: 55,
}, },

View File

@@ -21,7 +21,7 @@ import {
YamlParseError, YamlParseError,
} from '@redocly/openapi-core'; } from '@redocly/openapi-core';
import { blue, red, yellow } from 'colorette'; import { blue, red, yellow } from 'colorette';
import { existsSync } from 'fs'; import { existsSync, statSync } from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as process from 'process'; import * as process from 'process';
@@ -489,16 +489,23 @@ describe('cleanArgs', () => {
beforeEach(() => { beforeEach(() => {
// @ts-ignore // @ts-ignore
isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl; isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl;
// @ts-ignore
existsSync = (value) => jest.requireActual('fs').existsSync(path.resolve(__dirname, value));
// @ts-ignore
statSync = (value) => jest.requireActual('fs').statSync(path.resolve(__dirname, value));
});
afterEach(() => {
jest.clearAllMocks();
}); });
it('should remove potentially sensitive data from args', () => { it('should remove potentially sensitive data from args', () => {
const testArgs = { const testArgs = {
config: 'some-folder/redocly.yaml', config: './fixtures/redocly.yaml',
apis: ['main@v1', 'openapi.yaml', 'http://some.url/openapi.yaml'], apis: ['main@v1', 'fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
format: 'codeframe', format: 'codeframe',
}; };
expect(cleanArgs(testArgs)).toEqual({ expect(cleanArgs(testArgs)).toEqual({
config: '***.yaml', config: 'file-yaml',
apis: ['main@v1', '***.yaml', 'http://***'], apis: ['api-name@api-version', 'file-yaml', 'http://url'],
format: 'codeframe', format: 'codeframe',
}); });
}); });
@@ -507,26 +514,51 @@ describe('cleanArgs', () => {
destination: '@org/name@version', destination: '@org/name@version',
}; };
expect(cleanArgs(testArgs)).toEqual({ expect(cleanArgs(testArgs)).toEqual({
destination: '@***/name@version', destination: '@organization/api-name@api-version',
}); });
}); });
}); });
describe('cleanRawInput', () => { describe('cleanRawInput', () => {
it('should remove potentially sensitive data from raw CLI input', () => { beforeEach(() => {
// @ts-ignore // @ts-ignore
isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl; isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl;
// @ts-ignore
existsSync = (value) => jest.requireActual('fs').existsSync(path.resolve(__dirname, value));
// @ts-ignore
statSync = (value) => jest.requireActual('fs').statSync(path.resolve(__dirname, value));
});
afterEach(() => {
jest.clearAllMocks();
});
it('should remove potentially sensitive data from raw CLI input', () => {
const rawInput = [
'redocly',
'bundle',
'api-name@api-version',
'./fixtures/openapi.yaml',
'http://some.url/openapi.yaml',
'--config=fixtures/redocly.yaml',
'--output',
'fixtures',
];
expect(cleanRawInput(rawInput)).toEqual(
'redocly bundle api-name@api-version file-yaml http://url --config=file-yaml --output folder'
);
});
it('should preserve safe data from raw CLI input', () => {
const rawInput = [ const rawInput = [
'redocly', 'redocly',
'lint', 'lint',
'main@v1', './fixtures/openapi.json',
'openapi.yaml', '--format',
'http://some.url/openapi.yaml', 'stylish',
'--config=some-folder/redocly.yaml', '--extends=minimal',
'--skip-rule',
'operation-4xx-response',
]; ];
expect(cleanRawInput(rawInput)).toEqual( expect(cleanRawInput(rawInput)).toEqual(
'redocly lint main@v1 ***.yaml http://*** --config=***.yaml' 'redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response'
); );
}); });
}); });

View File

@@ -496,7 +496,6 @@ export async function sendTelemetry(
} = argv; } = argv;
const event_time = new Date().toISOString(); const event_time = new Date().toISOString();
const redoclyClient = new RedoclyClient(); const redoclyClient = new RedoclyClient();
const node_version = process.version;
const logged_in = await redoclyClient.isAuthorizedWithRedoclyByRegion(); const logged_in = await redoclyClient.isAuthorizedWithRedoclyByRegion();
const data: Analytics = { const data: Analytics = {
event: 'cli_command', event: 'cli_command',
@@ -504,10 +503,11 @@ export async function sendTelemetry(
logged_in, logged_in,
command, command,
arguments: cleanArgs(args), arguments: cleanArgs(args),
node_version, node_version: process.version,
version, version,
exit_code, exit_code,
environment: process.env.REDOCLY_ENVIRONMENT, environment: process.env.REDOCLY_ENVIRONMENT,
environment_ci: process.env.CI,
raw_input: cleanRawInput(process.argv.slice(2)), raw_input: cleanRawInput(process.argv.slice(2)),
has_config, has_config,
}; };
@@ -535,22 +535,34 @@ export type Analytics = {
version: string; version: string;
exit_code: ExitCode; exit_code: ExitCode;
environment?: string; environment?: string;
environment_ci?: string;
raw_input: string; raw_input: string;
has_config?: boolean; has_config?: boolean;
}; };
function isFile(value: string) {
return fs.existsSync(value) && fs.statSync(value).isFile();
}
function isDirectory(value: string) {
return fs.existsSync(value) && fs.statSync(value).isDirectory();
}
function cleanString(value?: string): string | undefined { function cleanString(value?: string): string | undefined {
if (!value) { if (!value) {
return value; return value;
} }
if (isAbsoluteUrl(value)) { if (isAbsoluteUrl(value)) {
return value.split('://')[0] + '://***'; return value.split('://')[0] + '://url';
} }
if (value.endsWith('.json') || value.endsWith('.yaml') || value.endsWith('.yml')) { if (isFile(value)) {
return value.replace(/^(.*)\.(yaml|yml|json)$/gi, (_, __, ext) => '***.' + ext); return value.replace(/.+\.([^.]+)$/, (_, ext) => 'file-' + ext);
}
if (isDirectory(value)) {
return 'folder';
} }
if (DESTINATION_REGEX.test(value)) { if (DESTINATION_REGEX.test(value)) {
return value.replace(/^@[\w\-\s]+\//, () => '@***/'); return value.startsWith('@') ? '@organization/api-name@api-version' : 'api-name@api-version';
} }
return value; return value;
} }