mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-28 02:53:15 -04:00
fix: prevent circular symlinks in projects registry (#10432)
close #10411
This commit is contained in:
6
.changeset/crazy-toes-join.md
Normal file
6
.changeset/crazy-toes-join.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/package-store": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Do not add a symlink to the project into the store's project registry if the store is in a subdirectory of the project [#10411](https://github.com/pnpm/pnpm/issues/10411).
|
||||
@@ -5,6 +5,7 @@ import { createShortHash } from '@pnpm/crypto.hash'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { globalInfo } from '@pnpm/logger'
|
||||
import symlinkDir from 'symlink-dir'
|
||||
import isSubdir from 'is-subdir'
|
||||
|
||||
const PROJECTS_DIR = 'projects'
|
||||
|
||||
@@ -17,6 +18,10 @@ export function getProjectsRegistryDir (storeDir: string): string {
|
||||
* Creates a symlink in {storeDir}/projects/{hash} → {projectDir}
|
||||
*/
|
||||
export async function registerProject (storeDir: string, projectDir: string): Promise<void> {
|
||||
// Avoid creating circular symlinks when the store is inside the project directory
|
||||
if (isSubdir(projectDir, storeDir)) {
|
||||
return
|
||||
}
|
||||
const registryDir = getProjectsRegistryDir(storeDir)
|
||||
await fs.mkdir(registryDir, { recursive: true })
|
||||
const linkPath = path.join(registryDir, createShortHash(projectDir))
|
||||
|
||||
@@ -47,6 +47,18 @@ describe('projectRegistry', () => {
|
||||
const entries = await fs.readdir(projectsDir)
|
||||
expect(entries).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('does not create symlink when store is inside project directory', async () => {
|
||||
const projectDir = temporaryDirectory()
|
||||
const storeDir = path.join(projectDir, 'node_modules', '.pnpm-store')
|
||||
await fs.mkdir(storeDir, { recursive: true })
|
||||
|
||||
await registerProject(storeDir, projectDir)
|
||||
|
||||
// The projects directory should not be created since we skipped registration
|
||||
const projectsDir = path.join(storeDir, 'projects')
|
||||
await expect(fs.readdir(projectsDir)).rejects.toThrow(/ENOENT/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getRegisteredProjects()', () => {
|
||||
|
||||
@@ -428,8 +428,9 @@ describe('global virtual store prune', () => {
|
||||
'is-positive': '1.0.0',
|
||||
},
|
||||
})
|
||||
const cacheDir = path.resolve('cache')
|
||||
const storeDir = path.resolve('store')
|
||||
// Store should be OUTSIDE the project directory to ensure proper project registration
|
||||
const cacheDir = path.resolve('..', 'cache')
|
||||
const storeDir = path.resolve('..', 'store')
|
||||
|
||||
// Install with global virtual store enabled
|
||||
await execa('node', [
|
||||
|
||||
Reference in New Issue
Block a user