@@ -46,15 +46,14 @@ function normalizePackageBin (pkg, changes) {
46
46
changes ?. push ( `removed invalid "bin[${ binKey } ]"` )
47
47
continue
48
48
}
49
- const base = path . join ( '/' , path . basename ( binKey . replace ( / \\ | : / g , '/' ) ) ) . slice ( 1 )
49
+ const base = path . basename ( secureAndUnixifyPath ( binKey ) )
50
50
if ( ! base ) {
51
51
delete pkg . bin [ binKey ]
52
52
changes ?. push ( `removed invalid "bin[${ binKey } ]"` )
53
53
continue
54
54
}
55
55
56
- const binTarget = path . join ( '/' , pkg . bin [ binKey ] . replace ( / \\ / g, '/' ) )
57
- . replace ( / \\ / g, '/' ) . slice ( 1 )
56
+ const binTarget = secureAndUnixifyPath ( pkg . bin [ binKey ] )
58
57
59
58
if ( ! binTarget ) {
60
59
delete pkg . bin [ binKey ]
@@ -83,6 +82,27 @@ function normalizePackageBin (pkg, changes) {
83
82
delete pkg . bin
84
83
}
85
84
85
+ function normalizePackageMan ( pkg , changes ) {
86
+ if ( pkg . man ) {
87
+ const mans = [ ]
88
+ for ( const man of ( Array . isArray ( pkg . man ) ? pkg . man : [ pkg . man ] ) ) {
89
+ if ( typeof man !== 'string' ) {
90
+ changes ?. push ( `removed invalid "man [${ man } ]"` )
91
+ } else {
92
+ mans . push ( secureAndUnixifyPath ( man ) )
93
+ }
94
+ }
95
+
96
+ if ( ! mans . length ) {
97
+ changes ?. push ( 'empty "man" was removed' )
98
+ } else {
99
+ pkg . man = mans
100
+ return pkg
101
+ }
102
+ }
103
+ delete pkg . man
104
+ }
105
+
86
106
function isCorrectlyEncodedName ( spec ) {
87
107
return ! spec . match ( / [ / @ \s + % : ] / ) &&
88
108
spec === encodeURIComponent ( spec )
@@ -103,6 +123,19 @@ function isValidScopedPackageName (spec) {
103
123
rest [ 1 ] === encodeURIComponent ( rest [ 1 ] )
104
124
}
105
125
126
+ function unixifyPath ( ref ) {
127
+ return ref . replace ( / \\ | : / g, '/' )
128
+ }
129
+
130
+ function securePath ( ref ) {
131
+ const secured = path . join ( '.' , path . join ( '/' , unixifyPath ( ref ) ) )
132
+ return secured . startsWith ( '.' ) ? '' : secured
133
+ }
134
+
135
+ function secureAndUnixifyPath ( ref ) {
136
+ return unixifyPath ( securePath ( ref ) )
137
+ }
138
+
106
139
// We don't want the `changes` array in here by default because this is a hot
107
140
// path for parsing packuments during install. So the calling method passes it
108
141
// in if it wants to track changes.
@@ -251,7 +284,7 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
251
284
252
285
// strip "node_modules/.bin" from scripts entries
253
286
// remove invalid scripts entries (non-strings)
254
- if ( steps . includes ( 'scripts' ) || steps . includes ( 'scriptpath' ) ) {
287
+ if ( ( steps . includes ( 'scripts' ) || steps . includes ( 'scriptpath' ) ) && data . scripts !== undefined ) {
255
288
const spre = / ^ ( \. [ / \\ ] ) ? n o d e _ m o d u l e s [ / \\ ] .b i n [ \\ / ] /
256
289
if ( typeof data . scripts === 'object' ) {
257
290
for ( const name in data . scripts ) {
@@ -325,13 +358,16 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
325
358
}
326
359
327
360
// expand directories.man
328
- if ( steps . includes ( 'mans' ) && ! data . man && data . directories ?. man ) {
329
- const manDir = data . directories . man
330
- const cwd = path . resolve ( pkg . path , manDir )
331
- const files = await lazyLoadGlob ( ) ( '**/*.[0-9]' , { cwd } )
332
- data . man = files . map ( man =>
333
- path . relative ( pkg . path , path . join ( cwd , man ) ) . split ( path . sep ) . join ( '/' )
334
- )
361
+ if ( steps . includes ( 'mans' ) ) {
362
+ if ( data . directories ?. man && ! data . man ) {
363
+ const manDir = secureAndUnixifyPath ( data . directories . man )
364
+ const cwd = path . resolve ( pkg . path , manDir )
365
+ const files = await lazyLoadGlob ( ) ( '**/*.[0-9]' , { cwd } )
366
+ data . man = files . map ( man =>
367
+ path . relative ( pkg . path , path . join ( cwd , man ) ) . split ( path . sep ) . join ( '/' )
368
+ )
369
+ }
370
+ normalizePackageMan ( data , changes )
335
371
}
336
372
337
373
if ( steps . includes ( 'bin' ) || steps . includes ( 'binDir' ) || steps . includes ( 'binRefs' ) ) {
@@ -340,7 +376,7 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
340
376
341
377
// expand "directories.bin"
342
378
if ( steps . includes ( 'binDir' ) && data . directories ?. bin && ! data . bin ) {
343
- const binsDir = path . resolve ( pkg . path , path . join ( '.' , path . join ( '/' , data . directories . bin ) ) )
379
+ const binsDir = path . resolve ( pkg . path , securePath ( data . directories . bin ) )
344
380
const bins = await lazyLoadGlob ( ) ( '**' , { cwd : binsDir } )
345
381
data . bin = bins . reduce ( ( acc , binFile ) => {
346
382
if ( binFile && ! binFile . startsWith ( '.' ) ) {
0 commit comments