Ejemplo n.º 1
0
        private static PoisonedFileEntry ExtractAndCheckZipFileOnly(IEnumerable <CatalogPackageEntry> catalogedPackages, string zipToCheck, string markerFileName, string tempDir, Queue <string> futureFilesToCheck)
        {
            var poisonEntry = new PoisonedFileEntry();

            poisonEntry.Path = zipToCheck;

            using (var sha = SHA256.Create())
                using (var stream = File.OpenRead(zipToCheck))
                {
                    poisonEntry.Hash = sha.ComputeHash(stream);
                }

            // first check for a matching poisoned or non-poisoned hash match:
            // - non-poisoned is a potential error where the package was redownloaded.
            // - poisoned is a use of a local package we were not expecting.
            foreach (var matchingCatalogedPackage in catalogedPackages.Where(c => c.OriginalHash.SequenceEqual(poisonEntry.Hash) || (c.PoisonedHash?.SequenceEqual(poisonEntry.Hash) ?? false)))
            {
                poisonEntry.Type |= PoisonType.Hash;
                var match = new PoisonMatch
                {
                    Package        = matchingCatalogedPackage.Path,
                    PackageId      = matchingCatalogedPackage.Id,
                    PackageVersion = matchingCatalogedPackage.Version,
                };
                poisonEntry.Matches.Add(match);
            }

            // now extract and look for the marker file
            if (ZipFileExtensions.Any(e => zipToCheck.ToLowerInvariant().EndsWith(e)))
            {
                ZipFile.ExtractToDirectory(zipToCheck, tempDir);
            }
            else if (TarFileExtensions.Any(e => zipToCheck.ToLowerInvariant().EndsWith(e)))
            {
                Directory.CreateDirectory(tempDir);
                var psi = new ProcessStartInfo("tar", $"xf {zipToCheck} -C {tempDir}");
                Process.Start(psi).WaitForExit();
            }
            else if (TarGzFileExtensions.Any(e => zipToCheck.ToLowerInvariant().EndsWith(e)))
            {
                Directory.CreateDirectory(tempDir);
                var psi = new ProcessStartInfo("tar", $"xzf {zipToCheck} -C {tempDir}");
                Process.Start(psi).WaitForExit();
            }
            else
            {
                throw new ArgumentOutOfRangeException($"Don't know how to decompress {zipToCheck}");
            }

            if (!string.IsNullOrWhiteSpace(markerFileName) && File.Exists(Path.Combine(tempDir, markerFileName)))
            {
                poisonEntry.Type |= PoisonType.NupkgFile;
            }

            foreach (var child in Directory.EnumerateFiles(tempDir, "*", SearchOption.AllDirectories))
            {
                // also add anything in this zip/package for checking
                futureFilesToCheck.Enqueue(child);
            }

            return(poisonEntry.Type != PoisonType.None ? poisonEntry : null);
        }
Ejemplo n.º 2
0
        private static PoisonedFileEntry CheckSingleFile(IEnumerable <CatalogPackageEntry> catalogedPackages, string rootPath, string fileToCheck)
        {
            // skip some common files that get copied verbatim from nupkgs - LICENSE, _._, etc as well as
            // file types that we never care about - text files, .gitconfig, etc.
            if (FileNamesToSkip.Any(f => Path.GetFileName(fileToCheck).ToLowerInvariant() == f.ToLowerInvariant()) ||
                FileExtensionsToSkip.Any(e => Path.GetExtension(fileToCheck).ToLowerInvariant() == e.ToLowerInvariant()))
            {
                return(null);
            }

            var poisonEntry = new PoisonedFileEntry();

            poisonEntry.Path = Utility.MakeRelativePath(fileToCheck, rootPath);

            // There seems to be some weird issues with using file streams both for hashing and assembly loading.
            // Copy everything into a memory stream to avoid these problems.
            var memStream = new MemoryStream();

            using (var stream = File.OpenRead(fileToCheck))
            {
                stream.CopyTo(memStream);
            }

            memStream.Seek(0, SeekOrigin.Begin);
            using (var sha = SHA256.Create())
            {
                poisonEntry.Hash = sha.ComputeHash(memStream);
            }

            foreach (var p in catalogedPackages)
            {
                // This hash can match either the original hash (we couldn't poison the file, or redownloaded it) or
                // the poisoned hash (the obvious failure case of a poisoned file leaked).
                foreach (var matchingCatalogedFile in p.Files.Where(f => f.OriginalHash.SequenceEqual(poisonEntry.Hash) || (f.PoisonedHash?.SequenceEqual(poisonEntry.Hash) ?? false)))
                {
                    poisonEntry.Type |= PoisonType.Hash;
                    var match = new PoisonMatch
                    {
                        File           = matchingCatalogedFile.Path,
                        Package        = p.Path,
                        PackageId      = p.Id,
                        PackageVersion = p.Version,
                    };
                    poisonEntry.Matches.Add(match);
                }
            }

            try
            {
                memStream.Seek(0, SeekOrigin.Begin);
                using (var asm = AssemblyDefinition.ReadAssembly(memStream))
                {
                    foreach (var a in asm.CustomAttributes)
                    {
                        foreach (var ca in a.ConstructorArguments)
                        {
                            if (ca.Type.Name == asm.MainModule.TypeSystem.String.Name)
                            {
                                if (ca.Value.ToString().Contains(PoisonMarker))
                                {
                                    poisonEntry.Type |= PoisonType.AssemblyAttribute;
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                // this is fine, it's just not an assembly.
            }

            return(poisonEntry.Type != PoisonType.None ? poisonEntry : null);
        }