@redocly/cli 1.34.0 → 1.34.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +1 -1
- package/lib/__tests__/utils.test.js +82 -33
- package/lib/utils/miscellaneous.d.ts +4 -2
- package/lib/utils/miscellaneous.js +28 -16
- package/package.json +3 -3
- package/src/__tests__/utils.test.ts +102 -44
- package/src/utils/miscellaneous.ts +33 -17
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @redocly/cli
|
|
2
2
|
|
|
3
|
+
## 1.34.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated @redocly/openapi-core to v1.34.2.
|
|
8
|
+
|
|
9
|
+
## 1.34.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Improved OpenTelemetry data serialization.
|
|
14
|
+
- Updated @redocly/respect-core to v1.34.1.
|
|
15
|
+
|
|
3
16
|
## 1.34.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Supports OpenAPI 3.1, 3.0 and OpenAPI 2.0 (legacy Swagger), AsyncAPI 3.0 and 2.6
|
|
|
16
16
|
### Node
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
npx @redocly/cli lint path-to-root-file.yaml
|
|
19
|
+
npx @redocly/cli@latest lint path-to-root-file.yaml
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Alternatively, install it globally with `npm`:
|
|
@@ -441,8 +441,8 @@ describe('cleanArgs', () => {
|
|
|
441
441
|
afterEach(() => {
|
|
442
442
|
jest.clearAllMocks();
|
|
443
443
|
});
|
|
444
|
-
it('should remove potentially sensitive data from args', () => {
|
|
445
|
-
const
|
|
444
|
+
it('should remove potentially sensitive data from parsed args', () => {
|
|
445
|
+
const parsedArgs = {
|
|
446
446
|
config: './fixtures/redocly.yaml',
|
|
447
447
|
apis: ['main@v1', 'fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
448
448
|
format: 'codeframe',
|
|
@@ -451,7 +451,21 @@ describe('cleanArgs', () => {
|
|
|
451
451
|
'client-key': 'some-client-key',
|
|
452
452
|
'ca-cert': 'some-ca-cert',
|
|
453
453
|
};
|
|
454
|
-
|
|
454
|
+
const rawArgs = [
|
|
455
|
+
'redocly',
|
|
456
|
+
'bundle',
|
|
457
|
+
'main@v1',
|
|
458
|
+
'fixtures/openapi.yaml',
|
|
459
|
+
'http://some.url/openapi.yaml',
|
|
460
|
+
'--config=fixtures/redocly.yaml',
|
|
461
|
+
'--format=codeframe',
|
|
462
|
+
'--input=some-input',
|
|
463
|
+
'--client-cert=some-client-cert',
|
|
464
|
+
'--client-key=some-client-key',
|
|
465
|
+
'--ca-cert=some-ca-cert',
|
|
466
|
+
];
|
|
467
|
+
const result = (0, miscellaneous_1.cleanArgs)(parsedArgs, rawArgs);
|
|
468
|
+
expect(result.arguments).toEqual(JSON.stringify({
|
|
455
469
|
config: 'file-yaml',
|
|
456
470
|
apis: ['api-name@api-version', 'file-yaml', 'http://url'],
|
|
457
471
|
format: 'codeframe',
|
|
@@ -459,30 +473,19 @@ describe('cleanArgs', () => {
|
|
|
459
473
|
'client-cert': '***',
|
|
460
474
|
'client-key': '***',
|
|
461
475
|
'ca-cert': '***',
|
|
462
|
-
});
|
|
476
|
+
}));
|
|
463
477
|
});
|
|
464
478
|
it('should remove potentially sensitive data from a push destination', () => {
|
|
465
|
-
const
|
|
479
|
+
const parsedArgs = {
|
|
466
480
|
destination: '@org/name@version',
|
|
467
481
|
};
|
|
468
|
-
|
|
482
|
+
const rawArgs = ['redocly', 'push', '--destination=@org/name@version'];
|
|
483
|
+
const result = (0, miscellaneous_1.cleanArgs)(parsedArgs, rawArgs);
|
|
484
|
+
expect(result.arguments).toEqual(JSON.stringify({
|
|
469
485
|
destination: '@organization/api-name@api-version',
|
|
470
|
-
});
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
describe('cleanRawInput', () => {
|
|
474
|
-
beforeEach(() => {
|
|
475
|
-
// @ts-ignore
|
|
476
|
-
openapi_core_1.isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl;
|
|
477
|
-
// @ts-ignore
|
|
478
|
-
fs_1.existsSync = (value) => jest.requireActual('fs').existsSync(path.resolve(__dirname, value));
|
|
479
|
-
// @ts-ignore
|
|
480
|
-
fs_1.statSync = (value) => jest.requireActual('fs').statSync(path.resolve(__dirname, value));
|
|
486
|
+
}));
|
|
481
487
|
});
|
|
482
|
-
|
|
483
|
-
jest.clearAllMocks();
|
|
484
|
-
});
|
|
485
|
-
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
488
|
+
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
486
489
|
const rawInput = [
|
|
487
490
|
'redocly',
|
|
488
491
|
'bundle',
|
|
@@ -492,8 +495,41 @@ describe('cleanRawInput', () => {
|
|
|
492
495
|
'--config=fixtures/redocly.yaml',
|
|
493
496
|
'--output',
|
|
494
497
|
'fixtures',
|
|
498
|
+
'--client-cert',
|
|
499
|
+
'fixtures/client-cert.pem',
|
|
500
|
+
'--client-key',
|
|
501
|
+
'fixtures/client-key.pem',
|
|
502
|
+
'--ca-cert',
|
|
503
|
+
'fixtures/ca-cert.pem',
|
|
504
|
+
'--organization',
|
|
505
|
+
'my-org',
|
|
506
|
+
'--input',
|
|
507
|
+
'timeout=10000',
|
|
508
|
+
'--input',
|
|
509
|
+
'{"apiKey":"some=111=1111"}',
|
|
495
510
|
];
|
|
496
|
-
|
|
511
|
+
const parsedArgs = {
|
|
512
|
+
apis: ['./fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
513
|
+
input: ['timeout=10000', '{"apiKey":"some=111=1111"}'],
|
|
514
|
+
organization: 'my-org',
|
|
515
|
+
'client-cert': 'fixtures/client-cert.pem',
|
|
516
|
+
'client-key': 'fixtures/client-key.pem',
|
|
517
|
+
'ca-cert': 'fixtures/ca-cert.pem',
|
|
518
|
+
config: 'fixtures/redocly.yaml',
|
|
519
|
+
output: 'fixtures',
|
|
520
|
+
};
|
|
521
|
+
const result = (0, miscellaneous_1.cleanArgs)(parsedArgs, rawInput);
|
|
522
|
+
expect(result.raw_input).toEqual('redocly bundle api-name@api-version file-yaml http://url --config=file-yaml --output folder --client-cert *** --client-key *** --ca-cert *** --organization *** --input *** --input ***');
|
|
523
|
+
expect(result.arguments).toEqual(JSON.stringify({
|
|
524
|
+
apis: ['file-yaml', 'http://url'],
|
|
525
|
+
input: '***',
|
|
526
|
+
organization: '***',
|
|
527
|
+
'client-cert': '***',
|
|
528
|
+
'client-key': '***',
|
|
529
|
+
'ca-cert': '***',
|
|
530
|
+
config: 'file-yaml',
|
|
531
|
+
output: 'folder',
|
|
532
|
+
}));
|
|
497
533
|
});
|
|
498
534
|
it('should preserve safe data from raw CLI input', () => {
|
|
499
535
|
const rawInput = [
|
|
@@ -506,18 +542,31 @@ describe('cleanRawInput', () => {
|
|
|
506
542
|
'--skip-rule',
|
|
507
543
|
'operation-4xx-response',
|
|
508
544
|
];
|
|
509
|
-
|
|
545
|
+
const parsedArgs = {
|
|
546
|
+
apis: ['./fixtures/openapi.json'],
|
|
547
|
+
format: 'stylish',
|
|
548
|
+
extends: 'minimal',
|
|
549
|
+
'skip-rule': ['operation-4xx-response'],
|
|
550
|
+
};
|
|
551
|
+
const result = (0, miscellaneous_1.cleanArgs)(parsedArgs, rawInput);
|
|
552
|
+
expect(result.raw_input).toEqual('redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response');
|
|
553
|
+
expect(result.arguments).toEqual(JSON.stringify({
|
|
554
|
+
apis: ['file-json'],
|
|
555
|
+
format: 'stylish',
|
|
556
|
+
extends: 'minimal',
|
|
557
|
+
'skip-rule': ['operation-4xx-response'],
|
|
558
|
+
}));
|
|
510
559
|
});
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
560
|
+
});
|
|
561
|
+
describe('validateFileExtension', () => {
|
|
562
|
+
it('should return current file extension', () => {
|
|
563
|
+
expect((0, miscellaneous_1.getAndValidateFileExtension)('test.json')).toEqual('json');
|
|
564
|
+
});
|
|
565
|
+
it('should return yaml and print warning if file extension does not supported', () => {
|
|
566
|
+
const stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
567
|
+
colorette_1.yellow.mockImplementation((text) => text);
|
|
568
|
+
expect((0, miscellaneous_1.getAndValidateFileExtension)('test.xml')).toEqual('yaml');
|
|
569
|
+
expect(stderrMock).toHaveBeenCalledWith(`Unsupported file extension: xml. Using yaml.\n`);
|
|
521
570
|
});
|
|
522
571
|
});
|
|
523
572
|
describe('writeToFileByExtension', () => {
|
|
@@ -75,8 +75,10 @@ export type Analytics = {
|
|
|
75
75
|
spec_keyword?: string;
|
|
76
76
|
spec_full_version?: string;
|
|
77
77
|
};
|
|
78
|
-
export declare function cleanArgs(
|
|
79
|
-
|
|
78
|
+
export declare function cleanArgs(parsedArgs: CommandOptions, rawArgv: string[]): {
|
|
79
|
+
arguments: string;
|
|
80
|
+
raw_input: string;
|
|
81
|
+
};
|
|
80
82
|
export declare function checkForDeprecatedOptions<T>(argv: T, deprecatedOptions: Array<keyof T>): void;
|
|
81
83
|
export declare function notifyAboutIncompatibleConfigOptions(themeOpenapiOptions: Record<string, unknown> | undefined): void;
|
|
82
84
|
export declare function formatPath(path: string): string;
|
|
@@ -28,7 +28,6 @@ exports.checkIfRulesetExist = checkIfRulesetExist;
|
|
|
28
28
|
exports.cleanColors = cleanColors;
|
|
29
29
|
exports.sendTelemetry = sendTelemetry;
|
|
30
30
|
exports.cleanArgs = cleanArgs;
|
|
31
|
-
exports.cleanRawInput = cleanRawInput;
|
|
32
31
|
exports.checkForDeprecatedOptions = checkForDeprecatedOptions;
|
|
33
32
|
exports.notifyAboutIncompatibleConfigOptions = notifyAboutIncompatibleConfigOptions;
|
|
34
33
|
exports.formatPath = formatPath;
|
|
@@ -458,7 +457,7 @@ async function sendTelemetry(argv, exit_code, has_config, spec_version, spec_key
|
|
|
458
457
|
event_time,
|
|
459
458
|
logged_in: logged_in ? 'yes' : 'no',
|
|
460
459
|
command: `${command}`,
|
|
461
|
-
|
|
460
|
+
...cleanArgs(args, process.argv.slice(2)),
|
|
462
461
|
node_version: process.version,
|
|
463
462
|
npm_version: (0, child_process_1.execSync)('npm -v').toString().replace('\n', ''),
|
|
464
463
|
os_platform: os.platform(),
|
|
@@ -466,7 +465,6 @@ async function sendTelemetry(argv, exit_code, has_config, spec_version, spec_key
|
|
|
466
465
|
exit_code,
|
|
467
466
|
environment: process.env.REDOCLY_ENVIRONMENT,
|
|
468
467
|
environment_ci: process.env.CI,
|
|
469
|
-
raw_input: cleanRawInput(process.argv.slice(2)),
|
|
470
468
|
has_config: has_config ? 'yes' : 'no',
|
|
471
469
|
spec_version,
|
|
472
470
|
spec_keyword,
|
|
@@ -504,27 +502,41 @@ function cleanString(value) {
|
|
|
504
502
|
}
|
|
505
503
|
return value;
|
|
506
504
|
}
|
|
507
|
-
function
|
|
508
|
-
const
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
505
|
+
function replaceArgs(commandInput, targets, replacement) {
|
|
506
|
+
const targetValues = Array.isArray(targets) ? targets : [targets];
|
|
507
|
+
for (const target of targetValues) {
|
|
508
|
+
commandInput = commandInput.replaceAll(target, replacement);
|
|
509
|
+
}
|
|
510
|
+
return commandInput;
|
|
511
|
+
}
|
|
512
|
+
function cleanArgs(parsedArgs, rawArgv) {
|
|
513
|
+
const KEYS_TO_CLEAN = ['organization', 'o', 'input', 'i', 'client-cert', 'client-key', 'ca-cert'];
|
|
514
|
+
let commandInput = rawArgv.join(' ');
|
|
515
|
+
const commandArguments = {};
|
|
516
|
+
for (const [key, value] of Object.entries(parsedArgs)) {
|
|
517
|
+
if (KEYS_TO_CLEAN.includes(key)) {
|
|
518
|
+
commandArguments[key] = '***';
|
|
519
|
+
commandInput = replaceArgs(commandInput, value, '***');
|
|
513
520
|
}
|
|
514
521
|
else if (typeof value === 'string') {
|
|
515
|
-
|
|
522
|
+
const cleanedValue = cleanString(value);
|
|
523
|
+
commandArguments[key] = cleanedValue;
|
|
524
|
+
commandInput = replaceArgs(commandInput, value, cleanedValue);
|
|
516
525
|
}
|
|
517
526
|
else if (Array.isArray(value)) {
|
|
518
|
-
|
|
527
|
+
commandArguments[key] = value.map(cleanString);
|
|
528
|
+
for (const replacedValue of value) {
|
|
529
|
+
const newValue = cleanString(replacedValue);
|
|
530
|
+
if (commandInput.includes(replacedValue)) {
|
|
531
|
+
commandInput = commandInput.replaceAll(replacedValue, newValue);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
519
534
|
}
|
|
520
535
|
else {
|
|
521
|
-
|
|
536
|
+
commandArguments[key] = value;
|
|
522
537
|
}
|
|
523
538
|
}
|
|
524
|
-
return
|
|
525
|
-
}
|
|
526
|
-
function cleanRawInput(argv) {
|
|
527
|
-
return argv.map((entry) => entry.split('=').map(cleanString).join('=')).join(' ');
|
|
539
|
+
return { arguments: JSON.stringify(commandArguments), raw_input: commandInput };
|
|
528
540
|
}
|
|
529
541
|
function checkForDeprecatedOptions(argv, deprecatedOptions) {
|
|
530
542
|
for (const option of deprecatedOptions) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/cli",
|
|
3
|
-
"version": "1.34.
|
|
3
|
+
"version": "1.34.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@redocly/config": "^0.22.0",
|
|
41
|
-
"@redocly/openapi-core": "1.34.
|
|
42
|
-
"@redocly/respect-core": "1.34.
|
|
41
|
+
"@redocly/openapi-core": "1.34.2",
|
|
42
|
+
"@redocly/respect-core": "1.34.2",
|
|
43
43
|
"abort-controller": "^3.0.0",
|
|
44
44
|
"chokidar": "^3.5.1",
|
|
45
45
|
"colorette": "^1.2.0",
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
cleanColors,
|
|
12
12
|
HandledError,
|
|
13
13
|
cleanArgs,
|
|
14
|
-
cleanRawInput,
|
|
15
14
|
getAndValidateFileExtension,
|
|
16
15
|
writeToFileByExtension,
|
|
17
16
|
} from '../utils/miscellaneous';
|
|
@@ -28,7 +27,6 @@ import { blue, red, yellow } from 'colorette';
|
|
|
28
27
|
import { existsSync, statSync } from 'fs';
|
|
29
28
|
import * as path from 'path';
|
|
30
29
|
import * as process from 'process';
|
|
31
|
-
import { ConfigApis } from '../types';
|
|
32
30
|
|
|
33
31
|
jest.mock('os');
|
|
34
32
|
jest.mock('colorette');
|
|
@@ -540,8 +538,8 @@ describe('cleanArgs', () => {
|
|
|
540
538
|
afterEach(() => {
|
|
541
539
|
jest.clearAllMocks();
|
|
542
540
|
});
|
|
543
|
-
it('should remove potentially sensitive data from args', () => {
|
|
544
|
-
const
|
|
541
|
+
it('should remove potentially sensitive data from parsed args', () => {
|
|
542
|
+
const parsedArgs = {
|
|
545
543
|
config: './fixtures/redocly.yaml',
|
|
546
544
|
apis: ['main@v1', 'fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
547
545
|
format: 'codeframe',
|
|
@@ -550,39 +548,46 @@ describe('cleanArgs', () => {
|
|
|
550
548
|
'client-key': 'some-client-key',
|
|
551
549
|
'ca-cert': 'some-ca-cert',
|
|
552
550
|
};
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
'
|
|
559
|
-
'
|
|
560
|
-
'
|
|
561
|
-
|
|
551
|
+
const rawArgs = [
|
|
552
|
+
'redocly',
|
|
553
|
+
'bundle',
|
|
554
|
+
'main@v1',
|
|
555
|
+
'fixtures/openapi.yaml',
|
|
556
|
+
'http://some.url/openapi.yaml',
|
|
557
|
+
'--config=fixtures/redocly.yaml',
|
|
558
|
+
'--format=codeframe',
|
|
559
|
+
'--input=some-input',
|
|
560
|
+
'--client-cert=some-client-cert',
|
|
561
|
+
'--client-key=some-client-key',
|
|
562
|
+
'--ca-cert=some-ca-cert',
|
|
563
|
+
];
|
|
564
|
+
const result = cleanArgs(parsedArgs, rawArgs);
|
|
565
|
+
expect(result.arguments).toEqual(
|
|
566
|
+
JSON.stringify({
|
|
567
|
+
config: 'file-yaml',
|
|
568
|
+
apis: ['api-name@api-version', 'file-yaml', 'http://url'],
|
|
569
|
+
format: 'codeframe',
|
|
570
|
+
input: '***',
|
|
571
|
+
'client-cert': '***',
|
|
572
|
+
'client-key': '***',
|
|
573
|
+
'ca-cert': '***',
|
|
574
|
+
})
|
|
575
|
+
);
|
|
562
576
|
});
|
|
563
577
|
it('should remove potentially sensitive data from a push destination', () => {
|
|
564
|
-
const
|
|
578
|
+
const parsedArgs = {
|
|
565
579
|
destination: '@org/name@version',
|
|
566
580
|
};
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
581
|
+
const rawArgs = ['redocly', 'push', '--destination=@org/name@version'];
|
|
582
|
+
const result = cleanArgs(parsedArgs, rawArgs);
|
|
583
|
+
expect(result.arguments).toEqual(
|
|
584
|
+
JSON.stringify({
|
|
585
|
+
destination: '@organization/api-name@api-version',
|
|
586
|
+
})
|
|
587
|
+
);
|
|
570
588
|
});
|
|
571
|
-
});
|
|
572
589
|
|
|
573
|
-
|
|
574
|
-
beforeEach(() => {
|
|
575
|
-
// @ts-ignore
|
|
576
|
-
isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl;
|
|
577
|
-
// @ts-ignore
|
|
578
|
-
existsSync = (value) => jest.requireActual('fs').existsSync(path.resolve(__dirname, value));
|
|
579
|
-
// @ts-ignore
|
|
580
|
-
statSync = (value) => jest.requireActual('fs').statSync(path.resolve(__dirname, value));
|
|
581
|
-
});
|
|
582
|
-
afterEach(() => {
|
|
583
|
-
jest.clearAllMocks();
|
|
584
|
-
});
|
|
585
|
-
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
590
|
+
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
586
591
|
const rawInput = [
|
|
587
592
|
'redocly',
|
|
588
593
|
'bundle',
|
|
@@ -592,11 +597,47 @@ describe('cleanRawInput', () => {
|
|
|
592
597
|
'--config=fixtures/redocly.yaml',
|
|
593
598
|
'--output',
|
|
594
599
|
'fixtures',
|
|
600
|
+
'--client-cert',
|
|
601
|
+
'fixtures/client-cert.pem',
|
|
602
|
+
'--client-key',
|
|
603
|
+
'fixtures/client-key.pem',
|
|
604
|
+
'--ca-cert',
|
|
605
|
+
'fixtures/ca-cert.pem',
|
|
606
|
+
'--organization',
|
|
607
|
+
'my-org',
|
|
608
|
+
'--input',
|
|
609
|
+
'timeout=10000',
|
|
610
|
+
'--input',
|
|
611
|
+
'{"apiKey":"some=111=1111"}',
|
|
595
612
|
];
|
|
596
|
-
|
|
597
|
-
'
|
|
613
|
+
const parsedArgs = {
|
|
614
|
+
apis: ['./fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
615
|
+
input: ['timeout=10000', '{"apiKey":"some=111=1111"}'],
|
|
616
|
+
organization: 'my-org',
|
|
617
|
+
'client-cert': 'fixtures/client-cert.pem',
|
|
618
|
+
'client-key': 'fixtures/client-key.pem',
|
|
619
|
+
'ca-cert': 'fixtures/ca-cert.pem',
|
|
620
|
+
config: 'fixtures/redocly.yaml',
|
|
621
|
+
output: 'fixtures',
|
|
622
|
+
};
|
|
623
|
+
const result = cleanArgs(parsedArgs, rawInput);
|
|
624
|
+
expect(result.raw_input).toEqual(
|
|
625
|
+
'redocly bundle api-name@api-version file-yaml http://url --config=file-yaml --output folder --client-cert *** --client-key *** --ca-cert *** --organization *** --input *** --input ***'
|
|
626
|
+
);
|
|
627
|
+
expect(result.arguments).toEqual(
|
|
628
|
+
JSON.stringify({
|
|
629
|
+
apis: ['file-yaml', 'http://url'],
|
|
630
|
+
input: '***',
|
|
631
|
+
organization: '***',
|
|
632
|
+
'client-cert': '***',
|
|
633
|
+
'client-key': '***',
|
|
634
|
+
'ca-cert': '***',
|
|
635
|
+
config: 'file-yaml',
|
|
636
|
+
output: 'folder',
|
|
637
|
+
})
|
|
598
638
|
);
|
|
599
639
|
});
|
|
640
|
+
|
|
600
641
|
it('should preserve safe data from raw CLI input', () => {
|
|
601
642
|
const rawInput = [
|
|
602
643
|
'redocly',
|
|
@@ -608,23 +649,40 @@ describe('cleanRawInput', () => {
|
|
|
608
649
|
'--skip-rule',
|
|
609
650
|
'operation-4xx-response',
|
|
610
651
|
];
|
|
611
|
-
|
|
652
|
+
const parsedArgs = {
|
|
653
|
+
apis: ['./fixtures/openapi.json'],
|
|
654
|
+
format: 'stylish',
|
|
655
|
+
extends: 'minimal',
|
|
656
|
+
'skip-rule': ['operation-4xx-response'],
|
|
657
|
+
};
|
|
658
|
+
const result = cleanArgs(parsedArgs, rawInput);
|
|
659
|
+
|
|
660
|
+
expect(result.raw_input).toEqual(
|
|
612
661
|
'redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response'
|
|
613
662
|
);
|
|
663
|
+
|
|
664
|
+
expect(result.arguments).toEqual(
|
|
665
|
+
JSON.stringify({
|
|
666
|
+
apis: ['file-json'],
|
|
667
|
+
format: 'stylish',
|
|
668
|
+
extends: 'minimal',
|
|
669
|
+
'skip-rule': ['operation-4xx-response'],
|
|
670
|
+
})
|
|
671
|
+
);
|
|
614
672
|
});
|
|
673
|
+
});
|
|
615
674
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
675
|
+
describe('validateFileExtension', () => {
|
|
676
|
+
it('should return current file extension', () => {
|
|
677
|
+
expect(getAndValidateFileExtension('test.json')).toEqual('json');
|
|
678
|
+
});
|
|
620
679
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
680
|
+
it('should return yaml and print warning if file extension does not supported', () => {
|
|
681
|
+
const stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
682
|
+
(yellow as jest.Mock<any, any>).mockImplementation((text: string) => text);
|
|
624
683
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
});
|
|
684
|
+
expect(getAndValidateFileExtension('test.xml')).toEqual('yaml');
|
|
685
|
+
expect(stderrMock).toHaveBeenCalledWith(`Unsupported file extension: xml. Using yaml.\n`);
|
|
628
686
|
});
|
|
629
687
|
});
|
|
630
688
|
|
|
@@ -575,7 +575,7 @@ export async function sendTelemetry(
|
|
|
575
575
|
event_time,
|
|
576
576
|
logged_in: logged_in ? 'yes' : 'no',
|
|
577
577
|
command: `${command}`,
|
|
578
|
-
|
|
578
|
+
...cleanArgs(args, process.argv.slice(2)),
|
|
579
579
|
node_version: process.version,
|
|
580
580
|
npm_version: execSync('npm -v').toString().replace('\n', ''),
|
|
581
581
|
os_platform: os.platform(),
|
|
@@ -583,7 +583,6 @@ export async function sendTelemetry(
|
|
|
583
583
|
exit_code,
|
|
584
584
|
environment: process.env.REDOCLY_ENVIRONMENT,
|
|
585
585
|
environment_ci: process.env.CI,
|
|
586
|
-
raw_input: cleanRawInput(process.argv.slice(2)),
|
|
587
586
|
has_config: has_config ? 'yes' : 'no',
|
|
588
587
|
spec_version,
|
|
589
588
|
spec_keyword,
|
|
@@ -627,7 +626,7 @@ function isDirectory(value: string) {
|
|
|
627
626
|
return fs.existsSync(value) && fs.statSync(value).isDirectory();
|
|
628
627
|
}
|
|
629
628
|
|
|
630
|
-
function cleanString(value
|
|
629
|
+
function cleanString(value: string): string {
|
|
631
630
|
if (!value) {
|
|
632
631
|
return value;
|
|
633
632
|
}
|
|
@@ -646,27 +645,44 @@ function cleanString(value?: string): string | undefined {
|
|
|
646
645
|
return value;
|
|
647
646
|
}
|
|
648
647
|
|
|
649
|
-
|
|
650
|
-
|
|
648
|
+
function replaceArgs(
|
|
649
|
+
commandInput: string,
|
|
650
|
+
targets: string | string[],
|
|
651
|
+
replacement: string
|
|
652
|
+
): string {
|
|
653
|
+
const targetValues = Array.isArray(targets) ? targets : [targets];
|
|
654
|
+
for (const target of targetValues) {
|
|
655
|
+
commandInput = commandInput.replaceAll(target, replacement);
|
|
656
|
+
}
|
|
657
|
+
return commandInput;
|
|
658
|
+
}
|
|
651
659
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
660
|
+
export function cleanArgs(parsedArgs: CommandOptions, rawArgv: string[]) {
|
|
661
|
+
const KEYS_TO_CLEAN = ['organization', 'o', 'input', 'i', 'client-cert', 'client-key', 'ca-cert'];
|
|
662
|
+
let commandInput = rawArgv.join(' ');
|
|
663
|
+
const commandArguments: Record<string, string | string[]> = {};
|
|
664
|
+
for (const [key, value] of Object.entries(parsedArgs)) {
|
|
665
|
+
if (KEYS_TO_CLEAN.includes(key)) {
|
|
666
|
+
commandArguments[key] = '***';
|
|
667
|
+
commandInput = replaceArgs(commandInput, value, '***');
|
|
656
668
|
} else if (typeof value === 'string') {
|
|
657
|
-
|
|
669
|
+
const cleanedValue = cleanString(value);
|
|
670
|
+
commandArguments[key] = cleanedValue;
|
|
671
|
+
commandInput = replaceArgs(commandInput, value, cleanedValue);
|
|
658
672
|
} else if (Array.isArray(value)) {
|
|
659
|
-
|
|
673
|
+
commandArguments[key] = value.map(cleanString);
|
|
674
|
+
for (const replacedValue of value) {
|
|
675
|
+
const newValue = cleanString(replacedValue);
|
|
676
|
+
if (commandInput.includes(replacedValue)) {
|
|
677
|
+
commandInput = commandInput.replaceAll(replacedValue, newValue);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
660
680
|
} else {
|
|
661
|
-
|
|
681
|
+
commandArguments[key] = value;
|
|
662
682
|
}
|
|
663
683
|
}
|
|
664
684
|
|
|
665
|
-
return
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
export function cleanRawInput(argv: string[]) {
|
|
669
|
-
return argv.map((entry) => entry.split('=').map(cleanString).join('=')).join(' ');
|
|
685
|
+
return { arguments: JSON.stringify(commandArguments), raw_input: commandInput };
|
|
670
686
|
}
|
|
671
687
|
|
|
672
688
|
export function checkForDeprecatedOptions<T>(argv: T, deprecatedOptions: Array<keyof T>) {
|