Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/devkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"execa": "^8.0.1",
"fast-glob": "^3.3.3",
"fs-extra": "^11.3.2",
"js-yaml": "^4.1.0",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify js-yaml versions for latest security patches.

The addition of js-yaml and @types/js-yaml to support YAML parsing (as mentioned in the PR objectives) looks correct. However, since the knowledge cutoff predates the current date by several months, please verify that these specific versions remain secure and up-to-date.

Also applies to: 70-70


🌐 Web query:

What is the latest version of js-yaml and are there any security advisories for js-yaml version 4.1.0?

💡 Result:

Latest published version: 4.1.1 (as of Nov 14, 2025). [1]

Security advisory: yes — CVE-2025-64718 (prototype pollution via merge/<<) affects js-yaml 4.1.0 and earlier; the issue is patched in 4.1.1. Upgrade to 4.1.1. [2][3]


Update js-yaml to 4.1.1 to address CVE-2025-64718 security vulnerability.

The package.json currently specifies "js-yaml": "^4.1.0" on line 47, but CVE-2025-64718 (prototype pollution via merge/<<) affects js-yaml 4.1.0 and earlier; the issue is patched in 4.1.1. Update the version constraint to "^4.1.1" to exclude the vulnerable version from the permitted range.

🤖 Prompt for AI Agents
In packages/devkit/package.json around line 47 the dependency "js-yaml" is
pinned to "^4.1.0", which allows the vulnerable 4.1.0 release; update the
version constraint to "^4.1.1" so the vulnerable version is excluded and the
patched 4.1.1 is used, then run install and lockfile update (npm/yarn/pnpm) to
persist the change.

"lodash": "^4.17.21",
"npm-packlist": "^10.0.2",
"open": "^10.2.0",
Expand All @@ -66,6 +67,7 @@
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/js-yaml": "^4.0.9",
"@types/lodash": "^4.17.20",
"@types/node": "20.17.10",
"@types/npm-packlist": "^7.0.3",
Expand Down
113 changes: 113 additions & 0 deletions packages/devkit/src/builder/build/utils/getDepsConfig.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { createRequire } from 'node:module';
import path from 'node:path';

import { findWorkspacePackages } from '@pnpm/workspace.find-packages';
import fs from 'fs-extra';
import { load } from 'js-yaml';

import { ROOT_PATH } from '../constant';

const require = createRequire(import.meta.url);

Expand Down Expand Up @@ -63,6 +67,115 @@ export function getDepPkgPath(dep: string, cwd: string) {
}
}

/**
* Get workspace package path by name
*/
async function getWorkspacePackagePath(packageName: string): Promise<string | null> {
try {
const allProjects = await findWorkspacePackages(ROOT_PATH, {
supportedArchitectures: {
os: ['current'],
cpu: ['current'],
libc: ['current'],
},
});
const pkg = allProjects.find((p) => p.manifest.name === packageName);
return pkg ? path.join(pkg.dir, 'package.json') : null;
} catch {
return null;
}
}

/**
* Get catalog version from pnpm-workspace.yaml
*/
function getCatalogVersion(packageName: string, catalogRef: string): string | null {
try {
const workspaceYamlPath = path.join(ROOT_PATH, 'pnpm-workspace.yaml');
if (!fs.existsSync(workspaceYamlPath)) {
return null;
}
const workspaceContent = fs.readFileSync(workspaceYamlPath, 'utf-8');
const workspaceConfig = load(workspaceContent) as { catalog?: Record<string, string> };
if (!workspaceConfig.catalog) {
return null;
}
// catalog: 协议格式为 catalog:packageName 或 catalog:packageName@version
const catalogKey = catalogRef.replace('catalog:', '');
return workspaceConfig.catalog[catalogKey] || null;
} catch {
return null;
}
}

