Skip to content

Commit d7bb7f2

Browse files
authored
fix: Add more validation to msi installer. (#1716)
1 parent 2fcce95 commit d7bb7f2

File tree

1 file changed

+154
-7
lines changed

1 file changed

+154
-7
lines changed

src/Agent/MsiInstaller/InstallerActions/CustomActions.cs

+154-7
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ private void DeleteFolder(string path)
264264
{
265265
if (Directory.Exists(path))
266266
{
267-
Directory.Delete(path, true);
267+
SaferFileUtils.DeleteDirectoryAndContents(path, this);
268268
LogSuccess("Folder deleted at '{0}'.", path);
269269
}
270270
else
@@ -285,7 +285,7 @@ private void DeleteFile(string path)
285285
{
286286
if (File.Exists(path))
287287
{
288-
File.Delete(path);
288+
SaferFileUtils.FileDelete(path, this);
289289
LogSuccess("File deleted at '{0}'.", path);
290290
}
291291
else
@@ -480,7 +480,7 @@ private void MigrateNewRelicXml()
480480
session.Log("Attempting to move file from {0} to {1}.", sourcePath, destinationPath);
481481
try
482482
{
483-
File.Copy(sourcePath, destinationPath);
483+
SaferFileUtils.FileCopy(sourcePath, destinationPath, this);
484484
session.Log("Moved file from {0} to {1}.", sourcePath, destinationPath);
485485
}
486486
catch (IOException)
@@ -543,7 +543,7 @@ private void MigrateCustomInstrumentation()
543543
}
544544
}
545545

546-
private static void CopyFolderContents(string source, string destination)
546+
private void CopyFolderContents(string source, string destination)
547547
{
548548
if (source == null) return;
549549
if (destination == null) return;
@@ -561,7 +561,7 @@ private static void CopyFolderContents(string source, string destination)
561561
// If the subfolder already exists don't try to copy.
562562
if (Directory.Exists(destinationPath)) continue;
563563

564-
Directory.Move(sourceDirectoryPath, destinationPath);
564+
SaferFileUtils.DirectoryMove(sourceDirectoryPath, destinationPath, this);
565565
}
566566

567567
// Copy all the files in the root of the directory.
@@ -571,9 +571,156 @@ private static void CopyFolderContents(string source, string destination)
571571
string fileName = Path.GetFileName(sourceFilePath);
572572
string destinationPath = destination + fileName;
573573

574-
File.Copy(sourceFilePath, destinationPath, true);
575-
File.Delete(sourceFilePath);
574+
SaferFileUtils.FileCopy(sourceFilePath, destinationPath, true, this);
575+
SaferFileUtils.FileDelete(sourceFilePath, this);
576576
}
577577
}
578578
}
579+
580+
/// <summary>
581+
/// This class is wrapper around the standard File and Directory classes
582+
/// that will check the file and directory structure for symlinks and junctions
583+
/// before attempting to copy or delete the file or directory. This code uses
584+
/// the presence of reparse points to approximate the usage of junctions or
585+
/// symlinks. No agent file or directory is expected to have a reparse point
586+
/// defined.
587+
/// </summary>
588+
internal static class SaferFileUtils
589+
{
590+
internal static void FileCopy(string source, string destination, MySession logger)
591+
{
592+
if (!FileIsSafeToUse(source, logger))
593+
{
594+
logger.Log("{0} was not copied.", source);
595+
return;
596+
}
597+
598+
File.Copy(source, destination);
599+
}
600+
601+
internal static void FileCopy(string source, string destination, bool overwrite, MySession logger)
602+
{
603+
if (!FileIsSafeToUse(source, logger))
604+
{
605+
logger.Log("{0} was not copied.", source);
606+
return;
607+
}
608+
609+
File.Copy(source, destination, overwrite);
610+
}
611+
612+
internal static void FileDelete(string fileNameAndPath, MySession logger)
613+
{
614+
if (!FileIsSafeToUse(fileNameAndPath, logger))
615+
{
616+
logger.Log("{0} was not deleted.", fileNameAndPath);
617+
return;
618+
}
619+
620+
File.Delete(fileNameAndPath);
621+
}
622+
623+
internal static void DeleteDirectoryAndContents(string path, MySession logger)
624+
{
625+
if (!DirectoryIsSafeToUse(path, logger))
626+
{
627+
logger.Log("{0} was not deleted.", path);
628+
return;
629+
}
630+
631+
Directory.Delete(path, true);
632+
}
633+
634+
internal static void DirectoryMove(string source, string destination, MySession logger)
635+
{
636+
if (!DirectoryIsSafeToUse(source, logger))
637+
{
638+
logger.Log("{0} was not moved.", source);
639+
return;
640+
}
641+
642+
Directory.Move(source, destination);
643+
}
644+
645+
private static bool FileIsSafeToUse(string fileNameAndPath, MySession logger)
646+
{
647+
if (!File.Exists(fileNameAndPath))
648+
{
649+
return false;
650+
}
651+
652+
var fileInfo = new FileInfo(fileNameAndPath);
653+
654+
if (FileSystemReportsAReparsePoint(fileInfo, logger))
655+
{
656+
return false;
657+
}
658+
659+
for (var directory = fileInfo.Directory; directory != null; directory = directory.Parent)
660+
{
661+
if (FileSystemReportsAReparsePoint(directory, logger))
662+
{
663+
return false;
664+
}
665+
}
666+
667+
return DirectoryAndParentsAreSafe(fileInfo.Directory, logger);
668+
}
669+
670+
private static bool DirectoryIsSafeToUse(string path, MySession logger)
671+
{
672+
if (!Directory.Exists(path))
673+
{
674+
return false;
675+
}
676+
677+
var directory = new DirectoryInfo(path);
678+
if (!DirectoryAndParentsAreSafe(directory, logger))
679+
{
680+
return false;
681+
}
682+
683+
foreach (var childDirectory in directory.GetDirectories("*", SearchOption.AllDirectories))
684+
{
685+
if (FileSystemReportsAReparsePoint(childDirectory, logger))
686+
{
687+
return false;
688+
}
689+
}
690+
691+
foreach (var childFile in directory.GetFiles("*", SearchOption.AllDirectories))
692+
{
693+
if (FileSystemReportsAReparsePoint(childFile, logger))
694+
{
695+
return false;
696+
}
697+
}
698+
699+
return true;
700+
}
701+
702+
private static bool DirectoryAndParentsAreSafe(DirectoryInfo directoryToCheck, MySession logger)
703+
{
704+
for (var directory = directoryToCheck; directory != null; directory = directory.Parent)
705+
{
706+
if (FileSystemReportsAReparsePoint(directory, logger))
707+
{
708+
return false;
709+
}
710+
}
711+
712+
return true;
713+
}
714+
715+
private static bool FileSystemReportsAReparsePoint(FileSystemInfo fsInfo, MySession logger)
716+
{
717+
if((fsInfo.Attributes & System.IO.FileAttributes.ReparsePoint) != 0)
718+
{
719+
logger.Log("Reparse point detected for {0}.", fsInfo.FullName);
720+
return true;
721+
}
722+
723+
return false;
724+
}
725+
}
579726
}

0 commit comments

Comments
 (0)