@mintlify/cli 4.0.1194 → 4.0.1195

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/bin/update.js CHANGED
@@ -9,8 +9,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { jsx as _jsx } from "react/jsx-runtime";
11
11
  import { SpinnerLog, SuccessLog, ErrorLog, addLog, addErrorLog, clearLogs, getLatestClientVersion, downloadTargetMint, getClientVersion, } from '@mintlify/previewing';
12
- import { execAsync, getLatestCliVersion, getVersions, detectPackageManager } from './helpers.js';
12
+ import { assertValidPackageName, execFileAsync, getLatestCliVersion, getVersions, detectPackageManager, windowsCompatibleSpawnOptions, } from './helpers.js';
13
13
  export const update = (_a) => __awaiter(void 0, [_a], void 0, function* ({ packageName }) {
14
+ assertValidPackageName(packageName);
14
15
  addLog(_jsx(SpinnerLog, { message: "updating..." }));
15
16
  const { cli: existingCliVersion } = getVersions(packageName);
16
17
  const latestCliVersion = getLatestCliVersion(packageName);
@@ -21,10 +22,10 @@ export const update = (_a) => __awaiter(void 0, [_a], void 0, function* ({ packa
21
22
  clearLogs();
22
23
  addLog(_jsx(SpinnerLog, { message: `updating ${packageName} package...` }));
23
24
  if (packageManager === 'pnpm') {
24
- yield execAsync(`pnpm install -g ${packageName}@latest --silent`);
25
+ yield execFileAsync('pnpm', ['install', '-g', `${packageName}@latest`, '--silent'], windowsCompatibleSpawnOptions);
25
26
  }
26
27
  else {
27
- yield execAsync(`npm install -g ${packageName}@latest --silent`);
28
+ yield execFileAsync('npm', ['install', '-g', `${packageName}@latest`, '--silent'], windowsCompatibleSpawnOptions);
28
29
  }
29
30
  }
30
31
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mintlify/cli",
3
- "version": "4.0.1194",
3
+ "version": "4.0.1195",
4
4
  "description": "The Mintlify CLI",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -46,10 +46,10 @@
46
46
  "dependencies": {
47
47
  "@inquirer/prompts": "7.9.0",
48
48
  "@mintlify/common": "1.0.921",
49
- "@mintlify/link-rot": "3.0.1101",
49
+ "@mintlify/link-rot": "3.0.1102",
50
50
  "@mintlify/models": "0.0.315",
51
- "@mintlify/prebuild": "1.0.1065",
52
- "@mintlify/previewing": "4.0.1126",
51
+ "@mintlify/prebuild": "1.0.1066",
52
+ "@mintlify/previewing": "4.0.1127",
53
53
  "@mintlify/validation": "0.1.719",
54
54
  "adm-zip": "0.5.16",
55
55
  "chalk": "5.2.0",
@@ -94,5 +94,5 @@
94
94
  "vitest": "2.1.9",
95
95
  "vitest-mock-process": "1.0.4"
96
96
  },
97
- "gitHead": "ee67037982b7ee4bf4c9b9f0a7b6be1fa12b8011"
97
+ "gitHead": "fe377420c6f347b1c823d6b475ea6f075701506e"
98
98
  }
package/src/helpers.tsx CHANGED
@@ -16,7 +16,7 @@ import detect from 'detect-port';
16
16
  import fse from 'fs-extra';
17
17
  import inquirer from 'inquirer';
18
18
  import yaml from 'js-yaml';
19
- import { exec, execSync } from 'node:child_process';
19
+ import { execFile, execFileSync } from 'node:child_process';
20
20
  import { readFileSync } from 'node:fs';
21
21
  import fs from 'node:fs/promises';
22
22
  import { createRequire } from 'node:module';
@@ -174,10 +174,30 @@ export const getVersions = (
174
174
  return { cli, client };
175
175
  };
176
176
 
177
+ // On Windows, npm/pnpm entrypoints are .cmd shims that Node refuses to spawn
178
+ // without a shell (EINVAL since the CVE-2024-27980 fix). Everywhere else we
179
+ // keep the shell disabled so arguments can't be interpreted as shell syntax.
180
+ export const windowsCompatibleSpawnOptions = {
181
+ shell: process.platform === 'win32',
182
+ };
183
+
184
+ // npm package name grammar (optionally scoped). Rejecting anything else before
185
+ // it reaches a package-manager spawn keeps the win32 shell path (above) safe
186
+ // from shell metacharacters.
187
+ const VALID_PACKAGE_NAME_PATTERN = /^(@[a-z0-9~-][a-z0-9._~-]*\/)?[a-z0-9~-][a-z0-9._~-]*$/;
188
+
189
+ export const assertValidPackageName = (packageName: string): void => {
190
+ if (!VALID_PACKAGE_NAME_PATTERN.test(packageName)) {
191
+ throw new Error(`Invalid package name: ${packageName}`);
192
+ }
193
+ };
194
+
177
195
  export const getLatestCliVersion = (packageName: string) => {
178
- return execSync(`npm view ${packageName} version --silent`, {
196
+ assertValidPackageName(packageName);
197
+ return execFileSync('npm', ['view', packageName, 'version', '--silent'], {
179
198
  encoding: 'utf-8',
180
199
  stdio: ['pipe', 'pipe', 'pipe'],
200
+ ...windowsCompatibleSpawnOptions,
181
201
  }).trim();
182
202
  };
183
203
 
@@ -222,7 +242,7 @@ export const terminate = async (code: number) => {
222
242
  process.exit(code);
223
243
  };
224
244
 
225
- export const execAsync = promisify(exec);
245
+ export const execFileAsync = promisify(execFile);
226
246
 
227
247
  export function isAI(): boolean {
228
248
  return (
@@ -231,8 +251,9 @@ export function isAI(): boolean {
231
251
  }
232
252
 
233
253
  export const detectPackageManager = async ({ packageName }: { packageName: string }) => {
254
+ assertValidPackageName(packageName);
234
255
  try {
235
- const { stdout: packagePath } = await execAsync(`which ${packageName}`);
256
+ const { stdout: packagePath } = await execFileAsync('which', [packageName]);
236
257
  if (packagePath.includes('pnpm')) {
237
258
  return 'pnpm';
238
259
  } else {
package/src/update.tsx CHANGED
@@ -10,9 +10,17 @@ import {
10
10
  getClientVersion,
11
11
  } from '@mintlify/previewing';
12
12
 
13
- import { execAsync, getLatestCliVersion, getVersions, detectPackageManager } from './helpers.js';
13
+ import {
14
+ assertValidPackageName,
15
+ execFileAsync,
16
+ getLatestCliVersion,
17
+ getVersions,
18
+ detectPackageManager,
19
+ windowsCompatibleSpawnOptions,
20
+ } from './helpers.js';
14
21
 
15
22
  export const update = async ({ packageName }: { packageName: string }) => {
23
+ assertValidPackageName(packageName);
16
24
  addLog(<SpinnerLog message="updating..." />);
17
25
  const { cli: existingCliVersion } = getVersions(packageName);
18
26
  const latestCliVersion = getLatestCliVersion(packageName);
@@ -26,9 +34,17 @@ export const update = async ({ packageName }: { packageName: string }) => {
26
34
  addLog(<SpinnerLog message={`updating ${packageName} package...`} />);
27
35
 
28
36
  if (packageManager === 'pnpm') {
29
- await execAsync(`pnpm install -g ${packageName}@latest --silent`);
37
+ await execFileAsync(
38
+ 'pnpm',
39
+ ['install', '-g', `${packageName}@latest`, '--silent'],
40
+ windowsCompatibleSpawnOptions
41
+ );
30
42
  } else {
31
- await execAsync(`npm install -g ${packageName}@latest --silent`);
43
+ await execFileAsync(
44
+ 'npm',
45
+ ['install', '-g', `${packageName}@latest`, '--silent'],
46
+ windowsCompatibleSpawnOptions
47
+ );
32
48
  }
33
49
  } catch (err) {
34
50
  const errorMessage = err instanceof Error ? err.message : String(err);