Initial commit
This commit is contained in:
Родитель
1d16433435
Коммит
c8b8b1fe47
|
@ -1,43 +1,25 @@
|
|||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
*.ide
|
||||
[Oo]bj/
|
||||
[Bb]in/
|
||||
TestResults/
|
||||
.nuget/
|
||||
_ReSharper.*/
|
||||
/packages
|
||||
artifacts/
|
||||
PublishProfiles/
|
||||
*.user
|
||||
*.suo
|
||||
*.cache
|
||||
*.docstates
|
||||
_ReSharper.*
|
||||
nuget.exe
|
||||
*.psess
|
||||
*.vsp
|
||||
*.pidb
|
||||
*.userprefs
|
||||
*DS_Store
|
||||
*.ncrunchsolution
|
||||
*.*sdf
|
||||
*.ipch
|
||||
/*.vspx
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# =========================
|
||||
# Operating System Files
|
||||
# =========================
|
||||
|
||||
# OSX
|
||||
# =========================
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.22310.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGetPackageVerifier", "NuGetPackageVerifier\NuGetPackageVerifier.csproj", "{2ED95FF6-1941-4053-99CE-F9748048762C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2ED95FF6-1941-4053-99CE-F9748048762C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2ED95FF6-1941-4053-99CE-F9748048762C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2ED95FF6-1941-4053-99CE-F9748048762C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2ED95FF6-1941-4053-99CE-F9748048762C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,39 @@
|
|||
using NuGet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class AssemblyHasDocumentFileRule : IPackageVerifierRule
|
||||
{
|
||||
public IEnumerable<MyPackageIssue> Validate(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger)
|
||||
{
|
||||
if (!package.IsSatellitePackage())
|
||||
{
|
||||
IEnumerable<string> allXmlFiles =
|
||||
from file in package.GetLibFiles()
|
||||
select file.Path into path
|
||||
where path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)
|
||||
select path;
|
||||
|
||||
foreach (IPackageFile current in package.GetLibFiles())
|
||||
{
|
||||
string assemblyPath = current.Path;
|
||||
// TODO: Does this need to check for just managed code?
|
||||
if (assemblyPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string docFilePath = Path.ChangeExtension(assemblyPath, ".xml");
|
||||
if (!allXmlFiles.Contains(docFilePath, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
yield return PackageIssueFactory.AssemblyHasNoDocFile(assemblyPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using NuGet;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class AssemblyStrongNameRule : IPackageVerifierRule
|
||||
{
|
||||
public IEnumerable<MyPackageIssue> Validate(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger)
|
||||
{
|
||||
foreach (IPackageFile currentFile in package.GetFiles())
|
||||
{
|
||||
string extension = Path.GetExtension(currentFile.Path);
|
||||
if (extension.Equals(".dll", StringComparison.OrdinalIgnoreCase) ||
|
||||
extension.Equals(".exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string assemblyPath = Path.ChangeExtension(Path.Combine(Path.GetTempPath(), Path.GetTempFileName()), extension);
|
||||
var isManagedCode = false;
|
||||
var isStrongNameSigned = false;
|
||||
int hresult = 0;
|
||||
try
|
||||
{
|
||||
using (Stream packageFileStream = currentFile.GetStream())
|
||||
{
|
||||
var _assemblyBytes = new byte[packageFileStream.Length];
|
||||
packageFileStream.Read(_assemblyBytes, 0, _assemblyBytes.Length);
|
||||
|
||||
using (var fileStream = new FileStream(assemblyPath, FileMode.Create))
|
||||
{
|
||||
packageFileStream.Seek(0, SeekOrigin.Begin);
|
||||
packageFileStream.CopyTo(fileStream);
|
||||
fileStream.Flush(true);
|
||||
}
|
||||
|
||||
if (IsAssemblyManaged(assemblyPath))
|
||||
{
|
||||
isManagedCode = true;
|
||||
var clrStrongName = (IClrStrongName)RuntimeEnvironment.GetRuntimeInterfaceAsObject(new Guid("B79B0ACD-F5CD-409b-B5A5-A16244610B92"), new Guid("9FD93CCF-3280-4391-B3A9-96E1CDE77C8D"));
|
||||
bool verificationForced;
|
||||
hresult = clrStrongName.StrongNameSignatureVerificationEx(assemblyPath, true, out verificationForced);
|
||||
if (hresult == 0)
|
||||
{
|
||||
isStrongNameSigned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError("Error while verifying strong name signature for {0}: {1}", currentFile.Path, ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
File.Delete(assemblyPath);
|
||||
}
|
||||
}
|
||||
if (isManagedCode && !isStrongNameSigned)
|
||||
{
|
||||
yield return PackageIssueFactory.AssemblyNotStrongNameSigned(currentFile.Path, hresult);
|
||||
}
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
private static bool IsAssemblyManaged(string assemblyPath)
|
||||
{
|
||||
// From http://msdn.microsoft.com/en-us/library/ms173100.aspx
|
||||
try
|
||||
{
|
||||
var testAssembly = AssemblyName.GetAssemblyName(assemblyPath);
|
||||
return true;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// The file cannot be found
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
// The file is not an assembly
|
||||
}
|
||||
catch (FileLoadException)
|
||||
{
|
||||
// The assembly has already been loaded
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[ComConversionLoss, Guid("9FD93CCF-3280-4391-B3A9-96E1CDE77C8D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[ComImport]
|
||||
internal interface IClrStrongName
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int GetHashFromAssemblyFile([MarshalAs(UnmanagedType.LPStr)] [In] string pszFilePath, [MarshalAs(UnmanagedType.U4)] [In] [Out] ref int piHashAlg, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] [Out] byte[] pbHash, [MarshalAs(UnmanagedType.U4)] [In] int cchHash, [MarshalAs(UnmanagedType.U4)] out int pchHash);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int GetHashFromAssemblyFileW([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzFilePath, [MarshalAs(UnmanagedType.U4)] [In] [Out] ref int piHashAlg, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] [Out] byte[] pbHash, [MarshalAs(UnmanagedType.U4)] [In] int cchHash, [MarshalAs(UnmanagedType.U4)] out int pchHash);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int GetHashFromBlob([In] IntPtr pbBlob, [MarshalAs(UnmanagedType.U4)] [In] int cchBlob, [MarshalAs(UnmanagedType.U4)] [In] [Out] ref int piHashAlg, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] [Out] byte[] pbHash, [MarshalAs(UnmanagedType.U4)] [In] int cchHash, [MarshalAs(UnmanagedType.U4)] out int pchHash);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int GetHashFromFile([MarshalAs(UnmanagedType.LPStr)] [In] string pszFilePath, [MarshalAs(UnmanagedType.U4)] [In] [Out] ref int piHashAlg, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] [Out] byte[] pbHash, [MarshalAs(UnmanagedType.U4)] [In] int cchHash, [MarshalAs(UnmanagedType.U4)] out int pchHash);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int GetHashFromFileW([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzFilePath, [MarshalAs(UnmanagedType.U4)] [In] [Out] ref int piHashAlg, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] [Out] byte[] pbHash, [MarshalAs(UnmanagedType.U4)] [In] int cchHash, [MarshalAs(UnmanagedType.U4)] out int pchHash);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int GetHashFromHandle([In] IntPtr hFile, [MarshalAs(UnmanagedType.U4)] [In] [Out] ref int piHashAlg, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] [Out] byte[] pbHash, [MarshalAs(UnmanagedType.U4)] [In] int cchHash, [MarshalAs(UnmanagedType.U4)] out int pchHash);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
int StrongNameCompareAssemblies([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzAssembly1, [MarshalAs(UnmanagedType.LPWStr)] [In] string pwzAssembly2, [MarshalAs(UnmanagedType.U4)] out int dwResult);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameFreeBuffer([In] IntPtr pbMemory);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameGetBlob([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzFilePath, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] [Out] byte[] pbBlob, [MarshalAs(UnmanagedType.U4)] [In] [Out] ref int pcbBlob);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameGetBlobFromImage([In] IntPtr pbBase, [MarshalAs(UnmanagedType.U4)] [In] int dwLength, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] [Out] byte[] pbBlob, [MarshalAs(UnmanagedType.U4)] [In] [Out] ref int pcbBlob);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameGetPublicKey([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzKeyContainer, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] [In] byte[] pbKeyBlob, [MarshalAs(UnmanagedType.U4)] [In] int cbKeyBlob, out IntPtr ppbPublicKeyBlob, [MarshalAs(UnmanagedType.U4)] out int pcbPublicKeyBlob);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
int StrongNameHashSize([MarshalAs(UnmanagedType.U4)] [In] int ulHashAlg, [MarshalAs(UnmanagedType.U4)] out int cbSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameKeyDelete([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzKeyContainer);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameKeyGen([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzKeyContainer, [MarshalAs(UnmanagedType.U4)] [In] int dwFlags, out IntPtr ppbKeyBlob, [MarshalAs(UnmanagedType.U4)] out int pcbKeyBlob);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameKeyGenEx([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzKeyContainer, [MarshalAs(UnmanagedType.U4)] [In] int dwFlags, [MarshalAs(UnmanagedType.U4)] [In] int dwKeySize, out IntPtr ppbKeyBlob, [MarshalAs(UnmanagedType.U4)] out int pcbKeyBlob);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameKeyInstall([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzKeyContainer, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] [In] byte[] pbKeyBlob, [MarshalAs(UnmanagedType.U4)] [In] int cbKeyBlob);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameSignatureGeneration([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzFilePath, [MarshalAs(UnmanagedType.LPWStr)] [In] string pwzKeyContainer, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] [In] byte[] pbKeyBlob, [MarshalAs(UnmanagedType.U4)] [In] int cbKeyBlob, [In] [Out] IntPtr ppbSignatureBlob, [MarshalAs(UnmanagedType.U4)] out int pcbSignatureBlob);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameSignatureGenerationEx([MarshalAs(UnmanagedType.LPWStr)] [In] string wszFilePath, [MarshalAs(UnmanagedType.LPWStr)] [In] string wszKeyContainer, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] [In] byte[] pbKeyBlob, [MarshalAs(UnmanagedType.U4)] [In] int cbKeyBlob, [In] [Out] IntPtr ppbSignatureBlob, [MarshalAs(UnmanagedType.U4)] out int pcbSignatureBlob, [MarshalAs(UnmanagedType.U4)] [In] int dwFlags);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameSignatureSize([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] [In] byte[] pbPublicKeyBlob, [MarshalAs(UnmanagedType.U4)] [In] int cbPublicKeyBlob, [MarshalAs(UnmanagedType.U4)] out int pcbSize);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
int StrongNameSignatureVerification([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzFilePath, [MarshalAs(UnmanagedType.U4)] [In] int dwInFlags, [MarshalAs(UnmanagedType.U4)] out int dwOutFlags);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
int StrongNameSignatureVerificationEx([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzFilePath, [MarshalAs(UnmanagedType.I1)] [In] bool fForceVerification, [MarshalAs(UnmanagedType.I1)] out bool fWasVerified);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
[return: MarshalAs(UnmanagedType.U4)]
|
||||
int StrongNameSignatureVerificationFromImage([In] IntPtr pbBase, [MarshalAs(UnmanagedType.U4)] [In] int dwLength, [MarshalAs(UnmanagedType.U4)] [In] int dwInFlags, [MarshalAs(UnmanagedType.U4)] out int dwOutFlags);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameTokenFromAssembly([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzFilePath, out IntPtr ppbStrongNameToken, [MarshalAs(UnmanagedType.U4)] out int pcbStrongNameToken);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameTokenFromAssemblyEx([MarshalAs(UnmanagedType.LPWStr)] [In] string pwzFilePath, out IntPtr ppbStrongNameToken, [MarshalAs(UnmanagedType.U4)] out int pcbStrongNameToken, out IntPtr ppbPublicKeyBlob, [MarshalAs(UnmanagedType.U4)] out int pcbPublicKeyBlob);
|
||||
|
||||
[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
|
||||
int StrongNameTokenFromPublicKey([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] [In] byte[] pbPublicKeyBlob, [MarshalAs(UnmanagedType.U4)] [In] int cbPublicKeyBlob, out IntPtr ppbStrongNameToken, [MarshalAs(UnmanagedType.U4)] out int pcbStrongNameToken);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
using NuGet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class AuthenticodeSigningRule : IPackageVerifierRule
|
||||
{
|
||||
public IEnumerable<MyPackageIssue> Validate(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger)
|
||||
{
|
||||
string packagePath = packageRepo.Source + "\\" + package.Id + "." + package.Version.ToString() + ".nupkg";
|
||||
string nupkgWithoutExt = Path.Combine(Path.GetDirectoryName(packagePath), Path.GetFileNameWithoutExtension(packagePath));
|
||||
try
|
||||
{
|
||||
UnzipPackage(nupkgWithoutExt);
|
||||
|
||||
foreach (IPackageFile current in package.GetFiles())
|
||||
{
|
||||
//string packagePath = package.FileSystem.Root + "\\" + Id + "." + Version + ".nupkg"
|
||||
string extension = Path.GetExtension(current.Path);
|
||||
|
||||
// TODO: Need to add more extensions?
|
||||
if (extension.Equals(".dll", StringComparison.OrdinalIgnoreCase) ||
|
||||
extension.Equals(".exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string pathOfFileToScan = Path.Combine(nupkgWithoutExt, current.Path);
|
||||
var realAssemblyPath = pathOfFileToScan;
|
||||
if (!File.Exists(realAssemblyPath))
|
||||
{
|
||||
realAssemblyPath = pathOfFileToScan.Replace("+", "%2B");
|
||||
if (!File.Exists(realAssemblyPath))
|
||||
{
|
||||
logger.LogError("The assembly '{0}' in this package can't be found (a bug in this tool, most likely).", current.Path);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
bool isAuthenticodeSigned = WinTrust.IsAuthenticodeSigned(realAssemblyPath);
|
||||
if (!isAuthenticodeSigned)
|
||||
{
|
||||
yield return PackageIssueFactory.PEFileNotAuthenticodeSigned(current.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanUpFolder(nupkgWithoutExt, logger);
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
private void UnzipPackage(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
Directory.Delete(path, recursive: true);
|
||||
}
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
ZipFile.ExtractToDirectory(path + ".nupkg", path);
|
||||
}
|
||||
|
||||
private void CleanUpFolder(string path, IPackageVerifierLogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
Directory.Delete(path, recursive: true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning("Couldn't clean temp unzip folder: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using NuGet;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public interface IPackageVerifierRule
|
||||
{
|
||||
IEnumerable<MyPackageIssue> Validate(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class IssueIgnore
|
||||
{
|
||||
public string PackageId { get; set; }
|
||||
public string IssueId { get; set; }
|
||||
public string Instance { get; set; }
|
||||
public string Justification { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NuGet;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class IssueProcessor
|
||||
{
|
||||
public IssueProcessor(IEnumerable<IssueIgnore> issuesToIgnore)
|
||||
{
|
||||
IssuesToIgnore = issuesToIgnore;
|
||||
}
|
||||
|
||||
public IEnumerable<IssueIgnore> IssuesToIgnore
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public IssueReport GetIssueReport(MyPackageIssue packageIssue, IPackage package)
|
||||
{
|
||||
var ignoredRules = IssuesToIgnore.Where(
|
||||
issueIgnore =>
|
||||
string.Equals(issueIgnore.IssueId, packageIssue.IssueId, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(issueIgnore.Instance, packageIssue.Instance, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(issueIgnore.PackageId, package.Id, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var firstIgnoreRule = ignoredRules.FirstOrDefault();
|
||||
|
||||
return new IssueReport(packageIssue, firstIgnoreRule != null, firstIgnoreRule?.Justification);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class IssueReport
|
||||
{
|
||||
public IssueReport(MyPackageIssue packageIssue, bool ignore, string ignoreJustification)
|
||||
{
|
||||
PackageIssue = packageIssue;
|
||||
IssueLevel = ignore ? LogLevel.Info : packageIssue.Level == MyPackageIssueLevel.Warning ? LogLevel.Warning : LogLevel.Error;
|
||||
IgnoreJustification = ignoreJustification;
|
||||
}
|
||||
|
||||
public MyPackageIssue PackageIssue { get; private set; }
|
||||
|
||||
public LogLevel IssueLevel { get; private set; }
|
||||
|
||||
public string IgnoreJustification { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace NuGetPackageVerifier.Logging
|
||||
{
|
||||
public interface IPackageVerifierLogger
|
||||
{
|
||||
void Log(LogLevel logLevel, string message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace NuGetPackageVerifier.Logging
|
||||
{
|
||||
public enum LogLevel
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
|
||||
namespace NuGetPackageVerifier.Logging
|
||||
{
|
||||
public class PackageVerifierLogger : IPackageVerifierLogger
|
||||
{
|
||||
public void Log(LogLevel logLevel, string message)
|
||||
{
|
||||
ConsoleColor foreColor;
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Error:
|
||||
foreColor = ConsoleColor.Red;
|
||||
break;
|
||||
|
||||
case LogLevel.Warning:
|
||||
foreColor = ConsoleColor.Yellow;
|
||||
break;
|
||||
|
||||
default:
|
||||
case LogLevel.Info:
|
||||
foreColor = ConsoleColor.Gray;
|
||||
break;
|
||||
}
|
||||
Console.ForegroundColor = foreColor;
|
||||
Console.WriteLine("{0}: {1}", logLevel.ToString().ToUpperInvariant(), message);
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
namespace NuGetPackageVerifier.Logging
|
||||
{
|
||||
public static class PackageVerifierLoggerExtensions
|
||||
{
|
||||
public static void Log(this IPackageVerifierLogger logger, LogLevel logLevel, string message, params object[] args)
|
||||
{
|
||||
logger.Log(logLevel, string.Format(message, args));
|
||||
}
|
||||
|
||||
public static void LogWarning(this IPackageVerifierLogger logger, string message, params object[] args)
|
||||
{
|
||||
logger.Log(LogLevel.Warning, message, args);
|
||||
}
|
||||
|
||||
public static void LogError(this IPackageVerifierLogger logger, string message, params object[] args)
|
||||
{
|
||||
logger.Log(LogLevel.Error, message, args);
|
||||
}
|
||||
|
||||
public static void LogInfo(this IPackageVerifierLogger logger, string message, params object[] args)
|
||||
{
|
||||
logger.Log(LogLevel.Info, message, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{2ED95FF6-1941-4053-99CE-F9748048762C}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>NuGetPackageVerifier</RootNamespace>
|
||||
<AssemblyName>NuGetPackageVerifier</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Web.XmlTransform">
|
||||
<HintPath>..\packages\Microsoft.Web.Xdt.2.1.0\lib\net40\Microsoft.Web.XmlTransform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NuGet.Core, Version=2.8.50926.602, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NuGet.Core.2.8.3\lib\net40-Client\NuGet.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Logging\PackageVerifierLoggerExtensions.cs" />
|
||||
<Compile Include="Logging\PackageVerifierLogger.cs" />
|
||||
<Compile Include="Logging\IPackageVerifierLogger.cs" />
|
||||
<Compile Include="Logging\LogLevel.cs" />
|
||||
<Compile Include="PackageAnalyzer.cs" />
|
||||
<Compile Include="AssemblyHasDocumentFileRule.cs" />
|
||||
<Compile Include="AssemblyStrongNameRule.cs" />
|
||||
<Compile Include="AuthenticodeSigningRule.cs" />
|
||||
<Compile Include="PackageIssueFactory.cs" />
|
||||
<Compile Include="IssueIgnore.cs" />
|
||||
<Compile Include="IssueProcessor.cs" />
|
||||
<Compile Include="IssueReport.cs" />
|
||||
<Compile Include="StrictSemanticVersionValidationRule.cs" />
|
||||
<Compile Include="PowerShellScriptIsSignedRule.cs" />
|
||||
<Compile Include="PackageIssue.cs" />
|
||||
<Compile Include="PackageIssueLevel.cs" />
|
||||
<Compile Include="IPackageVerifierRule.cs" />
|
||||
<Compile Include="RequiredAttributesRule.cs" />
|
||||
<Compile Include="SatellitePackageRule.cs" />
|
||||
<Compile Include="WinTrust.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,31 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NuGet;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class PackageAnalyzer
|
||||
{
|
||||
private IList<IPackageVerifierRule> _rules = new List<IPackageVerifierRule>();
|
||||
|
||||
public IList<IPackageVerifierRule> Rules
|
||||
{
|
||||
get
|
||||
{
|
||||
return _rules;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<MyPackageIssue> AnalyzePackage(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger)
|
||||
{
|
||||
IEnumerable<MyPackageIssue> packageIssues = new List<MyPackageIssue>();
|
||||
foreach (var rule in Rules)
|
||||
{
|
||||
var issues = rule.Validate(packageRepo, package, logger).ToList();
|
||||
packageIssues = packageIssues.Concat(issues);
|
||||
}
|
||||
return packageIssues;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class MyPackageIssue
|
||||
{
|
||||
public MyPackageIssue(string issueId, string issue, MyPackageIssueLevel level)
|
||||
: this(issueId, instance: null, issue: issue, level: level)
|
||||
{
|
||||
}
|
||||
|
||||
public MyPackageIssue(string issueId, string instance, string issue, MyPackageIssueLevel level)
|
||||
{
|
||||
Instance = instance;
|
||||
IssueId = issueId;
|
||||
Issue = issue;
|
||||
Level = level;
|
||||
}
|
||||
|
||||
public MyPackageIssueLevel Level
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string Issue
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string IssueId
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public string Instance
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} @ {1}: {2}: {3}", IssueId, Instance, Level.ToString().ToUpperInvariant(), Issue);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using NuGet;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public static class PackageIssueFactory
|
||||
{
|
||||
public static MyPackageIssue AssemblyNotStrongNameSigned(string assemblyPath, int hResult)
|
||||
{
|
||||
// TODO: Translate common HRESULTS http://blogs.msdn.com/b/yizhang/
|
||||
return new MyPackageIssue("SIGN_STRONGNAME", assemblyPath, string.Format("The managed assembly '{0}' in this package is either not signed or is delay signed. HRESULT=0x{1:X}", assemblyPath, hResult), MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue NotSemanticVersion(SemanticVersion version)
|
||||
{
|
||||
return new MyPackageIssue("VERSION_NOTSEMANTIC",
|
||||
string.Format("Version '{0}' does not follow semantic versioning guidelines.", version), MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue Satellite_PackageSummaryNotLocalized()
|
||||
{
|
||||
return new MyPackageIssue("LOC_SUMMARY", "Package summary is not localized correctly", MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue Satellite_PackageTitleNotLocalized()
|
||||
{
|
||||
return new MyPackageIssue("LOC_TITLE", "Package title is not localized correctly", MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue Satellite_PackageDescriptionNotLocalized()
|
||||
{
|
||||
return new MyPackageIssue("LOC_DESC", "Package description is not localized correctly", MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue RequiredCopyright()
|
||||
{
|
||||
return RequiredCore("NUSPEC_COPYRIGHT", "Copyright", MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue RequiredLicenseUrl()
|
||||
{
|
||||
return RequiredCore("NUSPEC_LICENSEURL", "License Url", MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue RequiredIconUrl()
|
||||
{
|
||||
return RequiredCore("NUSPEC_ICONURL", "Icon Url", MyPackageIssueLevel.Warning);
|
||||
}
|
||||
|
||||
public static MyPackageIssue RequiredTags()
|
||||
{
|
||||
return RequiredCore("NUSPEC_TAGS", "Tags", MyPackageIssueLevel.Warning);
|
||||
}
|
||||
|
||||
public static MyPackageIssue RequiredTitle()
|
||||
{
|
||||
return RequiredCore("NUSPEC_TITLE", "Title", MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue RequiredSummary()
|
||||
{
|
||||
return RequiredCore("NUSPEC_SUMMARY", "Summary", MyPackageIssueLevel.Warning);
|
||||
}
|
||||
|
||||
public static MyPackageIssue RequiredProjectUrl()
|
||||
{
|
||||
return RequiredCore("NUSPEC_PROJECTURL", "Project Url", MyPackageIssueLevel.Warning);
|
||||
}
|
||||
|
||||
public static MyPackageIssue RequiredRequireLicenseAcceptanceTrue()
|
||||
{
|
||||
return new MyPackageIssue("NUSPEC_ACCEPTLICENSE", string.Format("NuSpec Require License Acceptance is not set to true"), MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
private static MyPackageIssue RequiredCore(string issueId, string attributeName, MyPackageIssueLevel issueLevel)
|
||||
{
|
||||
return new MyPackageIssue(issueId, string.Format("NuSpec {0} attribute is missing", attributeName), issueLevel);
|
||||
}
|
||||
|
||||
public static MyPackageIssue PowerShellScriptNotSigned(string scriptPath)
|
||||
{
|
||||
return new MyPackageIssue("SIGN_POWERSHELL", scriptPath, string.Format("The PowerShell script '{0}' is not signed.", scriptPath), MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue PEFileNotAuthenticodeSigned(string assemblyPath)
|
||||
{
|
||||
return new MyPackageIssue("SIGN_AUTHENTICODE", assemblyPath, string.Format("The PE file '{0}' in this package is not authenticode signed.", assemblyPath), MyPackageIssueLevel.Error);
|
||||
}
|
||||
|
||||
public static MyPackageIssue AssemblyHasNoDocFile(string assemblyPath)
|
||||
{
|
||||
return new MyPackageIssue("DOC_MISSING", assemblyPath, string.Format("The assembly '{0}' doesn't have a corresponding XML document file.", assemblyPath), MyPackageIssueLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public enum MyPackageIssueLevel
|
||||
{
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using NuGet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class PowerShellScriptIsSignedRule : IPackageVerifierRule
|
||||
{
|
||||
private static readonly string[] PowerShellExtensions = new string[]
|
||||
{
|
||||
".ps1",
|
||||
".psm1",
|
||||
".psd1",
|
||||
".ps1xml"
|
||||
};
|
||||
|
||||
public IEnumerable<MyPackageIssue> Validate(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger)
|
||||
{
|
||||
foreach (IPackageFile current in package.GetFiles())
|
||||
{
|
||||
string extension = Path.GetExtension(current.Path);
|
||||
if (PowerShellScriptIsSignedRule.PowerShellExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!VerifySigned(current))
|
||||
{
|
||||
yield return PackageIssueFactory.PowerShellScriptNotSigned(current.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
private static bool VerifySigned(IPackageFile packageFile)
|
||||
{
|
||||
bool result;
|
||||
using (Stream stream = packageFile.GetStream())
|
||||
{
|
||||
System.IO.StreamReader streamReader = new System.IO.StreamReader(stream);
|
||||
string text = streamReader.ReadToEnd();
|
||||
result = (text.IndexOf("# SIG # Begin signature block", StringComparison.OrdinalIgnoreCase) > -1 && text.IndexOf("# SIG # End signature block", StringComparison.OrdinalIgnoreCase) > -1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NuGet;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
private const int ReturnOk = 0;
|
||||
private const int ReturnBadArgs = 1;
|
||||
private const int ReturnErrorsOrWarnings = 2;
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
// TODO: Take a switch saying whether to use TeamCity logger
|
||||
|
||||
// TODO: Add a way to read ignores from file
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
Console.WriteLine(@"USAGE: NuGetSuperBVT.exe c:\path\to\packages");
|
||||
return ReturnBadArgs;
|
||||
}
|
||||
|
||||
var totalTimeStopWatch = Stopwatch.StartNew();
|
||||
|
||||
var nupkgsPath = args[0];
|
||||
|
||||
var issuesToIgnore = new[]
|
||||
{
|
||||
new IssueIgnore { IssueId = "NUSPEC_TAGS", Instance = null, PackageId = "EntityFramework", Justification = "Because no tags" },
|
||||
new IssueIgnore { IssueId = "NUSPEC_SUMMARY", Instance = null, PackageId = "EntityFramework", Justification = "Because no summary" },
|
||||
new IssueIgnore { IssueId = "XYZ", Instance = "ZZZ", PackageId = "ABC", Justification = "Because" },
|
||||
new IssueIgnore { IssueId = "XYZ", Instance = "ZZZ", PackageId = "ABC", Justification = "Because" },
|
||||
new IssueIgnore { IssueId = "XYZ", Instance = "ZZZ", PackageId = "ABC", Justification = "Because" },
|
||||
new IssueIgnore { IssueId = "XYZ", Instance = "ZZZ", PackageId = "ABC", Justification = "Because" },
|
||||
};
|
||||
var issueProcessor = new IssueProcessor(issuesToIgnore);
|
||||
|
||||
var analyzer = new PackageAnalyzer();
|
||||
analyzer.Rules.Add(new AssemblyHasDocumentFileRule());
|
||||
analyzer.Rules.Add(new AssemblyStrongNameRule());
|
||||
analyzer.Rules.Add(new AuthenticodeSigningRule());
|
||||
analyzer.Rules.Add(new PowerShellScriptIsSignedRule());
|
||||
analyzer.Rules.Add(new RequiredAttributesRule());
|
||||
analyzer.Rules.Add(new SatellitePackageRule());
|
||||
analyzer.Rules.Add(new StrictSemanticVersionValidationRule());
|
||||
|
||||
var logger = new PackageVerifierLogger();
|
||||
|
||||
// TODO: Switch this to a custom IFileSystem that has only the packages we want (maybe?)
|
||||
var localPackageRepo = new LocalPackageRepository(nupkgsPath);
|
||||
|
||||
var numPackagesInRepo = localPackageRepo.GetPackages().Count();
|
||||
logger.LogInfo("Found {0} packages in {1}", numPackagesInRepo, nupkgsPath);
|
||||
|
||||
bool anyErrorOrWarnings = false;
|
||||
|
||||
foreach (var package in localPackageRepo.GetPackages())
|
||||
{
|
||||
var packageTimeStopWatch = Stopwatch.StartNew();
|
||||
|
||||
logger.LogInfo("Analyzing {0} ({1})", package.Id, package.Version);
|
||||
var issues = analyzer.AnalyzePackage(localPackageRepo, package, logger).ToList();
|
||||
|
||||
var issuesToReport = issues.Select(issue => issueProcessor.GetIssueReport(issue, package)).ToList();
|
||||
|
||||
if (issuesToReport.Count > 0)
|
||||
{
|
||||
var infos = issuesToReport.Where(issueReport => issueReport.IssueLevel == LogLevel.Info).ToList();
|
||||
var warnings = issuesToReport.Where(issueReport => issueReport.IssueLevel == LogLevel.Warning).ToList();
|
||||
var errors = issuesToReport.Where(issueReport => issueReport.IssueLevel == LogLevel.Error).ToList();
|
||||
|
||||
LogLevel errorLevel = LogLevel.Info;
|
||||
if (warnings.Count > 0)
|
||||
{
|
||||
errorLevel = LogLevel.Warning;
|
||||
anyErrorOrWarnings = true;
|
||||
}
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
errorLevel = LogLevel.Error;
|
||||
anyErrorOrWarnings = true;
|
||||
}
|
||||
logger.Log(
|
||||
errorLevel,
|
||||
"{0} error(s), {1} warning(s), and {2} info(s) found with package {3} ({4})",
|
||||
errors.Count, warnings.Count, infos.Count, package.Id, package.Version);
|
||||
|
||||
foreach (var current in issuesToReport)
|
||||
{
|
||||
PrintPackageIssue(logger, current);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInfo("No issues found with package {0}", package.Id, package.Version);
|
||||
}
|
||||
|
||||
packageTimeStopWatch.Stop();
|
||||
logger.LogInfo("Took {0}ms", packageTimeStopWatch.ElapsedMilliseconds);
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
totalTimeStopWatch.Stop();
|
||||
logger.LogInfo("Total took {0}ms", totalTimeStopWatch.ElapsedMilliseconds);
|
||||
|
||||
|
||||
return anyErrorOrWarnings ? ReturnErrorsOrWarnings : ReturnOk;
|
||||
}
|
||||
|
||||
private static void PrintPackageIssue(IPackageVerifierLogger logger, IssueReport issue)
|
||||
{
|
||||
// TODO: Support this: https://confluence.jetbrains.com/display/TCD8/Build+Script+Interaction+with+TeamCity
|
||||
|
||||
var issueInfo = issue.PackageIssue.IssueId;
|
||||
if (issue.PackageIssue.Instance == null)
|
||||
{
|
||||
issueInfo += " @ " + issue.PackageIssue.Instance;
|
||||
}
|
||||
|
||||
logger.Log(issue.IssueLevel, "{0}: {1}", issueInfo, issue.PackageIssue.Issue);
|
||||
if (issue.IgnoreJustification != null)
|
||||
{
|
||||
logger.Log(issue.IssueLevel, "Justification: {0}", issue.IgnoreJustification);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("NuGetSuperBVT")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("NuGetSuperBVT")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2ed95ff6-1941-4053-99ce-f9748048762c")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,46 @@
|
|||
using System.Collections.Generic;
|
||||
using NuGet;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class RequiredAttributesRule : IPackageVerifierRule
|
||||
{
|
||||
public IEnumerable<MyPackageIssue> Validate(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger)
|
||||
{
|
||||
if (string.IsNullOrEmpty(package.Copyright))
|
||||
{
|
||||
yield return PackageIssueFactory.RequiredCopyright();
|
||||
}
|
||||
if (package.LicenseUrl == null)
|
||||
{
|
||||
yield return PackageIssueFactory.RequiredLicenseUrl();
|
||||
}
|
||||
if (package.IconUrl == null)
|
||||
{
|
||||
yield return PackageIssueFactory.RequiredIconUrl();
|
||||
}
|
||||
if (string.IsNullOrEmpty(package.Tags))
|
||||
{
|
||||
yield return PackageIssueFactory.RequiredTags();
|
||||
}
|
||||
if (string.IsNullOrEmpty(package.Title))
|
||||
{
|
||||
yield return PackageIssueFactory.RequiredTitle();
|
||||
}
|
||||
if (string.IsNullOrEmpty(package.Summary))
|
||||
{
|
||||
yield return PackageIssueFactory.RequiredSummary();
|
||||
}
|
||||
if (package.ProjectUrl == null)
|
||||
{
|
||||
yield return PackageIssueFactory.RequiredProjectUrl();
|
||||
}
|
||||
if (!package.RequireLicenseAcceptance)
|
||||
{
|
||||
yield return PackageIssueFactory.RequiredRequireLicenseAcceptanceTrue();
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System.Collections.Generic;
|
||||
using NuGet;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class SatellitePackageRule : IPackageVerifierRule
|
||||
{
|
||||
public IEnumerable<MyPackageIssue> Validate(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger)
|
||||
{
|
||||
if (package.IsSatellitePackage())
|
||||
{
|
||||
if (package.Summary.Contains("{"))
|
||||
{
|
||||
yield return PackageIssueFactory.Satellite_PackageSummaryNotLocalized();
|
||||
}
|
||||
if (package.Title.Contains("{"))
|
||||
{
|
||||
yield return PackageIssueFactory.Satellite_PackageTitleNotLocalized();
|
||||
}
|
||||
if (package.Description.Contains("{"))
|
||||
{
|
||||
yield return PackageIssueFactory.Satellite_PackageDescriptionNotLocalized();
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
using NuGet;
|
||||
using NuGetPackageVerifier.Logging;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
public class StrictSemanticVersionValidationRule : IPackageVerifierRule
|
||||
{
|
||||
public IEnumerable<MyPackageIssue> Validate(IPackageRepository packageRepo, IPackage package, IPackageVerifierLogger logger)
|
||||
{
|
||||
SemanticVersion semanticVersion;
|
||||
if (SemanticVersion.TryParseStrict(package.Version.ToString(), out semanticVersion))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return PackageIssueFactory.NotSemanticVersion(package.Version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NuGetPackageVerifier
|
||||
{
|
||||
// See http://msdn.microsoft.com/en-us/library/aa388205(v=VS.85).aspx
|
||||
public class WinTrust
|
||||
{
|
||||
// The GUID action ID for using the AuthentiCode policy provider (see softpub.h)
|
||||
private const string WINTRUST_ACTION_GENERIC_VERIFY_V2 = "{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}";
|
||||
|
||||
// See wintrust.h
|
||||
enum UIChoice
|
||||
{
|
||||
WTD_UI_ALL = 1,
|
||||
WTD_UI_NONE,
|
||||
WTD_UI_NOBAD,
|
||||
WTD_UI_NOGOOD
|
||||
}
|
||||
|
||||
enum RevocationChecks
|
||||
{
|
||||
WTD_REVOKE_NONE,
|
||||
WTD_REVOKE_WHOLECHAIN
|
||||
}
|
||||
|
||||
enum UnionChoice
|
||||
{
|
||||
WTD_CHOICE_FILE = 1,
|
||||
WTD_CHOICE_CATALOG,
|
||||
WTD_CHOICE_BLOB,
|
||||
WTD_CHOICE_SIGNER,
|
||||
WTD_CHOICE_CERT
|
||||
}
|
||||
|
||||
enum StateAction
|
||||
{
|
||||
WTD_STATEACTION_IGNORE,
|
||||
WTD_STATEACTION_VERIFY,
|
||||
WTD_STATEACTION_CLOSE,
|
||||
WTD_STATEACTION_AUTO_CACHE,
|
||||
WTD_STATEACTION_AUTO_CACHE_FLUSH
|
||||
}
|
||||
|
||||
enum Provider
|
||||
{
|
||||
WTD_USE_IE4_TRUST_FLAG = 0x00000001,
|
||||
WTD_NO_IE4_CHAIN_FLAG = 0x00000002,
|
||||
WTD_NO_POLICY_USAGE_FLAG = 0x00000004,
|
||||
WTD_REVOCATION_CHECK_NONE = 0x00000010,
|
||||
WTD_REVOCATION_CHECK_END_CERT = 0x00000020,
|
||||
WTD_REVOCATION_CHECK_CHAIN = 0x00000040,
|
||||
WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT = 0x00000080,
|
||||
WTD_SAFER_FLAG = 0x00000100,
|
||||
WTD_HASH_ONLY_FLAG = 0x00000200,
|
||||
WTD_USE_DEFAULT_OSVER_CHECK = 0x00000400,
|
||||
WTD_LIFETIME_SIGNING_FLAG = 0x00000800,
|
||||
WTD_CACHE_ONLY_URL_RETRIEVAL = 0x00001000
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct WinTrustData
|
||||
{
|
||||
public uint cbStruct;
|
||||
public IntPtr pPolicyCallbackData;
|
||||
public IntPtr pSIPClientData;
|
||||
public uint dwUIChoice;
|
||||
public uint fdwRevocationChecks;
|
||||
public uint dwUnionChoice;
|
||||
public IntPtr pFile; // We're not interested in other union members
|
||||
public uint dwStateAction;
|
||||
public IntPtr hWVTStateData;
|
||||
public IntPtr pwszURLReference;
|
||||
public uint dwProvFlags;
|
||||
public uint dwUIContext;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct WinTrustFileInfo
|
||||
{
|
||||
public uint cbStruct;
|
||||
[MarshalAs(UnmanagedType.LPTStr)]
|
||||
public string pcwszFilePath;
|
||||
public IntPtr hFile;
|
||||
public IntPtr pgKnownSubject;
|
||||
}
|
||||
|
||||
public static bool IsAuthenticodeSigned(string path)
|
||||
{
|
||||
WinTrustFileInfo fileInfo = new WinTrustFileInfo()
|
||||
{
|
||||
cbStruct = (uint)Marshal.SizeOf(typeof(WinTrustFileInfo)),
|
||||
pcwszFilePath = Path.GetFullPath(path),
|
||||
hFile = IntPtr.Zero,
|
||||
pgKnownSubject = IntPtr.Zero
|
||||
};
|
||||
|
||||
WinTrustData data = new WinTrustData()
|
||||
{
|
||||
cbStruct = (uint)Marshal.SizeOf(typeof(WinTrustData)),
|
||||
dwProvFlags = Convert.ToUInt32(Provider.WTD_SAFER_FLAG),
|
||||
dwStateAction = Convert.ToUInt32(StateAction.WTD_STATEACTION_IGNORE),
|
||||
dwUIChoice = Convert.ToUInt32(UIChoice.WTD_UI_NONE),
|
||||
dwUIContext = 0,
|
||||
dwUnionChoice = Convert.ToUInt32(UnionChoice.WTD_CHOICE_FILE),
|
||||
fdwRevocationChecks = Convert.ToUInt32(RevocationChecks.WTD_REVOKE_NONE),
|
||||
hWVTStateData = IntPtr.Zero,
|
||||
pFile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WinTrustFileInfo))),
|
||||
pPolicyCallbackData = IntPtr.Zero,
|
||||
pSIPClientData = IntPtr.Zero,
|
||||
pwszURLReference = IntPtr.Zero
|
||||
};
|
||||
|
||||
// TODO: Potential memory leak. Need to invetigate
|
||||
Marshal.StructureToPtr(fileInfo, data.pFile, false);
|
||||
|
||||
IntPtr pGuid = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid)));
|
||||
IntPtr pData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WinTrustData)));
|
||||
Marshal.StructureToPtr(data, pData, true);
|
||||
Marshal.StructureToPtr(new Guid(WINTRUST_ACTION_GENERIC_VERIFY_V2), pGuid, true);
|
||||
|
||||
uint result = WinVerifyTrust(IntPtr.Zero, pGuid, pData);
|
||||
|
||||
Marshal.FreeHGlobal(pGuid);
|
||||
Marshal.FreeHGlobal(pData);
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
[DllImport("wintrust.dll", SetLastError = true)]
|
||||
internal static extern uint WinVerifyTrust(IntPtr hWnd, IntPtr pgActionID, IntPtr pWinTrustData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Web.Xdt" version="2.1.0" targetFramework="net451" />
|
||||
<package id="NuGet.Core" version="2.8.3" targetFramework="net451" />
|
||||
</packages>
|
Загрузка…
Ссылка в новой задаче