@redocly/cli 0.0.0-snapshot.1782483365 → 0.0.0-snapshot.1782825774

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.
@@ -34,6 +34,33 @@ describe('#split', () => {
34
34
  and all related files are saved to the directory: ${(0, colorette_1.blue)(openapiDir)} \n`);
35
35
  expect(process.stderr.write.mock.calls[1][0]).toContain(`${filePath}: split processed in <test>ms`);
36
36
  });
37
+ it('aborts instead of writing a component file outside the output directory', async () => {
38
+ const filePath = 'packages/cli/src/commands/split/__tests__/fixtures/path-traversal.json';
39
+ jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
40
+ await expect((0, index_1.handleSplit)({
41
+ argv: {
42
+ api: filePath,
43
+ outDir: openapiDir,
44
+ separator: '_',
45
+ },
46
+ config: (0, miscellaneous_1.loadConfigAndHandleErrors)(),
47
+ version: 'cli-version',
48
+ })).rejects.toThrow(utils.HandledError);
49
+ expect(utils.writeToFileByExtension).not.toHaveBeenCalled();
50
+ });
51
+ it('aborts instead of writing a path file outside the output directory', async () => {
52
+ const filePath = 'packages/cli/src/commands/split/__tests__/fixtures/path-traversal-paths.json';
53
+ jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
54
+ await expect((0, index_1.handleSplit)({
55
+ argv: {
56
+ api: filePath,
57
+ outDir: openapiDir,
58
+ separator: '/',
59
+ },
60
+ config: (0, miscellaneous_1.loadConfigAndHandleErrors)(),
61
+ version: 'cli-version',
62
+ })).rejects.toThrow(utils.HandledError);
63
+ });
37
64
  it('should use the correct separator', async () => {
38
65
  const filePath = 'packages/cli/src/commands/split/__tests__/fixtures/spec.json';
39
66
  jest.spyOn(utils, 'pathToFilename').mockImplementation(() => 'newFilePath');
@@ -172,6 +172,13 @@ function extractFileNameFromPath(filename) {
172
172
  function getFileNamePath(componentDirPath, componentName, ext) {
173
173
  return path.join(componentDirPath, componentName) + `.${ext}`;
174
174
  }
175
+ function assertWithinDir(baseDir, targetPath, subject) {
176
+ const base = path.resolve(baseDir);
177
+ const target = path.resolve(targetPath);
178
+ if (target !== base && !target.startsWith(base + path.sep)) {
179
+ (0, miscellaneous_1.exitWithError)(`Refusing to write "${subject}" outside the output directory.`);
180
+ }
181
+ }
175
182
  function gatherComponentsFiles(components, componentsFiles, componentType, componentName, filename) {
176
183
  let inherits = [];
177
184
  if (componentType === types_1.OPENAPI3_COMPONENT.Schemas) {
@@ -191,6 +198,7 @@ function iteratePathItems(pathItems, openapiDir, outDir, componentsFiles, pathSe
191
198
  const pathData = pathItems[pathName];
192
199
  if ((0, openapi_core_1.isRef)(pathData))
193
200
  continue;
201
+ assertWithinDir(openapiDir, pathFile, pathName);
194
202
  for (const method of types_1.OPENAPI3_METHOD_NAMES) {
195
203
  const methodData = pathData[method];
196
204
  const methodDataXCode = methodData?.['x-code-samples'] || methodData?.['x-codeSamples'];
@@ -201,6 +209,7 @@ function iteratePathItems(pathItems, openapiDir, outDir, componentsFiles, pathSe
201
209
  if (sample.source && sample.source.$ref)
202
210
  continue;
203
211
  const sampleFileName = path.join(openapiDir, 'code_samples', (0, miscellaneous_1.escapeLanguageName)(sample.lang), codeSamplesPathPrefix + (0, miscellaneous_1.pathToFilename)(pathName, pathSeparator), method + (0, miscellaneous_1.langToExt)(sample.lang));
212
+ assertWithinDir(openapiDir, sampleFileName, sample.lang);
204
213
  fs.mkdirSync(path.dirname(sampleFileName), { recursive: true });
205
214
  fs.writeFileSync(sampleFileName, sample.source);
206
215
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -230,6 +239,7 @@ function iterateComponents(openapi, openapiDir, componentsFiles, ext) {
230
239
  const componentDirPath = path.join(componentsDir, componentType);
231
240
  for (const componentName of Object.keys(components?.[componentType] || {})) {
232
241
  const filename = getFileNamePath(componentDirPath, componentName, ext);
242
+ assertWithinDir(openapiDir, filename, componentName);
233
243
  gatherComponentsFiles(components, componentsFiles, componentType, componentName, filename);
234
244
  }
235
245
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/cli",
3
- "version": "0.0.0-snapshot.1782483365",
3
+ "version": "0.0.0-snapshot.1782825774",
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": "0.0.0-snapshot.1782483365",
42
- "@redocly/respect-core": "0.0.0-snapshot.1782483365",
41
+ "@redocly/openapi-core": "0.0.0-snapshot.1782825774",
42
+ "@redocly/respect-core": "0.0.0-snapshot.1782825774",
43
43
  "abort-controller": "3.0.0",
44
44
  "chokidar": "3.5.3",
45
45
  "colorette": "1.4.0",