@@ -99,28 +99,64 @@ export async function getSshConfiguration(configurationPath: string, resolveIncl
99
99
return config ;
100
100
}
101
101
102
- async function resolveConfigIncludes ( config : Configuration , configPath : string ) : Promise < void > {
103
- for ( const entry of config ) {
104
- if ( isDirective ( entry ) && entry . param === 'Include' ) {
105
- let includePath : string = resolveHome ( entry . value ) ;
106
- if ( isWindows && ! ! includePath . match ( / ^ \/ [ a - z ] : / i) ) {
107
- includePath = includePath . substr ( 1 ) ;
108
- }
109
-
110
- if ( ! path . isAbsolute ( includePath ) ) {
111
- includePath = path . resolve ( path . dirname ( configPath ) , includePath ) ;
112
- }
113
-
114
- const pathsToGetFilesFrom : string [ ] = await globAsync ( includePath ) ;
102
+ function getProcessedPathKey ( filePath : string ) : string {
103
+ const absolutePath : string = path . resolve ( filePath ) ;
104
+ const normalizedPath : string = path . normalize ( absolutePath ) ;
105
+ return isWindows ? normalizedPath . toLowerCase ( ) : normalizedPath ;
106
+ }
115
107
116
- for ( const filePath of pathsToGetFilesFrom ) {
117
- await getIncludedConfigFile ( config , filePath ) ;
108
+ async function resolveConfigIncludes (
109
+ config : Configuration ,
110
+ configPath : string ,
111
+ processedIncludePaths ?: Set < string > ,
112
+ processedIncludeEntries ?: WeakSet < ConfigurationDirective >
113
+ ) : Promise < void > {
114
+ processedIncludePaths = processedIncludePaths ?? new Set < string > ( ) ;
115
+ processedIncludeEntries = processedIncludeEntries ?? new WeakSet < ConfigurationDirective > ( ) ;
116
+ const configKey : string = getProcessedPathKey ( configPath ) ;
117
+ if ( processedIncludePaths . has ( configKey ) ) {
118
+ return ;
119
+ }
120
+ processedIncludePaths . add ( configKey ) ;
121
+ try {
122
+ for ( const entry of config ) {
123
+ if ( isDirective ( entry ) && entry . param === 'Include' ) {
124
+ // Prevent duplicate expansion of the same Include directive within a single resolution pass.
125
+ if ( processedIncludeEntries . has ( entry ) ) {
126
+ continue ;
127
+ }
128
+ processedIncludeEntries . add ( entry ) ;
129
+ let includePath : string = resolveHome ( entry . value ) ;
130
+ if ( isWindows && ! ! includePath . match ( / ^ \/ [ a - z ] : / i) ) {
131
+ includePath = includePath . slice ( 1 ) ;
132
+ }
133
+
134
+ if ( ! path . isAbsolute ( includePath ) ) {
135
+ includePath = path . resolve ( path . dirname ( configPath ) , includePath ) ;
136
+ }
137
+
138
+ const pathsToGetFilesFrom : string [ ] = await globAsync ( includePath ) ;
139
+
140
+ for ( const filePath of pathsToGetFilesFrom ) {
141
+ const includeKey : string = getProcessedPathKey ( filePath ) ;
142
+ if ( processedIncludePaths . has ( includeKey ) ) {
143
+ continue ;
144
+ }
145
+ await getIncludedConfigFile ( config , filePath , processedIncludePaths , processedIncludeEntries ) ;
146
+ }
118
147
}
119
148
}
149
+ } finally {
150
+ processedIncludePaths . delete ( configKey ) ;
120
151
}
121
152
}
122
153
123
- async function getIncludedConfigFile ( config : Configuration , includePath : string ) : Promise < void > {
154
+ async function getIncludedConfigFile (
155
+ config : Configuration ,
156
+ includePath : string ,
157
+ processedIncludePaths : Set < string > ,
158
+ processedIncludeEntries : WeakSet < ConfigurationDirective >
159
+ ) : Promise < void > {
124
160
let includedContents : string ;
125
161
try {
126
162
includedContents = ( await fs . readFile ( includePath ) ) . toString ( ) ;
@@ -136,6 +172,7 @@ async function getIncludedConfigFile(config: Configuration, includePath: string)
136
172
getSshChannel ( ) . appendLine ( localize ( "failed.to.parse.SSH.config" , "Failed to parse SSH configuration file {0}: {1}" , includePath , ( err as Error ) . message ) ) ;
137
173
return ;
138
174
}
175
+ await resolveConfigIncludes ( parsedIncludedContents , includePath , processedIncludePaths , processedIncludeEntries ) ;
139
176
config . push ( ...parsedIncludedContents ) ;
140
177
}
141
178
0 commit comments