Skip to content

Commit 4d7af19

Browse files
authored
Merge branch 'dev' into dev
2 parents 1ca56bd + e94fd3a commit 4d7af19

File tree

420 files changed

+64987
-45804
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

420 files changed

+64987
-45804
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ x64/
2929
build/
3030
bld/
3131
src/*/[Bb]in/
32+
setup/**/*/[Bb]in/
33+
setup/[Bb]in/
3234
[Oo]bj/
3335

3436
# Roslyn cache directories

AzurePowershell.Test.targets

Lines changed: 568 additions & 529 deletions
Large diffs are not rendered by default.

appveyor.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ environment:
88
build_script:
99
- ps: |
1010
$ErrorActionPreference = "Stop"
11-
$response = Invoke-WebRequest ("https://api.github.com/repos/" + $env:APPVEYOR_REPO_NAME + "/releases/tags/" + $env:APPVEYOR_REPO_TAG_NAME) -Timeout 10
11+
$response = Invoke-WebRequest ("https://api.github.com/repos/" + $env:APPVEYOR_REPO_NAME + "/releases/latest") -Timeout 10
1212
if($response -ne $null -and $response.StatusCode -ne 200)
1313
{
1414
$host.SetShouldExit(1)
1515
}
1616
17-
$unzip_path = "C:\projects\azure-powershell-release"
17+
$unzip_path = "C:\projects\release"
1818
$unzip_file = $unzip_path + "zip"
1919
Invoke-WebRequest ($response.Content | ConvertFrom-Json).zipball_url -OutFile $unzip_file
2020
7z x $unzip_file -o"$unzip_path"
@@ -38,7 +38,7 @@ build_script:
3838
{
3939
continue
4040
}
41-
if((gc $psd1 | Out-String) -notmatch "ModuleVersion\s*=\s*$pattern")
41+
if((gc $psd1 | Out-String) -notmatch "ModuleVersion\s*=\s*'$pattern'")
4242
{
4343
continue
4444
}
@@ -48,12 +48,12 @@ build_script:
4848
test: off
4949
shallow_clone: true
5050
on_success:
51-
- git clone -q --branch=%target_branch% %content_repo% %TEMP%\Azure
52-
- cd %TEMP%\Azure
53-
- ps: ls $global:output -dir | % { copy $_.FullName (ls -dir | select -First 1) -Recurse -Force }
5451
- git config --global credential.helper store
5552
- ps: ac "$env:USERPROFILE\.git-credentials" "https://$($env:github_access_token):[email protected]`n"
5653
- git config --global user.email %email%
5754
- git config --global user.name %name%
55+
- git clone -q --branch=%target_branch% %content_repo% %TEMP%\Azure
56+
- cd %TEMP%\Azure
57+
- ps: ls $global:output -dir | % { copy $_.FullName (ls -dir | select -First 1) -Recurse -Force }
5858
- git add -A
59-
- git diff --quiet --exit-code --cached || git commit -m "Sync docs from source code repo to content repo." && git push origin %target_branch% && appveyor AddMessage "Content Updated"
59+
- git diff --quiet --exit-code --cached || git commit -m "Sync docs from source code repo to content repo." && git push origin %target_branch% && appveyor AddMessage "Content Updated"

build.proj