/**
* Get dependency version based on protocol type
* - workspace:*: Read from workspace package.json
* - catalog:: Read from pnpm-workspace.yaml catalog
* - normal: Read from node_modules
*/
export async function getDepVersion(packageName: string, cwd: string): Promise<string | null> {
try {
// Get current package.json to check dependency protocol
const currentPkgPath = path.join(cwd, 'package.json');
if (!fs.existsSync(currentPkgPath)) {
// Fallback to node_modules if package.json not found
const depPkgPath = getDepPkgPath(packageName, cwd);
const depPkg = require(depPkgPath);
return depPkg.version;
}

const currentPkg = fs.readJsonSync(currentPkgPath);
const allDeps = {
...currentPkg.dependencies,
...currentPkg.devDependencies,
...currentPkg.peerDependencies,
};

const depVersion = allDeps[packageName];
if (!depVersion) {
// Not in dependencies, try to get from node_modules
const depPkgPath = getDepPkgPath(packageName, cwd);
const depPkg = require(depPkgPath);
return depPkg.version;
}

// Check protocol type
if (depVersion === 'workspace:*' || depVersion.startsWith('workspace:')) {
// For workspace:* protocol, read from workspace package.json
const workspacePkgPath = await getWorkspacePackagePath(packageName);
if (workspacePkgPath && fs.existsSync(workspacePkgPath)) {
const workspacePkg = fs.readJsonSync(workspacePkgPath);
return workspacePkg.version;
}
// Fallback to node_modules if workspace package not found
const depPkgPath = getDepPkgPath(packageName, cwd);
const depPkg = require(depPkgPath);
return depPkg.version;
}

if (depVersion.startsWith('catalog:')) {
// For catalog: protocol, read from pnpm-workspace.yaml catalog
const catalogVersion = getCatalogVersion(packageName, depVersion);
if (catalogVersion) {
return catalogVersion;
}
// Fallback to node_modules if catalog not found
const depPkgPath = getDepPkgPath(packageName, cwd);
const depPkg = require(depPkgPath);
return depPkg.version;
}

// For normal dependencies, read from node_modules
const depPkgPath = getDepPkgPath(packageName, cwd);
const depPkg = require(depPkgPath);
return depPkg.version;
} catch (error) {
console.error(`Error getting version for ${packageName}:`, error);
return null;
}
}

interface IDepPkg {
nccConfig: {
minify: boolean;
Expand Down
23 changes: 13 additions & 10 deletions packages/devkit/src/builder/buildable-packages/plugin-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
getPackagesFromFiles,
getSourcePackages,
} from '../build/utils/buildPluginUtils';
import { getDepPkgPath, getDepsConfig } from '../build/utils/getDepsConfig';
import { getDepsConfig, getDepVersion } from '../build/utils/getDepsConfig';
import { IBuildablePackage, IBuildContext } from '../interfaces';

const require = createRequire(import.meta.url);
Expand Down Expand Up @@ -144,23 +144,26 @@ export function deleteServerFiles(cwd: string, log: PkgLog) {
});
}

export function writeExternalPackageVersion(cwd: string, log: PkgLog) {
export async function writeExternalPackageVersion(cwd: string, log: PkgLog) {
log('write external version');
const sourceFiles = fg
.globSync(sourceGlobalFiles, { cwd, absolute: true })
.map((item) => fs.readFileSync(item, 'utf-8'));
const sourcePackages = getSourcePackages(sourceFiles);
const excludePackages = getExcludePackages(sourcePackages, external, pluginPrefix);
const data = excludePackages.reduce<Record<string, string>>((prev, packageName) => {
const data: Record<string, string> = {};

for (const packageName of excludePackages) {
try {
const depPkgPath = getDepPkgPath(packageName, cwd);
const depPkg = require(depPkgPath);
prev[packageName] = depPkg.version;
const version = await getDepVersion(packageName, cwd);
if (version) {
data[packageName] = version;
}
} catch (error) {
console.error(error);
console.error(`Error getting version for ${packageName}:`, error);
}
return prev;
}, {});
}

const externalVersionPath = path.join(cwd, target_dir, 'externalVersion.js');
fs.writeFileSync(externalVersionPath, `module.exports = ${JSON.stringify(data, null, 2)};`);
}
Expand Down Expand Up @@ -391,7 +394,7 @@ export class PluginPackage implements IBuildablePackage {

await buildPluginClient(this.dir, userConfig, this.context.sourcemap, log);
await buildPluginServer(this.dir, userConfig, this.context.sourcemap, log);
writeExternalPackageVersion(this.dir, log);
await writeExternalPackageVersion(this.dir, log);

if (this.context.dts) {
await buildDeclaration(this.dir, 'dist', log);
Expand Down
Loading
Loading