@@ -97,16 +97,13 @@ interface IEntryContext {
9797 * The name of the symlink file that has been processed.
9898 */
9999 readonly symlinkFileNames : string [ ] ;
100- /**
101- * The name of the symlink folder that has been processed.
102- */
103- readonly symlinkFolders : { folder : string , realpath : string } [ ] ;
104100 getFilePath ( ) : string ;
105101 /**
106102 * Whether the specified path is outside the target folder
107103 * @param tpath
108104 */
109105 isOutsideTargetFolder ( tpath : string ) : Promise < boolean > ;
106+ ensureFolder ( folder : string ) : Promise < void > ;
110107}
111108
112109class EntryContext implements IEntryContext {
@@ -115,6 +112,7 @@ class EntryContext implements IEntryContext {
115112 private symlinkAsFileOnWindows : boolean ) {
116113 this . _symlinkFileNames = [ ] ;
117114 this . _symlinkFolders = [ ] ;
115+ this . _ensuredFolders = [ ] ;
118116 }
119117 private _decodeEntryFileName : string ;
120118 public get decodeEntryFileName ( ) : string {
@@ -133,33 +131,48 @@ class EntryContext implements IEntryContext {
133131 public get symlinkFileNames ( ) : string [ ] {
134132 return this . _symlinkFileNames ;
135133 }
136- private _symlinkFolders : { folder : string , realpath : string } [ ] ;
137- public get symlinkFolders ( ) : { folder : string , realpath : string } [ ] {
138- return this . _symlinkFolders ;
134+ private _symlinkFolders : { folder : string , realpath : string } [ ]
135+ private _ensuredFolders : string [ ] ;
136+
137+ private addSymlinkFolder ( folder : string , realpath : string ) : void {
138+ const find = this . _symlinkFolders . find ( ( item ) => item . folder === folder ) ;
139+ if ( ! find ) {
140+ this . _symlinkFolders . push ( { folder, realpath } ) ;
141+ }
139142 }
140143
141144 public getFilePath ( ) : string {
142145 return path . join ( this . targetFolder , this . decodeEntryFileName ) ;
143146 }
144147
148+ public async ensureFolder ( folder : string ) : Promise < void > {
149+ if ( this . _ensuredFolders . includes ( folder ) ) {
150+ return ;
151+ }
152+ const folderStat = await exfs . ensureFolder ( folder ) ;
153+ if ( folderStat . isSymbolicLink ) {
154+ this . addSymlinkFolder ( folder , folderStat . realpath ! ) ;
155+ }
156+ this . _ensuredFolders . push ( folder ) ;
157+ }
158+
145159 public async isOutsideTargetFolder ( tpath : string ) : Promise < boolean > {
146- if ( this . symlinkFileNames . length === 0 &&
147- this . symlinkFolders . length === 0
148- ) {
160+ if ( this . _symlinkFileNames . length === 0 &&
161+ this . _symlinkFolders . length === 0 ) {
149162 return false ;
150163 }
151164 if ( process . platform === "win32" &&
152165 this . symlinkAsFileOnWindows ) {
153166 return false ;
154167 }
155- for ( const { folder, realpath } of this . symlinkFolders ) {
168+ for ( const { folder, realpath } of this . _symlinkFolders ) {
156169 if ( tpath . includes ( folder ) ) {
157170 if ( realpath . indexOf ( this . realTargetFolder ) !== 0 ) {
158171 return true ;
159172 }
160173 }
161174 }
162- for ( const fileName of this . symlinkFileNames ) {
175+ for ( const fileName of this . _symlinkFileNames ) {
163176 if ( tpath . includes ( fileName ) ) {
164177 const realFilePath = await exfs . realpath ( tpath ) ;
165178 if ( realFilePath . indexOf ( this . realTargetFolder ) !== 0 ) {
@@ -336,13 +349,7 @@ export class Unzip extends Cancelable {
336349 private async extractEntry ( zfile : yauzl . ZipFile , entry : yauzl . Entry , entryContext : IEntryContext , token : CancellationToken ) : Promise < void > {
337350 const filePath = entryContext . getFilePath ( ) ;
338351 const fileDir = path . dirname ( filePath ) ;
339- const folderStat = await exfs . ensureFolder ( fileDir ) ;
340- if ( folderStat . isSymbolicLink ) {
341- entryContext . symlinkFolders . push ( {
342- folder : fileDir ,
343- realpath : folderStat . realpath ! ,
344- } )
345- }
352+ await entryContext . ensureFolder ( fileDir ) ;
346353 const outside = await entryContext . isOutsideTargetFolder ( fileDir ) ;
347354 if ( outside ) {
348355 const error = new Error ( `Refuse to write file outside "${ entryContext . targetFolder } ", file: "${ filePath } "` ) ;
0 commit comments