@mintlify/cli 4.0.905 → 4.0.907

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.
@@ -12,11 +12,16 @@ vi.mock('@mintlify/previewing', () => ({
12
12
  removeLastLog: vi.fn(),
13
13
  }));
14
14
 
15
- vi.mock('@mintlify/validation', () => ({
16
- docsConfigSchema: {
17
- options: [{ shape: { theme: { _def: { value: 'quill' } } } }],
18
- },
19
- }));
15
+ vi.mock('@mintlify/validation', async () => {
16
+ const original =
17
+ await vi.importActual<typeof import('@mintlify/validation')>('@mintlify/validation');
18
+ return {
19
+ ...original,
20
+ docsConfigSchema: {
21
+ options: [{ shape: { theme: { _def: { value: 'quill' } } } }],
22
+ },
23
+ };
24
+ });
20
25
 
21
26
  vi.mock('fs-extra', () => ({
22
27
  default: {
@@ -48,19 +53,19 @@ describe('init', () => {
48
53
  describe('path traversal prevention', () => {
49
54
  it('rejects path traversal with ../', async () => {
50
55
  await expect(init('../outside', false, 'quill', 'Test')).rejects.toThrow(
51
- 'Access denied: path "../outside" is outside the current directory'
56
+ 'Access denied: path: ../outside is outside the current directory'
52
57
  );
53
58
  });
54
59
 
55
60
  it('rejects deep path traversal', async () => {
56
61
  await expect(init('../../../etc', false, 'quill', 'Test')).rejects.toThrow(
57
- 'Access denied: path "../../../etc" is outside the current directory'
62
+ 'Access denied: path: ../../../etc is outside the current directory'
58
63
  );
59
64
  });
60
65
 
61
66
  it('rejects absolute paths outside cwd', async () => {
62
67
  await expect(init('/etc/test', false, 'quill', 'Test')).rejects.toThrow(
63
- 'Access denied: path "/etc/test" is outside the current directory'
68
+ 'Access denied: path: /etc/test is outside the current directory'
64
69
  );
65
70
  });
66
71
 
@@ -125,20 +125,3 @@ describe('openApiCheck', () => {
125
125
  expect(processExitMock).toHaveBeenCalledWith(1);
126
126
  });
127
127
  });
128
-
129
- describe('readLocalOpenApiFile', () => {
130
- it('rejects path traversal attempts', async () => {
131
- const { readLocalOpenApiFile } =
132
- await vi.importActual<typeof import('../src/helpers.js')>('../src/helpers.js');
133
-
134
- await expect(readLocalOpenApiFile('../etc/passwd')).rejects.toThrow(
135
- 'Access denied: invalid path'
136
- );
137
- await expect(readLocalOpenApiFile('/etc/passwd')).rejects.toThrow(
138
- 'Access denied: invalid path'
139
- );
140
- await expect(readLocalOpenApiFile('../../secret.yaml')).rejects.toThrow(
141
- 'Access denied: invalid path'
142
- );
143
- });
144
- });
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { readLocalOpenApiFile } from '../src/helpers.js';
4
+
5
+ describe('readLocalOpenApiFile', () => {
6
+ describe('path traversal prevention', () => {
7
+ it('rejects path traversal with ../', async () => {
8
+ await expect(readLocalOpenApiFile('../etc/passwd')).rejects.toThrow(
9
+ 'path: ../etc/passwd is outside the current directory'
10
+ );
11
+ });
12
+
13
+ it('rejects absolute paths', async () => {
14
+ await expect(readLocalOpenApiFile('/etc/passwd')).rejects.toThrow(
15
+ 'path: /etc/passwd is outside the current directory'
16
+ );
17
+ });
18
+
19
+ it('rejects deep path traversal', async () => {
20
+ await expect(readLocalOpenApiFile('../../secret.yaml')).rejects.toThrow(
21
+ 'path: ../../secret.yaml is outside the current directory'
22
+ );
23
+ });
24
+ });
25
+ });
package/bin/helpers.js CHANGED
@@ -11,7 +11,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
11
11
  import { getConfigPath } from '@mintlify/prebuild';
12
12
  import { MintConfigUpdater } from '@mintlify/prebuild';
13
13
  import { addLog, ErrorLog, getClientVersion, SuccessLog, InfoLog, SpinnerLog, removeLastLog, LOCAL_LINKED_CLI_VERSION, } from '@mintlify/previewing';
14
- import { upgradeToDocsConfig } from '@mintlify/validation';
14
+ import { upgradeToDocsConfig, validatePathWithinCwd } from '@mintlify/validation';
15
15
  import detect from 'detect-port';
16
16
  import fse from 'fs-extra';
17
17
  import fs from 'fs/promises';
@@ -143,13 +143,7 @@ export const suppressConsoleWarnings = () => {
143
143
  };
144
144
  };
145
145
  export const readLocalOpenApiFile = (filename) => __awaiter(void 0, void 0, void 0, function* () {
146
- const baseDir = process.cwd();
147
- // const pathname = path.resolve(process.cwd(), filename);
148
- const resolvedPath = path.resolve(baseDir, filename);
149
- const relative = path.relative(baseDir, resolvedPath);
150
- if (relative.startsWith('..') || path.isAbsolute(relative)) {
151
- throw new Error('Access denied: invalid path');
152
- }
146
+ const { resolvedPath } = validatePathWithinCwd(filename, CMD_EXEC_PATH);
153
147
  const file = yield fs.readFile(resolvedPath, 'utf-8');
154
148
  const document = yaml.load(file);
155
149
  return document;
package/bin/init.js CHANGED
@@ -10,19 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  import { select, input } from '@inquirer/prompts';
12
12
  import { addLogs, addLog, SpinnerLog, removeLastLog } from '@mintlify/previewing';
13
- import { docsConfigSchema } from '@mintlify/validation';
13
+ import { docsConfigSchema, validatePathWithinCwd } from '@mintlify/validation';
14
14
  import AdmZip from 'adm-zip';
15
15
  import fse from 'fs-extra';
16
16
  import { Box, Text } from 'ink';
17
- import path from 'path';
18
- const validatePathWithinCwd = (inputPath) => {
19
- const baseDir = process.cwd();
20
- const resolvedPath = path.resolve(baseDir, inputPath);
21
- const relative = path.relative(baseDir, resolvedPath);
22
- if (relative.startsWith(`..${path.sep}`) || relative === '..' || path.isAbsolute(relative)) {
23
- throw new Error(`Access denied: path "${inputPath}" is outside the current directory`);
24
- }
25
- };
26
17
  const sendOnboardingMessage = (installDir) => {
27
18
  addLogs(_jsx(Text, { bold: true, children: "Documentation Setup!" }), _jsx(Text, { children: "To see your docs run" }), _jsxs(Box, { children: [_jsx(Text, { color: "blue", children: "cd" }), _jsxs(Text, { children: [" ", installDir] })] }), _jsx(Text, { color: "blue", children: "mint dev" }));
28
19
  };
@@ -74,7 +65,7 @@ export function init(installDir, force, theme, name) {
74
65
  }
75
66
  installDir = installDir === '.' ? subdir : `${installDir}/${subdir}`;
76
67
  // Re-validate after subdirectory is appended
77
- validatePathWithinCwd(installDir);
68
+ validatePathWithinCwd(installDir, process.cwd());
78
69
  }
79
70
  }
80
71
  if (!isAI && (!selectedTheme || !projectName)) {