@@ -249,6 +249,189 @@ Host coder-vscode.dev.coder.com--*
249249 } )
250250} )
251251
252+ it ( "throws an error if there is a mismatched start and end block count" , async ( ) => {
253+ // The below config contains two start blocks and one end block.
254+ // This is a malformed config and should throw an error.
255+ // Previously were were simply taking the first occurrences of the start and
256+ // end blocks, which would potentially lead to loss of any content between the
257+ // missing end block and the next start block.
258+ const existentSSHConfig = `Host beforeconfig
259+ HostName before.config.tld
260+ User before
261+
262+ # --- START CODER VSCODE dev.coder.com ---
263+ Host coder-vscode.dev.coder.com--*
264+ ConnectTimeout 0
265+ LogLevel ERROR
266+ ProxyCommand some-command-here
267+ StrictHostKeyChecking no
268+ UserKnownHostsFile /dev/null
269+ # missing END CODER VSCODE dev.coder.com ---
270+
271+ Host donotdelete
272+ HostName dont.delete.me
273+ User please
274+
275+ # --- START CODER VSCODE dev.coder.com ---
276+ Host coder-vscode.dev.coder.com--*
277+ ConnectTimeout 0
278+ LogLevel ERROR
279+ ProxyCommand some-command-here
280+ StrictHostKeyChecking no
281+ UserKnownHostsFile /dev/null
282+ # --- END CODER VSCODE dev.coder.com ---
283+
284+ Host afterconfig
285+ HostName after.config.tld
286+ User after`
287+
288+ const sshConfig = new SSHConfig ( sshFilePath , mockFileSystem )
289+ mockFileSystem . readFile . mockResolvedValueOnce ( existentSSHConfig )
290+ await sshConfig . load ( )
291+
292+ // When we try to update the config, it should throw an error.
293+ await expect (
294+ sshConfig . update ( "dev.coder.com" , {
295+ Host : "coder-vscode.dev.coder.com--*" ,
296+ ProxyCommand : "some-command-here" ,
297+ ConnectTimeout : "0" ,
298+ StrictHostKeyChecking : "no" ,
299+ UserKnownHostsFile : "/dev/null" ,
300+ LogLevel : "ERROR" ,
301+ } ) ,
302+ ) . rejects . toThrow (
303+ `Malformed config: ssh config has 2 dev.coder.com START comments but 1 dev.coder.com END comments. Each START must have a matching END.` ,
304+ )
305+ } )
306+
307+ it ( "throws an error if there are more than one sections with the same label" , async ( ) => {
308+ const existentSSHConfig = `Host beforeconfig
309+ HostName before.config.tld
310+ User before
311+
312+ # --- START CODER VSCODE dev.coder.com ---
313+ Host coder-vscode.dev.coder.com--*
314+ ConnectTimeout 0
315+ LogLevel ERROR
316+ ProxyCommand some-command-here
317+ StrictHostKeyChecking no
318+ UserKnownHostsFile /dev/null
319+ # --- END CODER VSCODE dev.coder.com ---
320+
321+ Host donotdelete
322+ HostName dont.delete.me
323+ User please
324+
325+ # --- START CODER VSCODE dev.coder.com ---
326+ Host coder-vscode.dev.coder.com--*
327+ ConnectTimeout 0
328+ LogLevel ERROR
329+ ProxyCommand some-command-here
330+ StrictHostKeyChecking no
331+ UserKnownHostsFile /dev/null
332+ # --- END CODER VSCODE dev.coder.com ---
333+
334+ Host afterconfig
335+ HostName after.config.tld
336+ User after`
337+
338+ const sshConfig = new SSHConfig ( sshFilePath , mockFileSystem )
339+ mockFileSystem . readFile . mockResolvedValueOnce ( existentSSHConfig )
340+ await sshConfig . load ( )
341+
342+ // When we try to update the config, it should throw an error.
343+ await expect (
344+ sshConfig . update ( "dev.coder.com" , {
345+ Host : "coder-vscode.dev.coder.com--*" ,
346+ ProxyCommand : "some-command-here" ,
347+ ConnectTimeout : "0" ,
348+ StrictHostKeyChecking : "no" ,
349+ UserKnownHostsFile : "/dev/null" ,
350+ LogLevel : "ERROR" ,
351+ } ) ,
352+ ) . rejects . toThrow ( `Malformed config: ssh config has 2 dev.coder.com sections, please remove all but one.` )
353+ } )
354+
355+ it ( "correctly handles interspersed blocks with and without label" , async ( ) => {
356+ const existentSSHConfig = `Host beforeconfig
357+ HostName before.config.tld
358+ User before
359+
360+ # --- START CODER VSCODE ---
361+ Host coder-vscode.dev.coder.com--*
362+ ConnectTimeout 0
363+ LogLevel ERROR
364+ ProxyCommand some-command-here
365+ StrictHostKeyChecking no
366+ UserKnownHostsFile /dev/null
367+ # --- END CODER VSCODE ---
368+
369+ Host donotdelete
370+ HostName dont.delete.me
371+ User please
372+
373+ # --- START CODER VSCODE dev.coder.com ---
374+ Host coder-vscode.dev.coder.com--*
375+ ConnectTimeout 0
376+ LogLevel ERROR
377+ ProxyCommand some-command-here
378+ StrictHostKeyChecking no
379+ UserKnownHostsFile /dev/null
380+ # --- END CODER VSCODE dev.coder.com ---
381+
382+ Host afterconfig
383+ HostName after.config.tld
384+ User after`
385+
386+ const sshConfig = new SSHConfig ( sshFilePath , mockFileSystem )
387+ mockFileSystem . readFile . mockResolvedValueOnce ( existentSSHConfig )
388+ await sshConfig . load ( )
389+
390+ const expectedOutput = `Host beforeconfig
391+ HostName before.config.tld
392+ User before
393+
394+ # --- START CODER VSCODE ---
395+ Host coder-vscode.dev.coder.com--*
396+ ConnectTimeout 0
397+ LogLevel ERROR
398+ ProxyCommand some-command-here
399+ StrictHostKeyChecking no
400+ UserKnownHostsFile /dev/null
401+ # --- END CODER VSCODE ---
402+
403+ Host donotdelete
404+ HostName dont.delete.me
405+ User please
406+
407+ # --- START CODER VSCODE dev.coder.com ---
408+ Host coder-vscode.dev.coder.com--*
409+ ConnectTimeout 0
410+ LogLevel ERROR
411+ ProxyCommand some-command-here
412+ StrictHostKeyChecking no
413+ UserKnownHostsFile /dev/null
414+ # --- END CODER VSCODE dev.coder.com ---
415+
416+ Host afterconfig
417+ HostName after.config.tld
418+ User after`
419+
420+ await sshConfig . update ( "dev.coder.com" , {
421+ Host : "coder-vscode.dev.coder.com--*" ,
422+ ProxyCommand : "some-command-here" ,
423+ ConnectTimeout : "0" ,
424+ StrictHostKeyChecking : "no" ,
425+ UserKnownHostsFile : "/dev/null" ,
426+ LogLevel : "ERROR" ,
427+ } )
428+
429+ expect ( mockFileSystem . writeFile ) . toBeCalledWith ( sshFilePath , expectedOutput , {
430+ encoding : "utf-8" ,
431+ mode : 384 ,
432+ } )
433+ } )
434+
252435it ( "override values" , async ( ) => {
253436 mockFileSystem . readFile . mockRejectedValueOnce ( "No file found" )
254437 const sshConfig = new SSHConfig ( sshFilePath , mockFileSystem )
0 commit comments