From 093f658e7f4f8c2cfc2b295d040a40fb727374c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3n=20T=C3=B3mas=20Gr=C3=A9tarsson?= Date: Wed, 21 Oct 2026 14:48:56 -0700 Subject: [PATCH] fix: replace fast-glob with tinyglobby fast-glob pulls in micromatch -> braces@3.0.3, which is flagged by CVE-2026-93687 / GHSA-vfj7-8cjw-p6xm. No patched braces release exists and the upstream maintainer disputes the CVE, so the only way to clear it for consumers (including @salesforce/cli) is to drop the dependency. tinyglobby (fdir + picomatch) has no braces dependency and is already in the sf CLI dependency tree via @oclif/plugin-plugins. The new globPaths helper preserves fast-glob's output contract: - absolute patterns return absolute paths (tinyglobby returns cwd-relative) - directories have no trailing slash - results are ordered pattern-by-pattern (tinyglobby interleaves) @types/picomatch is added as a devDependency because fdir's typings import picomatch (same approach as oclif/core). --- package.json | 5 +++-- src/shared/fs.ts | 23 +++++++++++++++++++---- yarn.lock | 5 +++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b3b523f3..b46d15bb 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "@salesforce/core": "^9.1.0", "@salesforce/kit": "^4.0.1", "@salesforce/sf-plugins-core": "^13.0.4", - "fast-glob": "^3.3.3", "fast-xml-parser": "^5.10.1", - "js2xmlparser": "^4.0.2" + "js2xmlparser": "^4.0.2", + "tinyglobby": "^0.2.17" }, "devDependencies": { "@oclif/plugin-command-snapshot": "^6.0.0", @@ -25,6 +25,7 @@ "@types/chai": "^4.3.17", "@types/mocha": "^10.0.10", "@types/node": "^18", + "@types/picomatch": "^4.0.3", "@types/sinon": "^10.0.20", "eslint": "^10.12.0", "eslint-config-salesforce-typescript": "^7.0.0", diff --git a/src/shared/fs.ts b/src/shared/fs.ts index 25850feb..90b519a7 100644 --- a/src/shared/fs.ts +++ b/src/shared/fs.ts @@ -15,7 +15,7 @@ */ import path from 'node:path'; import fs from 'node:fs'; -import fg from 'fast-glob'; +import { glob } from 'tinyglobby'; import type { CustomObject } from '@salesforce/types/metadata'; import { convertJsonToXml, parseXml } from './convert.js'; import { SaveableCustomObject, SaveablePlatformEvent } from './types.js'; @@ -28,13 +28,28 @@ const getObjectXmlByPathAsJson = async (objectFilePath: string): Promise filePath.split(path.sep).join(path.posix.sep); +/** + * tinyglobby returns paths relative to cwd (even for absolute patterns), directories with a trailing slash, + * and interleaves results across patterns. + * Glob each pattern in order, keep absolute patterns absolute, and strip the trailing slash. + */ +const globPaths = async (patterns: string | string[], onlyDirectories = false): Promise => { + const patternList = Array.isArray(patterns) ? patterns : [patterns]; + const results = await Promise.all( + patternList.map((pattern) => + glob(pattern, { onlyDirectories, expandDirectories: false, absolute: path.isAbsolute(pattern) }) + ) + ); + return [...new Set(results.flat().map((p) => path.normalize(p.endsWith('/') ? p.slice(0, -1) : p)))]; +}; + /** * @param targetPaths typically your pkgDirs or project path * @returns directories that end in `/objects/` */ export const getDirectoriesThatContainObjects = async (targetPaths: string[]): Promise => { const globs = targetPaths.map((p) => `${ensurePosixPath(p)}/**/objects/`); - return (await fg(globs, { onlyDirectories: true })).map((dir) => path.normalize(dir)); + return globPaths(globs, true); }; /** @@ -43,7 +58,7 @@ export const getDirectoriesThatContainObjects = async (targetPaths: string[]): P */ export const getObjectDirectories = async (targetPaths: string[]): Promise => { const globs = targetPaths.map((p) => `${ensurePosixPath(p)}/**/objects/*`); - return (await fg(globs, { onlyDirectories: true })).map((dir) => path.normalize(dir)); + return globPaths(globs, true); }; /** @@ -52,7 +67,7 @@ export const getObjectDirectories = async (targetPaths: string[]): Promise => { const globs = `${ensurePosixPath(folder)}/*.object-meta.xml`; - const [objectMetaPath] = await fg(globs); + const [objectMetaPath] = await globPaths(globs); return getObjectXmlByPathAsJson(objectMetaPath); }; diff --git a/yarn.lock b/yarn.lock index 2bcb6e46..f75fe203 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1711,6 +1711,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== +"@types/picomatch@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-4.0.3.tgz#d92bbf24166478c5457c8bb9ba41a7f49d55e094" + integrity sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ== + "@types/prop-types@*": version "15.7.15" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7"