@@ -85,6 +85,60 @@ function sanitizeTarballPath(filePath: string): string {
85
85
return segments . join ( path . sep )
86
86
}
87
87
88
+ /**
89
+ * Remove a file or directory with safety protections.
90
+ * Minimal inline version of @socketsecurity/registry/lib/fs remove().
91
+ * Prevents catastrophic deletes by checking paths are within safe boundaries.
92
+ * @throws {Error } When attempting to delete protected paths.
93
+ */
94
+ async function remove (
95
+ filepath : string ,
96
+ options ?: { force ?: boolean } ,
97
+ ) : Promise < void > {
98
+ const absolutePath = path . resolve ( filepath )
99
+ const cwd = process . cwd ( )
100
+
101
+ // Safety check: prevent deleting cwd or parent directories unless forced.
102
+ if ( ! options ?. force ) {
103
+ // Check if trying to delete cwd itself.
104
+ if ( absolutePath === cwd ) {
105
+ throw new Error ( 'Cannot delete the current working directory' )
106
+ }
107
+
108
+ // Check if trying to delete outside SOCKET_HOME (catastrophic delete protection).
109
+ const relation = path . relative ( SOCKET_HOME , absolutePath )
110
+ const isInside = Boolean (
111
+ relation &&
112
+ relation !== '..' &&
113
+ ! relation . startsWith ( `..${ path . sep } ` ) &&
114
+ ! path . isAbsolute ( relation ) ,
115
+ )
116
+
117
+ if ( ! isInside ) {
118
+ throw new Error (
119
+ `Cannot delete files/directories outside SOCKET_HOME (${ SOCKET_HOME } ). ` +
120
+ `Attempted to delete: ${ absolutePath } ` ,
121
+ )
122
+ }
123
+ }
124
+
125
+ // Perform deletion.
126
+ try {
127
+ const stats = await fs . stat ( absolutePath )
128
+ if ( stats . isDirectory ( ) ) {
129
+ await fs . rm ( absolutePath , { recursive : true , force : true } )
130
+ } else {
131
+ await fs . unlink ( absolutePath )
132
+ }
133
+ } catch ( error ) {
134
+ const code = ( error as NodeJS . ErrnoException ) ?. code
135
+ // Silently ignore if file doesn't exist.
136
+ if ( code !== 'ENOENT' ) {
137
+ throw error
138
+ }
139
+ }
140
+ }
141
+
88
142
// ============================================================================
89
143
// Installation lock management
90
144
// ============================================================================
@@ -119,9 +173,7 @@ async function acquireLock(): Promise<string> {
119
173
// Process exists, wait and retry.
120
174
} catch {
121
175
// Process doesn't exist, remove stale lock.
122
- await fs . unlink ( lockPath ) . catch ( ( ) => {
123
- // Ignore, may have been removed by another process.
124
- } )
176
+ await remove ( lockPath )
125
177
continue
126
178
}
127
179
}
@@ -154,15 +206,12 @@ async function acquireLock(): Promise<string> {
154
206
*/
155
207
async function releaseLock ( lockPath : string ) : Promise < void > {
156
208
try {
157
- await fs . unlink ( lockPath )
209
+ await remove ( lockPath )
158
210
debugLog ( `Released installation lock: ${ lockPath } ` )
159
211
} catch ( error ) {
160
- const code = ( error as NodeJS . ErrnoException ) ?. code
161
- if ( code !== 'ENOENT' ) {
162
- console . error (
163
- `Warning: Failed to release lock ${ lockPath } : ${ formatError ( error ) } ` ,
164
- )
165
- }
212
+ console . error (
213
+ `Warning: Failed to release lock ${ lockPath } : ${ formatError ( error ) } ` ,
214
+ )
166
215
}
167
216
}
168
217
@@ -202,7 +251,7 @@ async function downloadAndInstallPackage(version: string): Promise<void> {
202
251
await extractTarball ( tarballPath )
203
252
204
253
// Remove tarball after successful extraction.
205
- await fs . unlink ( tarballPath ) . catch ( error => {
254
+ await remove ( tarballPath ) . catch ( error => {
206
255
console . error (
207
256
`Warning: Failed to remove tarball ${ tarballPath } : ${ formatError ( error ) } ` ,
208
257
)
@@ -216,7 +265,7 @@ async function downloadAndInstallPackage(version: string): Promise<void> {
216
265
217
266
// Clean up tarball if extraction failed.
218
267
if ( tarballPath ) {
219
- await fs . unlink ( tarballPath ) . catch ( ( ) => {
268
+ await remove ( tarballPath ) . catch ( ( ) => {
220
269
// Ignore - best effort cleanup.
221
270
} )
222
271
}
0 commit comments