Lines changed: 134 additions & 92 deletions
Large diffs are not rendered by default.
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace PowerShellSetup.Tests
5+
{
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Diagnostics;
9+
using System.IO;
10+
using System.Linq;
11+
using System.Management.Automation;
12+
using System.Threading.Tasks;
13+
using Xunit;
14+
using Xunit.Abstractions;
15+
16+
/// <summary>
17+
/// Set of tests to run against MSI
18+
/// These set of tests are especially for scenario like:
19+
/// 1) Layout errors, things getting copied to the wrong location
20+
/// 2) Checking if files in the MSI including the MSI are signed
21+
/// 3) Checking if files included in MSI uses right signing alogirthm
22+
///
23+
/// </summary>
24+
public class MsiLayoutTests: MsiTestBase
25+
{
26+
const string MSI_NAME = "AzurePowerShell.msi";
27+
const string MSI_EXTRACT_DIR_NAME = "msiContents";
28+
string _procOutput;
29+
string _procErr;
30+
31+
public MsiLayoutTests(ITestOutputHelper testOutput) : base(testOutput)
32+
{
33+
_procOutput = string.Empty;
34+
_procErr = string.Empty;
35+
}
36+
37+
[Fact]
38+
[Trait("AcceptanceType", "CheckIn")]
39+
public void VerifyMsiExecExists()
40+
{
41+
ProcessStartInfo psi = GetInitializedPSI();
42+
psi.FileName = "Msiexec.exe";
43+
psi.Arguments = "/qn /x:1234";
44+
45+
this.ExecuteShellCmd(psi, out _procOutput, out _procErr);
46+
Assert.NotEmpty(_procOutput);
47+
}
48+
49+
//[Fact]
50+
//[Trait("AcceptanceType", "CheckIn")]
51+
//public void VerifyNoJavaScriptFiles()
52+
//{
53+
// string procErr = string.Empty;
54+
// string msiContentsDirPath = ExtractMsiContents(out procErr);
55+
// IEnumerable<string> msiFiles = Directory.EnumerateFiles(msiContentsDirPath, "*.js", SearchOption.AllDirectories);
56+
// TestLog.WriteLine("Expecting no *.js files in MSI");
57+
// foreach (string unsigFile in msiFiles)
58+
// {
59+
// TestLog.WriteLine(unsigFile);
60+
// }
61+
// Assert.Equal(0, msiFiles.Count<string>());
62+
//}
63+
64+
//[Fact]
65+
//[Trait("AcceptanceType", "CheckIn")]
66+
//public void VerifyNoJsonFiles()
67+
//{
68+
// string procErr = string.Empty;
69+
// string msiContentsDirPath = ExtractMsiContents(out procErr);
70+
// IEnumerable<string> msiFiles = Directory.EnumerateFiles(msiContentsDirPath, "*.json", SearchOption.AllDirectories);
71+
// TestLog.WriteLine("Expecting no *.json files in MSI");
72+
// foreach (string unsigFile in msiFiles)
73+
// {
74+
// TestLog.WriteLine(unsigFile);
75+
// }
76+
// Assert.Equal(0, msiFiles.Count<string>());
77+
//}
78+
79+
[Fact]
80+
[Trait("SignedBuild", "BVT")]
81+
public void VerifyFilesAreSigned()
82+
{
83+
string SHA1 = "sha1RSA";
84+
string SHA2 = "sha256RSA";
85+
86+
List<string> expectedSignatureAlgos = new List<string>() { SHA1, SHA2 };
87+
string msiContentsDir = this.ExtractMsiContents(out _procErr);
88+
Assert.True(Directory.Exists(msiContentsDir));
89+
Assert.True(string.IsNullOrEmpty(_procErr));
90+
91+
IEnumerable<string> msiFiles = Directory.EnumerateFiles(msiContentsDir, "*", SearchOption.AllDirectories);
92+
//Ignore Newtonsoft, .xml, .msi (need to find out why unsigned msi get's packaged inside the MSI)
93+
IEnumerable<string> exceptionFiles = Directory.EnumerateFiles(msiContentsDir, "*.xml", SearchOption.AllDirectories)
94+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "newtonsoft*.dll", SearchOption.AllDirectories))
95+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "automapper*.dll", SearchOption.AllDirectories))
96+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "security*.dll", SearchOption.AllDirectories))
97+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "bouncy*.dll", SearchOption.AllDirectories))
98+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.psd1", SearchOption.AllDirectories))
99+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.json", SearchOption.AllDirectories))
100+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.cscfg", SearchOption.AllDirectories))
101+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.csdef", SearchOption.AllDirectories))
102+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.cmd", SearchOption.AllDirectories))
103+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.config", SearchOption.AllDirectories))
104+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.php", SearchOption.AllDirectories))
105+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.yml", SearchOption.AllDirectories))
106+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.gitignore", SearchOption.AllDirectories))
107+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.js", SearchOption.AllDirectories)) // We are doing this because Get-AuthenticodeSignature cannot verify .js files
108+
.Union<string>(Directory.EnumerateFiles(msiContentsDir, "*.msi", SearchOption.AllDirectories));
109+
110+
Assert.NotNull(msiFiles);
111+
IEnumerable<string> filesToVerify = msiFiles.Except<string>(exceptionFiles);
112+
113+
#region
114+
// Make sure filesToVerify do not have any of the files that are either external to MS
115+
// or are not signed (which are expected not to be signed eg. psd1 files)
116+
List<string> noXmlFiles = filesToVerify.Where<string>((fl) => fl.EndsWith(".xml")).ToList<string>();
117+
//TestLog.WriteLine("Verifying no .xml files are in the verify list of files");
118+
Assert.True(noXmlFiles.Count == 0);
119+
120+
List<string> noNewtonsoftFiles = filesToVerify.Where<string>((fl) => fl.Contains("newtonsoft")).ToList<string>();
121+
//TestLog.WriteLine("Verifying no 'Newtonsoft*.dll' files are in the verify list of files");
122+
Assert.True(noNewtonsoftFiles.Count == 0);
123+
124+
List<string> noMsiFiles = filesToVerify.Where<string>((fl) => fl.EndsWith(".msi")).ToList<string>();
125+
//TestLog.WriteLine("Verifying no '*.msi' files are in the verify list of files");
126+
Assert.True(noMsiFiles.Count == 0);
127+
#endregion
128+
129+
List<string> noPsd1Files = filesToVerify.Where<string>((fl) => fl.EndsWith(".psd1")).ToList<string>();
130+
//TestLog.WriteLine("Verifying no '*.psd1' files are in the verify list of files");
131+
Assert.True(noPsd1Files.Count == 0);
132+
133+
// Now extract each category of files and verify if they matched to the algorithm they are expected to be signed
134+
IEnumerable<string> dllFiles = filesToVerify.Where<string>((fl) => fl.EndsWith(".dll")).ToList<string>();
135+
IEnumerable<string> scriptFiles = filesToVerify.Where<string>((fl) => fl.EndsWith(".ps1"))
136+
.Union<string>(filesToVerify.Where<string>((fl) => fl.EndsWith(".psm1")))
137+
//.Union<string>(filesToVerify.Where<string>((fl) => fl.EndsWith(".js"))) // We are doing this because Get-AuthenticodeSignature cannot verify .js files
138+
.Union<string>(filesToVerify.Where<string>((fl) => fl.EndsWith(".ps1xml")));
139+
140+
IEnumerable<string> dllScriptCombined = dllFiles.Union<string>(scriptFiles);
141+
IEnumerable<string> diff = filesToVerify.Except<string>(dllScriptCombined);
142+
143+
TestLog.WriteLine("Verify number of dlls, script files match");
144+
foreach(string fl in diff)
145+
{
146+
TestLog.WriteLine(fl);
147+
}
148+
Assert.Equal(dllScriptCombined.Count(), filesToVerify.Count());
149+
150+
List<string> unsignedDlls = GetUnsignedFiles(dllFiles, expectedSignatureAlgos);
151+
TestLog.WriteLine("Verifying if DLLs are properly signed");
152+
unsignedDlls.ForEach((unsigDll) => TestLog.WriteLine(unsigDll));
153+
Assert.Equal(unsignedDlls.Count, 0);
154+
155+
List<string> unsignedScripts = GetUnsignedFiles(scriptFiles, SHA2);
156+
TestLog.WriteLine("Verifying if SCRIPTS are properly signed");
157+
unsignedScripts.ForEach((unsigScript) => TestLog.WriteLine(unsigScript));
158+
Assert.Equal(unsignedScripts.Count, 0);
159+
160+
// We do this because, we sign MSI as SHA2 with SHA1 hash.
161+
// Verifying msi under windows --> Rightclick --> Properties will show SHA1
162+
// Verifying msi with Get-AuthenticodeSignature will return as SHA2
163+
List<string> unsignedMsi = GetUnsignedFiles(new List<string>() { this.GetAzurePSMsiPath() }, SHA2);
164+
TestLog.WriteLine("Verifying if MSI is properly signed");
165+
unsignedMsi.ForEach((unsigMsi) => TestLog.WriteLine(unsigMsi));
166+
Assert.Equal(unsignedMsi.Count, 0);
167+
}
168+
169+
#region Private Functions
170+
private List<string> GetUnsignedFiles(IEnumerable<string> signedFiles, string expectedAlgorithm)
171+
{
172+
List<string> unsignedFiles = new List<string>();
173+
string unsignedFileStatusFormat = "Expected signature '{0}', Actual '{1}' signature ::: {2}";
174+
175+
Parallel.ForEach<string>(signedFiles, (providedFilePath) =>
176+
{
177+
string sigAlgo = GetFileSignature(providedFilePath);
178+
179+
if(string.IsNullOrEmpty(sigAlgo))
180+
{
181+
unsignedFiles.Add(string.Format(unsignedFileStatusFormat, expectedAlgorithm, sigAlgo, providedFilePath));
182+
}
183+
else if(!sigAlgo.Equals(expectedAlgorithm, StringComparison.OrdinalIgnoreCase))
184+
{
185+
unsignedFiles.Add(string.Format(unsignedFileStatusFormat, expectedAlgorithm, sigAlgo, providedFilePath));
186+
}
187+
});
188+
189+
return unsignedFiles;
190+
}
191+
192+
private List<string> GetUnsignedFiles(IEnumerable<string> signedFiles, List<string> expectedSignatureAlgorithmList)
193+
{
194+
List<string> unsignedFiles = new List<string>();
195+
string algoList = string.Join("/", expectedSignatureAlgorithmList);
196+
string unsignedFileStatusFormat = "Expected signature '{0}', Actual '{1}' signature ::: {2}";
197+
198+
Parallel.ForEach<string>(signedFiles, (providedFilePath) =>
199+
{
200+
string sigAlgo = GetFileSignature(providedFilePath);
201+
202+
if (string.IsNullOrEmpty(sigAlgo))
203+
{
204+
unsignedFiles.Add(string.Format(unsignedFileStatusFormat, algoList, sigAlgo, providedFilePath));
205+
}
206+
else
207+
{
208+
string match = expectedSignatureAlgorithmList.Find((pn) => pn.Equals(sigAlgo, System.StringComparison.OrdinalIgnoreCase));
209+
if (string.IsNullOrEmpty(match))
210+
{
211+
unsignedFiles.Add(string.Format(unsignedFileStatusFormat, algoList, sigAlgo, providedFilePath));
212+
}
213+
}
214+
});
215+
216+
return unsignedFiles;
217+
}
218+
219+
private List<string> GetUnsignedFilesSync(IEnumerable<string> signedFiles, string expectedAlgorithm)
220+
{
221+
List<string> unsignedFiles = new List<string>();
222+
string unsignedFileStatusFormat = "Expected signature '{0}', Actual '{1}' signature ::: {2}";
223+
224+
foreach(string providedFilePath in signedFiles)
225+
{
226+
string sigAlgo = GetFileSignature(providedFilePath);
227+
228+
if (string.IsNullOrEmpty(sigAlgo))
229+
{
230+
unsignedFiles.Add(string.Format(unsignedFileStatusFormat, expectedAlgorithm, sigAlgo, providedFilePath));
231+
}
232+
else if (!sigAlgo.Equals(expectedAlgorithm, StringComparison.OrdinalIgnoreCase))
233+
{
234+
unsignedFiles.Add(string.Format(unsignedFileStatusFormat, expectedAlgorithm, sigAlgo, providedFilePath));
235+
}
236+
}
237+
238+
return unsignedFiles;
239+
}
240+
241+
/// <summary>
242+
/// Checks if a file is signed and returns the friendly alogrithm name of the file
243+
/// Returns the friendly algorithm name of a file
244+
/// </summary>
245+
/// <param name="providedFilePath">Full file path for which Signature has to be verified</param>
246+
/// <returns>Friendly Algorithm name, String.empty if not signed</returns>
247+
private string GetFileSignature(string providedFilePath)
248+
{
249+
string friendlyAlgorithmName = string.Empty;
250+
if (File.Exists(providedFilePath))
251+
{
252+
using (PowerShell ps = PowerShell.Create())
253+
{
254+
ps.AddCommand("Get-AuthenticodeSignature", true);
255+
ps.AddParameter("FilePath", providedFilePath);
256+
var cmdLetResults = ps.Invoke();
257+
258+
foreach (PSObject result in cmdLetResults)
259+
{
260+
Signature sig = (Signature)result.BaseObject;
261+
if(sig.Status.Equals(SignatureStatus.Valid))
262+
{
263+
friendlyAlgorithmName = sig.SignerCertificate.SignatureAlgorithm.FriendlyName;
264+
}
265+
break;
266+
}
267+
}
268+
}
269+
270+
return friendlyAlgorithmName;
271+
}
272+
#endregion
273+
}
274+
}

0 commit comments

Comments
 (0)