aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorchris_bednarski <Chris.Bednarski@minfos.com.au>2023-08-13 11:34:57 +1000
committerBob Arnson <github@bobs.org>2023-08-19 18:08:09 -0400
commitfebb9c943d6c6c0db3a660de6f96b7d9f5941b1a (patch)
tree3fe6673016b6edc7399c594842dbf7f1460d0335 /src
parent6f520b9def48f302625906ad961e1afa28edb3d0 (diff)
downloadwix-febb9c943d6c6c0db3a660de6f96b7d9f5941b1a.tar.gz
wix-febb9c943d6c6c0db3a660de6f96b7d9f5941b1a.tar.bz2
wix-febb9c943d6c6c0db3a660de6f96b7d9f5941b1a.zip
add integration tests for the firewall extenstion
Diffstat (limited to 'src')
-rw-r--r--src/internal/SetBuildNumber/Directory.Packages.props.pp1
-rw-r--r--src/test/burn/WixTestTools/Firewall/RuleDetails.cs256
-rw-r--r--src/test/burn/WixTestTools/Firewall/UniqueCheck.cs76
-rw-r--r--src/test/burn/WixTestTools/Firewall/Verifier.cs303
-rw-r--r--src/test/burn/WixTestTools/WixTestTools.csproj11
-rw-r--r--src/test/msi/TestData/FirewallExtensionTests/FirewallRules/FirewallRules.wixproj13
-rw-r--r--src/test/msi/TestData/FirewallExtensionTests/FirewallRules/product.wxs23
-rw-r--r--src/test/msi/WixToolsetTest.MsiE2E/FirewallExtensionTests.cs204
-rw-r--r--src/test/msi/WixToolsetTest.MsiE2E/WixToolsetTest.MsiE2E.csproj11
9 files changed, 898 insertions, 0 deletions
diff --git a/src/internal/SetBuildNumber/Directory.Packages.props.pp b/src/internal/SetBuildNumber/Directory.Packages.props.pp
index cea1def8..b39eeb78 100644
--- a/src/internal/SetBuildNumber/Directory.Packages.props.pp
+++ b/src/internal/SetBuildNumber/Directory.Packages.props.pp
@@ -37,6 +37,7 @@
37 <PackageVersion Include="WixToolset.NetFx.wixext" Version="{packageversion}" /> 37 <PackageVersion Include="WixToolset.NetFx.wixext" Version="{packageversion}" />
38 <PackageVersion Include="WixToolset.UI.wixext" Version="{packageversion}" /> 38 <PackageVersion Include="WixToolset.UI.wixext" Version="{packageversion}" />
39 <PackageVersion Include="WixToolset.Util.wixext" Version="{packageversion}" /> 39 <PackageVersion Include="WixToolset.Util.wixext" Version="{packageversion}" />
40 <PackageVersion Include="WixToolset.Firewall.wixext" Version="{packageversion}" />
40 </ItemGroup> 41 </ItemGroup>
41 42
42 <ItemGroup> 43 <ItemGroup>
diff --git a/src/test/burn/WixTestTools/Firewall/RuleDetails.cs b/src/test/burn/WixTestTools/Firewall/RuleDetails.cs
new file mode 100644
index 00000000..38a80bb8
--- /dev/null
+++ b/src/test/burn/WixTestTools/Firewall/RuleDetails.cs
@@ -0,0 +1,256 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixTestTools.Firewall
4{
5 using NetFwTypeLib;
6
7 /// <summary>
8 /// The RuleDetails class provides access to the properties of a firewall rule.<br/>
9 /// Details are retrieved via the <b>NetFwTypeLib.INetFwRule3</b> interface and originally stored at<br/>
10 /// <b>HKLM\SYSTEM\ControlSet001\Services\SharedAccess\Parameters\FirewallPolicy\FirewallRules</b>.
11 /// </summary>
12 public class RuleDetails
13 {
14 public RuleDetails(string name)
15 {
16 this.Name = name;
17 }
18
19 public RuleDetails(INetFwRule3 rule)
20 {
21 this.Name = rule.Name;
22 this.Description = rule.Description;
23 this.ApplicationName = rule.ApplicationName;
24 this.ServiceName = rule.serviceName;
25 this.Protocol = rule.Protocol;
26 this.LocalPorts = rule.LocalPorts;
27 this.RemotePorts = rule.RemotePorts;
28 this.LocalAddresses = rule.LocalAddresses;
29 this.RemoteAddresses = rule.RemoteAddresses;
30 this.IcmpTypesAndCodes = rule.IcmpTypesAndCodes;
31 this.Direction = rule.Direction;
32 this.Interfaces = rule.Interfaces as object[];
33 this.InterfaceTypes = rule.InterfaceTypes;
34 this.Enabled = rule.Enabled;
35 this.Grouping = rule.Grouping;
36 this.Profiles = rule.Profiles;
37 this.EdgeTraversal = rule.EdgeTraversal;
38 this.Action = rule.Action;
39 this.EdgeTraversalOptions = rule.EdgeTraversalOptions;
40 this.LocalAppPackageId = rule.LocalAppPackageId;
41 this.LocalUserOwner = rule.LocalUserOwner;
42 this.LocalUserAuthorizedList = rule.LocalUserAuthorizedList;
43 this.RemoteUserAuthorizedList = rule.RemoteUserAuthorizedList;
44 this.RemoteMachineAuthorizedList = rule.RemoteMachineAuthorizedList;
45 this.SecureFlags = rule.SecureFlags;
46 }
47
48 /// <summary>
49 /// Specifies a friendly name of this rule. Rule name should be unique, must not contain a "|" (pipe) character and cannot be "all".<br/>
50 /// Use <b>netsh advfirewall firewall add rule help</b> for more information.
51 /// </summary>
52 public string Name { get; set; }
53
54 /// <summary>
55 /// This property is optional. It specifies the description of this rule.<br/>
56 /// The string must not contain the "|" character, as the pipe character is used to separate firewall rule properties when stored in the registry.
57 /// </summary>
58 public string Description { get; set; }
59
60 /// <summary>
61 /// This property is optional. It specifies the path and name (an image path) of the application to which this rule applies.<br/>
62 /// Environment variables can be used in the path.<br/>
63 /// Example: "%ProgramFiles%\Windows Defender\MsMpEng.exe"<br/>
64 /// Example: "%SystemRoot%\system32\svchost.exe"
65 /// </summary>
66 public string ApplicationName { get; set; }
67
68 /// <summary>
69 /// This property is optional. A service name value of "*" indicates that a service, not an application, must be sending or receiving traffic.<br/>
70 /// Example: "Spooler"<br/>
71 /// Example: "FTPSVC"
72 /// </summary>
73 public string ServiceName { get; set; }
74
75 /// <summary>
76 /// This property is optional. The Protocol property must be set before the LocalPorts or RemotePorts properties, or an error will be returned.<br/>
77 /// A list of protocol numbers is available at the <a href="https://www.iana.org/assignments/protocol-numbers/">IANA website</a>.
78 /// The default value is 256, which means any protocol.<br/>
79 /// Example: 6 , which specifies the TCP protocol<br/>
80 /// Example: 17 , which specifies the UDP protocol
81 /// </summary>
82 public int? Protocol { get; set; }
83
84 /// <summary>
85 /// This property is optional. The Protocol property must be set before the LocalPorts property or an error will be returned.<br/>
86 /// A clear text string containing a single port, a comma separated list of port numbers, or a port range can be provided. "RPC" is an acceptable value.<br/>
87 /// Example: "23456"<br/>
88 /// Example: "10234-10300"<br/>
89 /// Example: "5026,7239"
90 /// </summary>
91 public string LocalPorts { get; set; }
92
93 /// <summary>
94 /// This property is optional. The Protocol property must be set before the RemotePorts property or an error will be returned.<br/>
95 /// A clear text string containing a single port, a comma separated list of port numbers, or a port range can be provided.<br/>
96 /// Example: "23456"<br/>
97 /// Example: "10234-10300"<br/>
98 /// Example: "5026,7239"
99 /// </summary>
100 public string RemotePorts { get; set; }
101
102 /// <summary>
103 /// This property is optional. It consists of one or more comma-delimited tokens specifying the local addresses from which the application can listen for traffic.<br/>
104 /// "<b>*</b>" indicates any local address and is the default value. If present, this must be the only token included.<br/>
105 /// Other tokens:<br/>
106 /// <b>o</b> "Defaultgateway"<br/>
107 /// <b>o</b> "DHCP"<br/>
108 /// <b>o</b> "WINS"<br/>
109 /// <b>o</b> "LocalSubnet" indicates any local address on the local subnet. This token is not case-sensitive.<br/>
110 /// <b>o</b> A subnet can be specified using either the subnet mask or network prefix notation. If neither a subnet mask not a network prefix is specified, the subnet mask defaults to 255.255.255.255.<br/>
111 /// <b>o</b> A valid IPv6 address.<br/>
112 /// <b>o</b> An IPv4 address range in the format of "start address - end address" with no spaces included.<br/>
113 /// <b>o</b> An IPv6 address range in the format of "start address - end address" with no spaces included.<br/>
114 /// Example: "LocalSubnet"
115 /// </summary>
116 public string LocalAddresses { get; set; }
117
118 /// <summary>
119 /// This property is optional. It consists of one or more comma-delimited tokens specifying the local addresses from which the application can listen for traffic.<br/>
120 /// "<b>*</b>" indicates any remote address and is the default value. If present, this must be the only token included.<br/>
121 /// Other tokens:<br/>
122 /// <b>o</b> "Defaultgateway"<br/>
123 /// <b>o</b> "DHCP"<br/>
124 /// <b>o</b> "WINS"<br/>
125 /// <b>o</b> "LocalSubnet" indicates any local address on the local subnet. This token is not case-sensitive.<br/>
126 /// <b>o</b> A subnet can be specified using either the subnet mask or network prefix notation. If neither a subnet mask not a network prefix is specified, the subnet mask defaults to 255.255.255.255.<br/>
127 /// <b>o</b> A valid IPv6 address.<br/>
128 /// <b>o</b> An IPv4 address range in the format of "start address - end address" with no spaces included.<br/>
129 /// <b>o</b> An IPv6 address range in the format of "start address - end address" with no spaces included.<br/>
130 /// Example: "LocalSubnet"
131 /// </summary>
132 public string RemoteAddresses { get; set; }
133
134 /// <summary>
135 /// This property is optional. A list of ICMP types and codes separated by semicolon. "*" indicates all ICMP types and codes.<br/>
136 /// Example: "4:*,9:*,12:*"
137 /// </summary>
138 public string IcmpTypesAndCodes { get; set; }
139
140 /// <summary>
141 /// This property is optional. If this property is not specified, the default value is NET_FW_RULE_DIR_IN.
142 /// </summary>
143 public NET_FW_RULE_DIRECTION_? Direction { get; set; }
144
145 /// <summary>
146 /// This parameter allows the specification of an array of interface LUIDs (locally unique identifiers) supplied as strings.<br/>
147 /// This is commonly used by USB RNDIS (Remote Network Driver Interface Specification) devices to restrict traffic to a specific non-routable interface.<br/>
148 /// Use <b>netsh trace show interfaces</b> to show a list of local interfaces and their LUIDs.<br/>
149 /// Example: new object[] { "Wi-Fi", "Local Area Connection* 14" }
150 /// </summary>
151 public object[] Interfaces { get; set; }
152
153 /// <summary>
154 /// This property is optional. It specifies the list of interface types for which the rule applies.<br/>
155 /// Acceptable values for this property are "RemoteAccess", "Wireless", "Lan", and "All".<br/>
156 /// If more than one interface type is specified, the strings must be separated by a comma.<br/>
157 /// Example: "Lan,Wireless"
158 /// </summary>
159 public string InterfaceTypes { get; set; }
160
161 /// <summary>
162 /// This property is optional. It enables or disables a rule. A new rule is disabled by default.
163 /// </summary>
164 public bool? Enabled { get; set; }
165
166 /// <summary>
167 /// This property is optional. It specifies the group to which an individual rule belongs and groups multiple rules into a single line in the Windows Firewall control panel<br/>
168 /// This allows the users to enable or disable multiple rules with a single click.<br/>
169 /// The Grouping property can also be specified using indirect strings.<br/>
170 /// Example: "Simple Group Name"<br/>
171 /// Example: "@yourresources.dll,-1005"
172 /// </summary>
173 public string Grouping { get; set; }
174
175 /// <summary>
176 /// This property is optional. The NET_FW_PROFILE_TYPE2 enumerated type specifies the type of profile.<br/>
177 /// NET_FW_PROFILE2_ALL is the default value. Profiles can be combined from the following values:<br/>
178 /// <b>o</b> NET_FW_PROFILE2_DOMAIN = 0x1<br/>
179 /// <b>o</b> NET_FW_PROFILE2_PRIVATE = 0x2<br/>
180 /// <b>o</b> NET_FW_PROFILE2_PUBLIC = 0x4<br/>
181 /// <b>o</b> NET_FW_PROFILE2_ALL = 0x7fffffff<br/>
182 /// Example: 0x5
183 /// </summary>
184 public int? Profiles { get; set; }
185
186 /// <summary>
187 /// New rules have the EdgeTraversal property disabled by default.<br/>
188 /// The EdgeTraversal property indicates that specific inbound traffic is allowed to tunnel through NATs and other edge devices using the Teredo tunneling technology.<br/>
189 /// The application or service with the inbound firewall rule needs to support IPv6. The primary application of this setting allows listeners on the host to be globally addressable through a Teredo IPv6 address.<br/>
190 /// See EdgeTraversalOptions property for additional information.
191 /// </summary>
192 public bool? EdgeTraversal { get; set; }
193
194 /// <summary>
195 /// This property is optional. The NET_FW_ACTION enumerated type specifies the action for this rule.<br/>
196 /// NET_FW_ACTION_ALLOW is the default value. Profiles can be combined from the following values:<br/>
197 /// <b>o</b> NET_FW_ACTION_BLOCK = 0x0<br/>
198 /// <b>o</b> NET_FW_ACTION_ALLOW = 0x1<br/>
199 /// </summary>
200 public NET_FW_ACTION_? Action { get; set; }
201
202 /// <summary>
203 /// This property is optional and can be used to access the edge traversal properties of a firewall rule defined by NET_FW_EDGE_TRAVERSAL_TYPE enumerated type.<br/>
204 /// NET_FW_EDGE_TRAVERSAL_TYPE_DENY is the default value. Enumerated types cannot be combined and must be selected from the following list of values:<br/>
205 /// <b>o</b> NET_FW_EDGE_TRAVERSAL_TYPE_DENY = 0x0 - Edge traversal traffic is always blocked.<br/>
206 /// <b>o</b> NET_FW_EDGE_TRAVERSAL_TYPE_ALLOW = 0x1 - Edge traversal traffic is always allowed.<br/>
207 /// These two options above are equivalent to setting the EdgeTraversal property to true or false.<br/>
208 /// <b>o</b> NET_FW_EDGE_TRAVERSAL_TYPE_DEFER_TO_APP = 0x2 - Edge traversal traffic is allowed when the application sets the IPV6_PROTECTION_LEVEL socket option to PROTECTION_LEVEL_UNRESTRICTED. Otherwise, it is blocked.<br/>
209 /// <b>o</b> NET_FW_EDGE_TRAVERSAL_TYPE_DEFER_TO_USER = 0x3 - The user is prompted whether to allow edge traversal traffic when the application sets the IPV6_PROTECTION_LEVEL socket option to PROTECTION_LEVEL_UNRESTRICTED.<br/>
210 /// If the user chooses to allow edge traversal traffic, the rule is modified to defer to the application's settings. If the application has not set the IPV6_PROTECTION_LEVEL socket option to PROTECTION_LEVEL_UNRESTRICTED,<br/>
211 /// edge traversal traffic is blocked. In order to use this option, the firewall rule must have both the application path and protocol scopes specified. This option cannot be used if port(s) are defined.
212 /// </summary>
213 public int? EdgeTraversalOptions { get; set; }
214
215 /// <summary>
216 /// This property is optional. It specifies the package identifier or the app container identifier of a process, whether from a Windows Store app or a desktop app.<br/>
217 /// For more details and examples look at <b>HKLM\SYSTEM\ControlSet001\Services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\AppIso\FirewallRules</b>.<br/>
218 /// Example: "S-1-15-2-1239072475-3687740317-1842961305-3395936705-4023953123-1525404051-2779347315" Microsoft.WindowsMaps
219 /// </summary>
220 public string LocalAppPackageId { get; set; }
221
222 /// <summary>
223 /// This property is optional. It specifies the user security identifier (SID) of the user who is the owner of the rule.<br/>
224 /// If this rule does not specify LocalUserAuthorizedList, all the traffic that this rule matches must be destined to or originated from this user.<br/>
225 /// Example: "S-1-5-21-1898747406-2352535518-1247798438-1914"
226 /// </summary>
227 public string LocalUserOwner { get; set; }
228
229 /// <summary>
230 /// This property is optional. It specifies a list of authorized local users for an app container.<br/>
231 /// Example: "O:LSD:(A;;CC;;;S-1-5-84-0-0-0-0-0)"
232 /// </summary>
233 public string LocalUserAuthorizedList { get; set; }
234
235 /// <summary>
236 /// This property is optional. It specifies a list of remote users who are authorized to access an app container.<br/>
237 /// </summary>
238 public string RemoteUserAuthorizedList { get; set; }
239
240 /// <summary>
241 /// This property is optional. It specifies a list of remote computers which are authorized to access an app container.<br/>
242 /// </summary>
243 public string RemoteMachineAuthorizedList { get; set; }
244
245 /// <summary>
246 /// This property is optional. It specifies which firewall verifications of security levels provided by IPsec must be guaranteed to allow the connection.<br/>
247 /// The allowed values must correspond to one of those of the <b>NET_FW_AUTHENTICATE_TYPE</b> enumeration:<br/>
248 /// <b>o</b> NET_FW_AUTHENTICATE_NONE - 0x0 - No security check is performed.<br/>
249 /// <b>o</b> NET_FW_AUTHENTICATE_NO_ENCAPSULATION - 0x1 - The traffic is allowed if it is IPsec-protected with authentication and no encapsulation protection. This means that the peer is authenticated, but there is no integrity protection on the data.<br/>
250 /// <b>o</b> NET_FW_AUTHENTICATE_WITH_INTEGRITY - 0x2 - The traffic is allowed if it is IPsec-protected with authentication and integrity protection.<br/>
251 /// <b>o</b> NET_FW_AUTHENTICATE_AND_NEGOTIATE_ENCRYPTION - 0x3 - The traffic is allowed if its is IPsec-protected with authentication and integrity protection. In addition, negotiation of encryption protections on subsequent packets is requested.<br/>
252 /// <b>o</b> NET_FW_AUTHENTICATE_AND_ENCRYPT - 0x4 - The traffic is allowed if it is IPsec-protected with authentication, integrity and encryption protection since the very first packet.
253 /// </summary>
254 public int? SecureFlags { get; set; }
255 }
256}
diff --git a/src/test/burn/WixTestTools/Firewall/UniqueCheck.cs b/src/test/burn/WixTestTools/Firewall/UniqueCheck.cs
new file mode 100644
index 00000000..83a1e57a
--- /dev/null
+++ b/src/test/burn/WixTestTools/Firewall/UniqueCheck.cs
@@ -0,0 +1,76 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixTestTools.Firewall
4{
5 using NetFwTypeLib;
6
7 /// <summary>
8 /// A lot of firewall rules don't follow the Microsoft recommendation of using unique names.<br/>
9 /// This class helps to disambiguate the rules based on Name, Direction, Profile, Protocol, ApplicationName and the LocalUserOwner.
10 /// </summary>
11 public class UniqueCheck
12 {
13 public UniqueCheck()
14 {
15 }
16
17 public UniqueCheck(RuleDetails details)
18 {
19 this.Name = details.Name;
20 this.Direction = details.Direction;
21 this.Profile = details.Profiles;
22 this.Protocol = details.Protocol;
23 this.ApplicationName = details.ApplicationName;
24 this.LocalUserOwner = details.LocalUserOwner;
25 }
26
27
28 public string Name { get; set; }
29
30 public NET_FW_RULE_DIRECTION_? Direction { get; set; }
31
32 public int? Profile { get; set; }
33
34 public int? Protocol { get; set; }
35
36 public string ApplicationName { get; set; }
37
38 public string LocalUserOwner { get; set; }
39
40
41 public bool FirewallRuleIsUnique(INetFwRule3 rule)
42 {
43 if (this.Name != null && rule.Name != this.Name)
44 {
45 return false;
46 }
47
48 if (this.Direction.HasValue && rule.Direction != this.Direction.Value)
49 {
50 return false;
51 }
52
53 if (this.Profile.HasValue && rule.Profiles != this.Profile.Value)
54 {
55 return false;
56 }
57
58 if (this.Protocol.HasValue && rule.Protocol != this.Protocol.Value)
59 {
60 return false;
61 }
62
63 if (this.ApplicationName != null && rule.ApplicationName != this.ApplicationName)
64 {
65 return false;
66 }
67
68 if (this.LocalUserOwner != null && rule.LocalUserOwner != this.LocalUserOwner)
69 {
70 return false;
71 }
72
73 return true;
74 }
75 }
76}
diff --git a/src/test/burn/WixTestTools/Firewall/Verifier.cs b/src/test/burn/WixTestTools/Firewall/Verifier.cs
new file mode 100644
index 00000000..d3f32c5c
--- /dev/null
+++ b/src/test/burn/WixTestTools/Firewall/Verifier.cs
@@ -0,0 +1,303 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixTestTools.Firewall
4{
5 using System;
6 using System.Collections.Generic;
7 using NetFwTypeLib;
8 using Xunit;
9
10 public static class Verifier
11 {
12 static INetFwRules GetINetFwRules()
13 {
14 var policyType = Type.GetTypeFromProgID("HNetCfg.FwPolicy2", true);
15 var policyInstance = Activator.CreateInstance(policyType);
16 var policy2 = policyInstance as INetFwPolicy2;
17 return policy2.Rules;
18 }
19
20 static INetFwRule3 GetINetFwRule3(string name, UniqueCheck unique)
21 {
22 var rules = GetINetFwRules();
23 INetFwRule3 rule3;
24
25 if (unique != null)
26 {
27 var enumerator = rules.GetEnumerator();
28 while (enumerator.MoveNext())
29 {
30 rule3 = enumerator.Current as INetFwRule3;
31 if (!unique.FirewallRuleIsUnique(rule3))
32 {
33 continue;
34 }
35
36 return rule3;
37 }
38 }
39
40 var rule1 = rules.Item(name);
41 rule3 = rule1 as INetFwRule3;
42 return rule3;
43 }
44
45 public static RuleDetails GetFirewallRule(string name, UniqueCheck unique)
46 {
47 var rule = GetINetFwRule3(name, unique);
48 var details = new RuleDetails(rule);
49 return details;
50 }
51
52 public static bool FirewallRuleExists(string name, UniqueCheck unique = null)
53 {
54 try
55 {
56 GetINetFwRule3(name, unique);
57 return true;
58 }
59 catch (System.IO.FileNotFoundException)
60 {
61 return false;
62 }
63 }
64
65 public static IEnumerable<RuleDetails> GetFirewallRules()
66 {
67 var rules = GetINetFwRules();
68 var enumerator = rules.GetEnumerator();
69 while (enumerator.MoveNext())
70 {
71 var rule3 = enumerator.Current as INetFwRule3;
72 yield return new RuleDetails(rule3);
73 }
74 }
75
76 public static void AddFirewallRule(RuleDetails information)
77 {
78 var rules = GetINetFwRules();
79 var rule1 = Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FWRule"));
80 var rule3 = rule1 as INetFwRule3;
81
82 rule3.Name = information.Name;
83
84 if (!String.IsNullOrEmpty(information.Description))
85 {
86 rule3.Description = information.Description;
87 }
88
89 if (!String.IsNullOrEmpty(information.ApplicationName))
90 {
91 rule3.ApplicationName = information.ApplicationName;
92 }
93
94 if (!String.IsNullOrEmpty(information.ServiceName))
95 {
96 rule3.serviceName = information.ServiceName;
97 }
98
99 if (information.Protocol.HasValue)
100 {
101 rule3.Protocol = information.Protocol.Value;
102 }
103
104 if (!String.IsNullOrEmpty(information.LocalPorts))
105 {
106 rule3.LocalPorts = information.LocalPorts;
107 }
108
109 if (!String.IsNullOrEmpty(information.RemotePorts))
110 {
111 rule3.RemotePorts = information.RemotePorts;
112 }
113
114 if (!String.IsNullOrEmpty(information.LocalAddresses))
115 {
116 rule3.LocalAddresses = information.LocalAddresses;
117 }
118
119 if (!String.IsNullOrEmpty(information.RemoteAddresses))
120 {
121 rule3.RemoteAddresses = information.RemoteAddresses;
122 }
123
124 if (!String.IsNullOrEmpty(information.IcmpTypesAndCodes))
125 {
126 rule3.IcmpTypesAndCodes = information.IcmpTypesAndCodes;
127 }
128
129 if (information.Direction.HasValue)
130 {
131 rule3.Direction = information.Direction.Value;
132 }
133
134 if (information.Interfaces != null)
135 {
136 rule3.Interfaces = information.Interfaces;
137 }
138
139 if (!String.IsNullOrEmpty(information.InterfaceTypes))
140 {
141 rule3.InterfaceTypes = information.InterfaceTypes;
142 }
143
144 if (information.Enabled.HasValue)
145 {
146 rule3.Enabled = information.Enabled.Value;
147 }
148
149 if (!String.IsNullOrEmpty(information.Grouping))
150 {
151 rule3.Grouping = information.Grouping;
152 }
153
154 if (information.Profiles.HasValue)
155 {
156 rule3.Profiles = information.Profiles.Value;
157 }
158
159 if (information.EdgeTraversal.HasValue)
160 {
161 rule3.EdgeTraversal = information.EdgeTraversal.Value;
162 }
163
164 if (information.Action.HasValue)
165 {
166 rule3.Action = information.Action.Value;
167 }
168
169 if (information.EdgeTraversalOptions.HasValue)
170 {
171 rule3.EdgeTraversalOptions = information.EdgeTraversalOptions.Value;
172 }
173
174 if (!String.IsNullOrEmpty(information.LocalAppPackageId))
175 {
176 rule3.LocalAppPackageId = information.LocalAppPackageId;
177 }
178
179 if (!String.IsNullOrEmpty(information.LocalUserOwner))
180 {
181 rule3.LocalUserOwner = information.LocalUserOwner;
182 }
183
184 if (!String.IsNullOrEmpty(information.LocalUserAuthorizedList))
185 {
186 rule3.LocalUserAuthorizedList = information.LocalUserAuthorizedList;
187 }
188
189 if (!String.IsNullOrEmpty(information.RemoteUserAuthorizedList))
190 {
191 rule3.RemoteUserAuthorizedList = information.RemoteUserAuthorizedList;
192 }
193
194 if (!String.IsNullOrEmpty(information.RemoteMachineAuthorizedList))
195 {
196 rule3.RemoteMachineAuthorizedList = information.RemoteMachineAuthorizedList;
197 }
198
199 if (information.SecureFlags.HasValue)
200 {
201 rule3.SecureFlags = information.SecureFlags.Value;
202 }
203
204 rules.Add(rule3);
205 }
206
207 public static void UpdateFirewallRule(string name, RuleDetails information, UniqueCheck unique = null)
208 {
209 var rule = GetINetFwRule3(name, unique);
210
211 // remove ports so the Protocol can be changed, if required
212 if (information.Protocol.HasValue && rule.Protocol != information.Protocol.Value)
213 {
214 rule.LocalPorts = null;
215 rule.RemotePorts = null;
216 }
217
218 rule.Name = information.Name;
219 rule.Description = information.Description;
220 rule.Direction = information.Direction ?? NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN;
221 rule.ApplicationName = information.ApplicationName;
222 rule.serviceName = information.ServiceName;
223 rule.Protocol = information.Protocol ?? 256;
224 rule.LocalPorts = information.LocalPorts;
225 rule.RemotePorts = information.RemotePorts;
226 rule.LocalAddresses = information.LocalAddresses;
227 rule.RemoteAddresses = information.RemoteAddresses;
228 rule.IcmpTypesAndCodes = information.IcmpTypesAndCodes;
229 rule.Interfaces = information.Interfaces;
230 rule.InterfaceTypes = information.InterfaceTypes;
231 rule.Enabled = information.Enabled ?? false;
232 rule.Grouping = information.Grouping;
233 rule.Profiles = information.Profiles ?? 0x7fffffff;
234 rule.EdgeTraversal = information.EdgeTraversal ?? false;
235 rule.Action = information.Action ?? NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
236 rule.EdgeTraversalOptions = information.EdgeTraversalOptions ?? 0x0;
237 rule.LocalAppPackageId = information.LocalAppPackageId;
238 rule.LocalUserOwner = information.LocalUserOwner;
239 rule.LocalUserAuthorizedList = information.LocalUserAuthorizedList;
240 rule.RemoteUserAuthorizedList = information.RemoteUserAuthorizedList;
241 rule.RemoteMachineAuthorizedList = information.RemoteMachineAuthorizedList;
242 rule.SecureFlags = information.SecureFlags ?? 0;
243 }
244
245 public static void EnableFirewallRule(string name, UniqueCheck unique = null)
246 {
247 var rule = GetINetFwRule3(name, unique);
248 rule.Enabled = true;
249 }
250
251 public static void DisableFirewallRule(string name, UniqueCheck unique = null)
252 {
253 var rule = GetINetFwRule3(name, unique);
254 rule.Enabled = false;
255 }
256
257 public static void RemoveFirewallRulesByName(string name)
258 {
259 var rules = GetINetFwRules();
260 rules.Remove(name);
261 }
262
263 static string FormatErrorMessage(string name, string property, object expected, object actual, UniqueCheck unique)
264 {
265 return $"Assert Failure: {property} differ on rule: {name}" +
266 "\nExpected: " + expected +
267 "\nActual: " + actual +
268 "\n\nDirection: " + unique?.Direction +
269 "\nProfile: " + unique?.Profile +
270 "\nProtocol: " + unique?.Protocol +
271 "\nApplicationName: " + unique?.ApplicationName +
272 "\nLocalUserOwner: " + unique?.LocalUserOwner;
273 }
274
275 public static void VerifyFirewallRule(string name, RuleDetails expected, UniqueCheck unique = null)
276 {
277 var actual = GetFirewallRule(name, unique);
278 Assert.True(expected.Name == actual.Name, String.Format("Assert Failure: Names differ on rule: \nExpected: {0}\nActual: {1}", expected.Name, actual.Name));
279 Assert.True(expected.Description == actual.Description, FormatErrorMessage(name, "Descriptions", expected.Description, actual.Description, unique));
280 Assert.True(expected.ApplicationName == actual.ApplicationName, FormatErrorMessage(name, "ApplicationNames", expected.ApplicationName, actual.ApplicationName, unique));
281 Assert.True(expected.ServiceName == actual.ServiceName, FormatErrorMessage(name, "ServiceNames", expected.ServiceName, actual.ServiceName, unique));
282 Assert.True(expected.Protocol == actual.Protocol, FormatErrorMessage(name, "Protocols", expected.Protocol, actual.Protocol, unique));
283 Assert.True(expected.LocalPorts == actual.LocalPorts, FormatErrorMessage(name, "LocalPorts", expected.LocalPorts, actual.LocalPorts, unique));
284 Assert.True(expected.RemotePorts == actual.RemotePorts, FormatErrorMessage(name, "RemotePorts", expected.RemotePorts, actual.RemotePorts, unique));
285 Assert.True(expected.IcmpTypesAndCodes == actual.IcmpTypesAndCodes, FormatErrorMessage(name, "IcmpTypesAndCodes", expected.IcmpTypesAndCodes, actual.Description, unique));
286 Assert.True(expected.Direction == actual.Direction, FormatErrorMessage(name, "Directions", expected.Direction, actual.Direction, unique));
287 Assert.Equal<object>(expected.Interfaces, actual.Interfaces);
288 Assert.True(expected.InterfaceTypes == actual.InterfaceTypes, FormatErrorMessage(name, "InterfaceTypes", expected.InterfaceTypes, actual.InterfaceTypes, unique));
289 Assert.True(expected.Enabled == actual.Enabled, FormatErrorMessage(name, "Enabled flags", expected.Enabled, actual.Enabled, unique));
290 Assert.True(expected.Grouping == actual.Grouping, FormatErrorMessage(name, "Groupings", expected.Grouping, actual.Grouping, unique));
291 Assert.True(expected.Profiles == actual.Profiles, FormatErrorMessage(name, "Profiles", expected.Profiles, actual.Profiles, unique));
292 Assert.True(expected.EdgeTraversal == actual.EdgeTraversal, FormatErrorMessage(name, "EdgeTraversals", expected.EdgeTraversal, actual.EdgeTraversal, unique));
293 Assert.True(expected.Action == actual.Action, FormatErrorMessage(name, "Actions", expected.Action, actual.Action, unique));
294 Assert.True(expected.EdgeTraversalOptions == actual.EdgeTraversalOptions, FormatErrorMessage(name, "EdgeTraversalOptions", expected.EdgeTraversalOptions, actual.EdgeTraversalOptions, unique));
295 Assert.True(expected.LocalAppPackageId == actual.LocalAppPackageId, FormatErrorMessage(name, "LocalAppPackageIds", expected.LocalAppPackageId, actual.LocalAppPackageId, unique));
296 Assert.True(expected.LocalUserOwner == actual.LocalUserOwner, FormatErrorMessage(name, "LocalUserOwners", expected.LocalUserOwner, actual.LocalUserOwner, unique));
297 Assert.True(expected.LocalUserAuthorizedList == actual.LocalUserAuthorizedList, FormatErrorMessage(name, "LocalUserAuthorizedLists", expected.LocalUserAuthorizedList, actual.LocalUserAuthorizedList, unique));
298 Assert.True(expected.RemoteUserAuthorizedList == actual.RemoteUserAuthorizedList, FormatErrorMessage(name, "RemoteUserAuthorizedLists", expected.RemoteUserAuthorizedList, actual.RemoteUserAuthorizedList, unique));
299 Assert.True(expected.RemoteMachineAuthorizedList == actual.RemoteMachineAuthorizedList, FormatErrorMessage(name, "RemoteMachineAuthorizedLists", expected.RemoteMachineAuthorizedList, actual.RemoteMachineAuthorizedList, unique));
300 Assert.True(expected.SecureFlags == actual.SecureFlags, FormatErrorMessage(name, "SecureFlags", expected.SecureFlags, actual.SecureFlags, unique));
301 }
302 }
303}
diff --git a/src/test/burn/WixTestTools/WixTestTools.csproj b/src/test/burn/WixTestTools/WixTestTools.csproj
index 65db5a60..8d8f11cc 100644
--- a/src/test/burn/WixTestTools/WixTestTools.csproj
+++ b/src/test/burn/WixTestTools/WixTestTools.csproj
@@ -7,6 +7,17 @@
7 <PlatformTarget>x64</PlatformTarget> 7 <PlatformTarget>x64</PlatformTarget>
8 <IsWixTestSupportProject>true</IsWixTestSupportProject> 8 <IsWixTestSupportProject>true</IsWixTestSupportProject>
9 </PropertyGroup> 9 </PropertyGroup>
10 <ItemGroup>
11 <COMReference Include="NetFwTypeLib">
12 <VersionMinor>0</VersionMinor>
13 <VersionMajor>1</VersionMajor>
14 <Guid>58fbcf7c-e7a9-467c-80b3-fc65e8fcca08</Guid>
15 <Lcid>0</Lcid>
16 <WrapperTool>tlbimp</WrapperTool>
17 <Isolated>false</Isolated>
18 <EmbedInteropTypes>False</EmbedInteropTypes>
19 </COMReference>
20 </ItemGroup>
10 21
11 <ItemGroup> 22 <ItemGroup>
12 <PackageReference Include="Microsoft.Win32.Registry" /> 23 <PackageReference Include="Microsoft.Win32.Registry" />
diff --git a/src/test/msi/TestData/FirewallExtensionTests/FirewallRules/FirewallRules.wixproj b/src/test/msi/TestData/FirewallExtensionTests/FirewallRules/FirewallRules.wixproj
new file mode 100644
index 00000000..b1770b0f
--- /dev/null
+++ b/src/test/msi/TestData/FirewallExtensionTests/FirewallRules/FirewallRules.wixproj
@@ -0,0 +1,13 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <UpgradeCode>{4D188568-1CCF-4EEE-BC27-17C3DCC83E58}</UpgradeCode>
5 <ProductComponentsRef>true</ProductComponentsRef>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Product.wxs" Link="Product.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <PackageReference Include="WixToolset.Firewall.wixext" />
12 </ItemGroup>
13</Project> \ No newline at end of file
diff --git a/src/test/msi/TestData/FirewallExtensionTests/FirewallRules/product.wxs b/src/test/msi/TestData/FirewallExtensionTests/FirewallRules/product.wxs
new file mode 100644
index 00000000..e8ce54bf
--- /dev/null
+++ b/src/test/msi/TestData/FirewallExtensionTests/FirewallRules/product.wxs
@@ -0,0 +1,23 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3
4<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:fw="http://wixtoolset.org/schemas/v4/wxs/firewall">
5 <Fragment>
6 <ComponentGroup Id="ProductComponents">
7 <ComponentRef Id="FirewallComponent1"/>
8 </ComponentGroup>
9 </Fragment>
10
11 <Fragment>
12 <Component Id="FirewallComponent1" Guid="A2E492E7-7350-4F77-8424-1FE94553D776" Directory="INSTALLFOLDER">
13 <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" >
14 <fw:FirewallException Id="FirewallException1"
15 Description="WiX Toolset firewall exception rule integration test - minimal app properties"
16 Name="WiXToolset401 Test - 0001" Scope="any" />
17 </File>
18 <fw:FirewallException Id="FirewallException2"
19 Description="WiX Toolset firewall exception rule integration test - minimal port properties"
20 Name="WiXToolset401 Test - 0002" Scope="any" Port="23456"/>
21 </Component>
22 </Fragment>
23</Wix>
diff --git a/src/test/msi/WixToolsetTest.MsiE2E/FirewallExtensionTests.cs b/src/test/msi/WixToolsetTest.MsiE2E/FirewallExtensionTests.cs
new file mode 100644
index 00000000..fce95845
--- /dev/null
+++ b/src/test/msi/WixToolsetTest.MsiE2E/FirewallExtensionTests.cs
@@ -0,0 +1,204 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolsetTest.MsiE2E
4{
5 using System;
6 using System.IO;
7 using NetFwTypeLib;
8 using WixTestTools;
9 using WixTestTools.Firewall;
10 using Xunit;
11 using Xunit.Abstractions;
12
13 public class FirewallExtensionTests : MsiE2ETests
14 {
15 public FirewallExtensionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
16
17 [RuntimeFact]
18 public void VerifierSelfTest()
19 {
20 foreach (var expected in Verifier.GetFirewallRules())
21 {
22 var check = new UniqueCheck(expected);
23 Verifier.VerifyFirewallRule(expected.Name, expected, check);
24 }
25 }
26
27 [RuntimeFact]
28 public void CanInstallAndUninstallFirewallRulesWithMinimalProperties()
29 {
30 var product = this.CreatePackageInstaller("FirewallRules");
31 product.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
32
33 // Validate new firewall exception details.
34 var expected1 = new RuleDetails("WiXToolset401 Test - 0001")
35 {
36 Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW,
37 ApplicationName = this.TestContext.GetTestInstallFolder(false, Path.Combine("FirewallRules", "product.wxs")),
38 Description = "WiX Toolset firewall exception rule integration test - minimal app properties",
39 Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN,
40 EdgeTraversal = true,
41 EdgeTraversalOptions = 1,
42 Enabled = true,
43 InterfaceTypes = "All",
44 LocalAddresses = "*",
45 Profiles = Int32.MaxValue,
46 Protocol = 256,
47 RemoteAddresses = "*",
48 SecureFlags = 0,
49 };
50
51 Verifier.VerifyFirewallRule("WiXToolset401 Test - 0001", expected1);
52
53 var expected2 = new RuleDetails("WiXToolset401 Test - 0002")
54 {
55 Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW,
56 Description = "WiX Toolset firewall exception rule integration test - minimal port properties",
57 Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN,
58 EdgeTraversal = false,
59 EdgeTraversalOptions = 0,
60 Enabled = true,
61 InterfaceTypes = "All",
62 LocalAddresses = "*",
63 LocalPorts = "23456",
64 Profiles = Int32.MaxValue,
65 Protocol = 6,
66 RemoteAddresses = "*",
67 RemotePorts = "*",
68 SecureFlags = 0,
69 };
70
71 Verifier.VerifyFirewallRule("WiXToolset401 Test - 0002", expected2);
72
73 product.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
74
75 // verify the firewall exceptions have been removed.
76 Assert.False(Verifier.FirewallRuleExists("WiXToolset401 Test - 0001"));
77 Assert.False(Verifier.FirewallRuleExists("WiXToolset401 Test - 0002"));
78 }
79
80 [RuntimeFact]
81 public void DisabledPortFirewallRuleIsEnabledAfterRepair()
82 {
83 var product = this.CreatePackageInstaller("FirewallRules");
84 product.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
85
86 Verifier.DisableFirewallRule("WiXToolset401 Test - 0002");
87
88 product.RepairProduct(MSIExec.MSIExecReturnCode.SUCCESS);
89
90 var expected = new RuleDetails("WiXToolset401 Test - 0002")
91 {
92 Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW,
93 Description = "WiX Toolset firewall exception rule integration test - minimal port properties",
94 Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN,
95 EdgeTraversal = false,
96 EdgeTraversalOptions = 0,
97 Enabled = true,
98 InterfaceTypes = "All",
99 LocalAddresses = "*",
100 LocalPorts = "23456",
101 Profiles = Int32.MaxValue,
102 Protocol = 6,
103 RemoteAddresses = "*",
104 RemotePorts = "*",
105 SecureFlags = 0,
106 };
107
108 Verifier.VerifyFirewallRule("WiXToolset401 Test - 0002", expected);
109 }
110
111 [RuntimeFact]
112 public void DisabledApplicationFirewallRuleIsEnabledAfterRepair()
113 {
114 var product = this.CreatePackageInstaller("FirewallRules");
115 product.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
116
117 Verifier.DisableFirewallRule("WiXToolset401 Test - 0001");
118
119 product.RepairProduct(MSIExec.MSIExecReturnCode.SUCCESS);
120
121 var expected = new RuleDetails("WiXToolset401 Test - 0001")
122 {
123 Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW,
124 ApplicationName = this.TestContext.GetTestInstallFolder(false, Path.Combine("FirewallRules", "product.wxs")),
125 Description = "WiX Toolset firewall exception rule integration test - minimal app properties",
126 Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN,
127 EdgeTraversal = true,
128 EdgeTraversalOptions = 1,
129 Enabled = true,
130 InterfaceTypes = "All",
131 LocalAddresses = "*",
132 Profiles = Int32.MaxValue,
133 Protocol = 256,
134 RemoteAddresses = "*",
135 SecureFlags = 0,
136 };
137
138 Verifier.VerifyFirewallRule("WiXToolset401 Test - 0001", expected);
139 }
140
141 [RuntimeFact]
142 public void MissingPortFirewallRuleIsAddedAfterRepair()
143 {
144 var product = this.CreatePackageInstaller("FirewallRules");
145 product.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
146
147 Verifier.RemoveFirewallRulesByName("WiXToolset401 Test - 0002");
148 Assert.False(Verifier.FirewallRuleExists("WiXToolset401 Test - 0002"));
149
150 product.RepairProduct(MSIExec.MSIExecReturnCode.SUCCESS);
151
152 var expected = new RuleDetails("WiXToolset401 Test - 0002")
153 {
154 Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW,
155 Description = "WiX Toolset firewall exception rule integration test - minimal port properties",
156 Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN,
157 EdgeTraversal = false,
158 EdgeTraversalOptions = 0,
159 Enabled = true,
160 InterfaceTypes = "All",
161 LocalAddresses = "*",
162 LocalPorts = "23456",
163 Profiles = Int32.MaxValue,
164 Protocol = 6,
165 RemoteAddresses = "*",
166 RemotePorts = "*",
167 SecureFlags = 0,
168 };
169
170 Verifier.VerifyFirewallRule("WiXToolset401 Test - 0002", expected);
171 }
172
173 [RuntimeFact]
174 public void MissingApplicationFirewallRuleIsAddedAfterRepair()
175 {
176 var product = this.CreatePackageInstaller("FirewallRules");
177 product.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS);
178
179 Verifier.RemoveFirewallRulesByName("WiXToolset401 Test - 0001");
180 Assert.False(Verifier.FirewallRuleExists("WiXToolset401 Test - 0001"));
181
182 product.RepairProduct(MSIExec.MSIExecReturnCode.SUCCESS);
183
184 var expected = new RuleDetails("WiXToolset401 Test - 0001")
185 {
186 Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW,
187 ApplicationName = this.TestContext.GetTestInstallFolder(false, Path.Combine("FirewallRules", "product.wxs")),
188 Description = "WiX Toolset firewall exception rule integration test - minimal app properties",
189 Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN,
190 EdgeTraversal = true,
191 EdgeTraversalOptions = 1,
192 Enabled = true,
193 InterfaceTypes = "All",
194 LocalAddresses = "*",
195 Profiles = Int32.MaxValue,
196 Protocol = 256,
197 RemoteAddresses = "*",
198 SecureFlags = 0,
199 };
200
201 Verifier.VerifyFirewallRule("WiXToolset401 Test - 0001", expected);
202 }
203 }
204}
diff --git a/src/test/msi/WixToolsetTest.MsiE2E/WixToolsetTest.MsiE2E.csproj b/src/test/msi/WixToolsetTest.MsiE2E/WixToolsetTest.MsiE2E.csproj
index d64c942a..a5536de4 100644
--- a/src/test/msi/WixToolsetTest.MsiE2E/WixToolsetTest.MsiE2E.csproj
+++ b/src/test/msi/WixToolsetTest.MsiE2E/WixToolsetTest.MsiE2E.csproj
@@ -7,6 +7,17 @@
7 <PlatformTarget>x64</PlatformTarget> 7 <PlatformTarget>x64</PlatformTarget>
8 <IsWixTestProject>true</IsWixTestProject> 8 <IsWixTestProject>true</IsWixTestProject>
9 </PropertyGroup> 9 </PropertyGroup>
10 <ItemGroup>
11 <COMReference Include="NetFwTypeLib">
12 <VersionMinor>0</VersionMinor>
13 <VersionMajor>1</VersionMajor>
14 <Guid>58fbcf7c-e7a9-467c-80b3-fc65e8fcca08</Guid>
15 <Lcid>0</Lcid>
16 <WrapperTool>tlbimp</WrapperTool>
17 <Isolated>false</Isolated>
18 <EmbedInteropTypes>False</EmbedInteropTypes>
19 </COMReference>
20 </ItemGroup>
10 21
11 <ItemGroup> 22 <ItemGroup>
12 <Content Include="runtests.cmd" CopyToOutputDirectory="PreserveNewest" /> 23 <Content Include="runtests.cmd" CopyToOutputDirectory="PreserveNewest" />