Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Security and Compliance tasks in our Release pipeline #11849

Merged
85 commits merged into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
1977f6b
Add compliance task to pipeline
miniksa Aug 4, 2021
a151fed
attempt machine version test and change to artifact feed connection
miniksa Aug 4, 2021
d9ffba3
bippity boppity boo
miniksa Aug 4, 2021
6a63f3c
goof less
miniksa Aug 4, 2021
8b38ccc
undo temporary testing stuff
miniksa Aug 4, 2021
75e5c95
re-sprinkle helix access tokens
miniksa Aug 4, 2021
b9dc74e
conditionally append access token
miniksa Aug 4, 2021
ebe8075
use correct method name
miniksa Aug 5, 2021
ffc5dc3
attempt to break out compliance tasks onto 2019 image and temporarily…
miniksa Aug 5, 2021
1dc142b
add checkout bit and nerf dependency
miniksa Aug 5, 2021
75c69fd
wrong indent because yaml
miniksa Aug 5, 2021
8fa0dd1
apparently pkgesseccomp not updated to 12 for 1es
miniksa Aug 5, 2021
68c52c2
wrong destination folder for copy pasta
miniksa Aug 5, 2021
40f77ff
try a different place for archives?
miniksa Aug 5, 2021
ed7ccac
make testing this slightly easier temporarily
miniksa Aug 5, 2021
61eea11
try to show output
miniksa Aug 5, 2021
96a7f25
try to show output
miniksa Aug 5, 2021
865a0d7
try to show output
miniksa Aug 5, 2021
2e3c8ac
more unzip fun
miniksa Aug 5, 2021
446f4b5
messin' about
miniksa Aug 5, 2021
b237283
vmimage
miniksa Aug 5, 2021
5c4c708
add pkges setup task to compliance pipe
miniksa Aug 10, 2021
77eab3c
update match patterns
miniksa Aug 10, 2021
a4d6ad8
separate extract into two steps I guess
miniksa Aug 10, 2021
f7b1e96
throw more things at the wall
miniksa Aug 10, 2021
4f697bd
remove line
miniksa Nov 30, 2021
826f8c0
let's see how this blends
miniksa Nov 30, 2021
a9a3789
fiddle about
miniksa Nov 30, 2021
aa12f45
ran out of heap space on compile with x86. fortunately v3 of the task…
miniksa Dec 1, 2021
c9ded19
apparently npm is required by v3. go back to v2 and force x64 to pass…
miniksa Dec 2, 2021
cb829f5
use v3 I guess and install npm
miniksa Dec 3, 2021
8607097
stop confusing myself.
miniksa Dec 3, 2021
4c34f0b
wrong node installer
miniksa Dec 3, 2021
fd14a71
Revert "wrong node installer"
miniksa Dec 3, 2021
ee69e06
Revert "stop confusing myself."
miniksa Dec 3, 2021
95a1fff
ok let's try this who knows.
miniksa Dec 3, 2021
9908721
build msix default
miniksa Dec 3, 2021
0b6ac59
boo
miniksa Dec 3, 2021
7c7e427
add msbuild.exe
miniksa Dec 3, 2021
ead0864
throw more eggs at the wall
miniksa Dec 3, 2021
c4a5c09
true
miniksa Dec 3, 2021
5849938
nerf some stuff while I test
miniksa Dec 3, 2021
ebd8aae
try something else who knows
miniksa Dec 3, 2021
8df6d1d
Try building just the WindowsTerminal proj
miniksa Dec 4, 2021
cb5eed8
turn it up to 11
miniksa Dec 4, 2021
9e6733f
turn off policheck for now
miniksa Dec 6, 2021
ae7e598
try turning off prefast
miniksa Dec 6, 2021
925e93e
and turn policheck back on
miniksa Dec 6, 2021
a9f3d4b
make some bins
miniksa Dec 6, 2021
6281c88
try 4 hours why not
miniksa Dec 6, 2021
e97db4e
try going to the source
miniksa Dec 7, 2021
6c4dd22
continue slamming head directly into wall
miniksa Dec 7, 2021
fb78f11
enable xfg, I think
miniksa Dec 7, 2021
f15c650
temporarily nerf actual signing
miniksa Dec 7, 2021
f289a67
run separate credscan task
miniksa Dec 7, 2021
1b9ee5a
ok that was too sneaky. use comments instead.
miniksa Dec 7, 2021
49f1f4b
suppress suppression file while we don't have anything to suppress
miniksa Dec 8, 2021
e5e3c63
try to fix cfgcheck
miniksa Dec 9, 2021
8f4e322
don't policheck generated winrt files
miniksa Dec 9, 2021
b162280
try to fix cfg?
miniksa Dec 9, 2021
ec4db65
ok how about this directory, policheck? chew on that!
miniksa Dec 9, 2021
cd0db77
exclude more from policheck that doesn't make sense
miniksa Dec 9, 2021
9d7406a
ok here goes nothing
miniksa Dec 9, 2021
6650747
make compliance run in parallel
miniksa Dec 13, 2021
a9c80cb
a leftover
miniksa Dec 13, 2021
9dabd54
Merge branch 'main' into dev/miniksa/compliance
miniksa Dec 13, 2021
51b5edd
syntax error
miniksa Dec 13, 2021
2b3d732
its shallow isn't it
miniksa Dec 13, 2021
b5290a7
flag it on and off
miniksa Dec 13, 2021
81fc0ff
flip checks around again
miniksa Dec 13, 2021
8e7fd87
spell check!
miniksa Dec 14, 2021
2cb310d
work around compiler crash when working with these lambdas in a macro…
miniksa Dec 14, 2021
b461264
code format
miniksa Dec 14, 2021
7d365e9
extend timeout
miniksa Dec 14, 2021
5c402de
back to xfg
miniksa Dec 14, 2021
fc7b009
add binskim explicitly?
miniksa Dec 15, 2021
140227d
Add a lot of commentary and try to fix binskim search patterns
miniksa Dec 15, 2021
0644e12
adjust binskim pattern
miniksa Dec 15, 2021
b880244
Revert "back to xfg"
miniksa Dec 15, 2021
567a0ba
note
miniksa Dec 15, 2021
510b8dd
spellcheck
miniksa Dec 15, 2021
7bcb698
Merge branch 'main' into dev/miniksa/compliance
miniksa Dec 15, 2021
40381eb
need to restore pgo step too
miniksa Dec 15, 2021
c641dee
separate out compliance job, I think?
miniksa Jan 4, 2022
e1e7cbd
Merge branch 'main' into dev/miniksa/compliance
miniksa Jan 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,7 @@ gset
gsl
GTP
GTR
guardxfg
guc
gui
guidatom
Expand Down Expand Up @@ -1802,6 +1803,7 @@ PNTSTATUS
POBJECT
Podcast
POINTSLIST
Poli
POLYTEXTW
popd
POPF
Expand Down Expand Up @@ -2826,6 +2828,7 @@ xdy
XEncoding
xes
xff
xfg
XFile
XFORM
XManifest
Expand Down
11 changes: 11 additions & 0 deletions build/config/PolicheckExclusions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<PoliCheckExclusions>
<!-- All strings must be UPPER CASE -->
<!--Each of these exclusions is a folder name -if \[name]\exists in the file path, it will be skipped -->
<Exclusion Type="FolderPathFull">winrt|.git|oss|packages</Exclusion>
<!--Each of these exclusions is a folder name -if any folder or file starts with "\[name]", it will be skipped -->
<!--<Exclusion Type="FolderPathStart">ABC|XYZ</Exclusion>-->
miniksa marked this conversation as resolved.
Show resolved Hide resolved
<!--Each of these file types will be completely skipped for the entire scan -->
<Exclusion Type="FileType">.PNG|.SVG|.BMP|.ICO</Exclusion>
<!--The specified file names will be skipped during the scan regardless which folder they are in -->
<!--<Exclusion Type="FileName">ABC.TXT|XYZ.CS</Exclusion>-->
</PoliCheckExclusions>
15 changes: 11 additions & 4 deletions build/pipelines/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ parameters:
displayName: "Build Windows Terminal MSIX"
type: boolean
default: true
- name: runCompliance
displayName: "Run Compliance and Security Build"
type: boolean
default: true
- name: buildTerminalVPack
displayName: "Build Windows Terminal VPack"
type: boolean
Expand All @@ -34,7 +38,6 @@ parameters:
- Optimize
- Instrument
- None

- name: buildConfigurations
type: object
default:
Expand Down Expand Up @@ -65,11 +68,11 @@ jobs:
BuildConfiguration: ${{ config }}
BuildPlatform: ${{ platform }}
displayName: Build
timeoutInMinutes: 240
cancelTimeoutInMinutes: 1
steps:
- checkout: self
clean: true
fetchDepth: 1
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PGO can't resolve from peer branches with depth 1 here, so I'll allow it for release builds.

submodules: true
persistCredentials: True
- task: PkgESSetupBuild@12
Expand All @@ -90,6 +93,7 @@ jobs:
displayName: Use NuGet 5.10
inputs:
versionSpec: 5.10
- task: NuGetAuthenticate@0
# In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
# This should be `task: NuGetCommand@2`
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
Expand Down Expand Up @@ -156,6 +160,7 @@ jobs:
- ${{ if eq(parameters.buildTerminal, true) }}:
- task: VSBuild@1
displayName: Build solution **\OpenConsole.sln
condition: true
inputs:
solution: '**\OpenConsole.sln'
vsVersion: 16.0
Expand Down Expand Up @@ -196,8 +201,6 @@ jobs:
filePath: build\scripts\Index-Pdbs.ps1
arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
errorActionPreference: silentlyContinue
- task: ComponentGovernanceComponentDetection@0
displayName: Component Detection
- task: PowerShell@2
displayName: Run Unit Tests
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
Expand Down Expand Up @@ -254,6 +257,7 @@ jobs:
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf
ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)

- task: PublishSymbols@2
displayName: Publish symbols path
continueOnError: True
Expand All @@ -265,6 +269,9 @@ jobs:
IndexSources: false
SymbolServerType: TeamServices

- ${{ if eq(parameters.runCompliance, true) }}:
miniksa marked this conversation as resolved.
Show resolved Hide resolved
- template: ./templates/build-console-compliance-job.yml

- ${{ if eq(parameters.buildTerminal, true) }}:
- job: BundleAndSign
displayName: Create and sign AppX/MSIX bundles
Expand Down
224 changes: 224 additions & 0 deletions build/pipelines/templates/build-console-compliance-job.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
jobs:
- job: Compliance
# We don't *need* a matrix but there's no other way to set parameters on a "job"
# in the AzDO YAML syntax. It would have to be a "stage" or a "template".
# Doesn't matter. We're going to do compliance on Release x64 because
# that's the one all the tooling works against for sure.
strategy:
matrix:
Release_x64:
BuildConfiguration: Release
BuildPlatform: x64
displayName: Validate Security and Compliance
timeoutInMinutes: 240
steps:
- checkout: self
clean: true
submodules: true
persistCredentials: True
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- task: PowerShell@2
displayName: Rationalize Build Platform
inputs:
targetType: inline
script: >-
$Arch = "$(BuildPlatform)"

If ($Arch -Eq "x86") { $Arch = "Win32" }

Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
- task: NuGetToolInstaller@1
displayName: Use NuGet 5.10
inputs:
versionSpec: 5.10
- task: NuGetAuthenticate@0
# In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
# This should be `task: NuGetCommand@2`
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
displayName: Restore NuGet packages for extraneous build actions
inputs:
command: restore
feedsToUse: config
configPath: NuGet.config
restoreSolution: build/packages.config
restoreDirectory: '$(Build.SourcesDirectory)\packages'
- task: NuGetCommand@2
displayName: NuGet custom
inputs:
command: custom
selectOrConfig: config
nugetConfigPath: NuGet.Config
arguments: restore OpenConsole.sln -SolutionDirectory $(Build.SourcesDirectory)
- task: UniversalPackages@0
displayName: Download terminal-internal Universal Package
inputs:
feedListDownload: 2b3f8893-a6e8-411f-b197-a9e05576da48
packageListDownload: e82d490c-af86-4733-9dc4-07b772033204
versionListDownload: $(TerminalInternalPackageVersion)
- task: TouchdownBuildTask@1
displayName: Download Localization Files
inputs:
teamId: 7105
authId: $(TouchdownAppId)
authKey: $(TouchdownAppKey)
resourceFilePath: >-
src\cascadia\TerminalApp\Resources\en-US\Resources.resw

src\cascadia\TerminalControl\Resources\en-US\Resources.resw

src\cascadia\TerminalConnection\Resources\en-US\Resources.resw

src\cascadia\TerminalSettingsModel\Resources\en-US\Resources.resw

src\cascadia\TerminalSettingsEditor\Resources\en-US\Resources.resw

src\cascadia\WindowsTerminalUniversal\Resources\en-US\Resources.resw

src\cascadia\CascadiaPackage\Resources\en-US\Resources.resw
appendRelativeDir: true
localizationTarget: false
pseudoSetting: Included
- task: PowerShell@2
displayName: Move Loc files one level up
inputs:
targetType: inline
script: >-
$Files = Get-ChildItem . -R -Filter 'Resources.resw' | ? FullName -Like '*en-US\*\Resources.resw'

$Files | % { Move-Item -Verbose $_.Directory $_.Directory.Parent.Parent -EA:Ignore }
pwsh: true

# 1ES Component Governance onboarding (Detects open source components). See https://docs.opensource.microsoft.com/tools/cg.html
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: Component Detection

# # PREfast and PoliCheck need Node. Install that first.
- task: NodeTool@0

# !!! NOTE !!! Run PREfast first. Some of the other tasks are going to run on a completed build.
# PREfast is going to build the code as a part of its analysis and the generated sources
# and output binaries will be sufficient for the rest of the analysis.
# If you disable this, the other tasks won't likely work. You would have to add a build
# step instead that builds the code normally before calling them.
# Also... PREfast will rebuild anyway so that's why we're not running a normal build first.
# Waste of time to build twice.
# PREfast. See https://www.1eswiki.com/wiki/SDL_Native_Rules_Build_Task

# The following 1ES tasks all operate completely differently and have a different syntax for usage.
# Most notable is every one of them has a different way of excluding things.
# Go see their 1eswiki.com pages to figure out how to exclude things.
# When writing exclusions, try to make them narrow so when new projects/binaries are added, they
# cause an error here and have to be explicitly pulled out. Don't write an exclusion so broad
# that it will catch other new stuff.

# https://www.1eswiki.com/wiki/PREfast_Build_Task
# Builds the project with C/C++ static analysis tools to find coding flaws and vulnerabilities
# !!! WARNING !!! It doesn't work with WAPPROJ packaging projects. Build the sub-projects instead.
- task: securedevelopmentteam.vss-secure-development-tools.build-task-prefast.SDLNativeRules@3
displayName: 'Run the PREfast SDL Native Rules for MSBuild'
condition: succeededOrFailed()
inputs:
msBuildCommandline: msbuild.exe /nologo /m /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /p:WindowsTerminalReleaseBuild=true /p:platform=$(BuildPlatform) /p:configuration=$(BuildConfiguration) /t:Terminal\Window\WindowsTerminal /p:VisualStudioVersion=16.0 $(Build.SourcesDirectory)\OpenConsole.sln

# Copies output from PREfast SDL Native Rules task to expected location for consumption by PkgESSecComp
- task: CopyFiles@1
displayName: 'Copy PREfast xml files to SDLNativeRulesDir'
inputs:
SourceFolder: '$(Agent.BuildDirectory)'
Contents: |
**\*.nativecodeanalysis.xml
TargetFolder: '$(Agent.BuildDirectory)\_sdt\logs\SDLNativeRules'

# https://www.1eswiki.com/index.php?title=PoliCheck_Build_Task
# Scans the text of source code, comments, and content for terminology that could be sensitive for legal, cultural, or geopolitical reasons.
# (Also finds vulgarities... takes all the fun out of everything.)
- task: securedevelopmentteam.vss-secure-development-tools.build-task-policheck.PoliCheck@2
displayName: 'Run PoliCheck'
inputs:
targetType: F
targetArgument: $(Build.SourcesDirectory)
result: PoliCheck.xml
optionsFC: 1
optionsXS: 1
optionsUEPath: $(Build.SourcesDirectory)\build\config\PolicheckExclusions.xml
optionsHMENABLE: 0
continueOnError: true

# https://www.1eswiki.com/wiki/CredScan_Azure_DevOps_Build_Task
# Searches through source code and build outputs for a credential left behind in the open
- task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3
displayName: 'Run CredScan'
inputs:
outputFormat: pre
# suppressionsFile: LocalSuppressions.json
batchSize: 20
debugMode: false
continueOnError: true

# https://www.1eswiki.com/wiki/BinSkim_Build_Task
# Searches managed and unmanaged binaries for known security vulnerabilities.
- task: securedevelopmentteam.vss-secure-development-tools.build-task-binskim.BinSkim@4
displayName: 'Run BinSkim'
inputs:
TargetPattern: guardianGlob
# See https://aka.ms/gdn-globs for how to do match patterns
AnalyzeTargetGlob: $(Build.SourcesDirectory)\bin\**\*.dll;$(Build.SourcesDirectory)\bin\**\*.exe;-:file|**\Microsoft.UI.Xaml.dll;-:file|**\Microsoft.Toolkit.Win32.UI.XamlHost.dll;-:file|**\vcruntime*.dll;-:file|**\vcomp*.dll;-:file|**\vccorlib*.dll;-:file|**\vcamp*.dll;-:file|**\msvcp*.dll;-:file|**\concrt*.dll;-:file|**\TerminalThemeHelpers*.dll;-:file|**\cpprest*.dll
continueOnError: true

# Set XES_SERIALPOSTBUILDREADY to run Security and Compliance task once per build
- powershell: Write-Host “##vso[task.setvariable variable=XES_SERIALPOSTBUILDREADY;]true”
displayName: 'Set XES_SERIALPOSTBUILDREADY Vars'

# https://www.osgwiki.com/wiki/Package_ES_Security_and_Compliance
# Does a few things:
# - Ensures that Windows-required compliance tasks are run either inside this task
# or were run as a previous step prior to this one
# (PREfast, PoliCheck, Credscan)
# - Runs Windows-specific compliance tasks inside the task
# + CheckCFlags - ensures that compiler and linker flags meet Windows standards
# + CFGCheck/XFGCheck - ensures that Control Flow Guard (CFG) or
# eXtended Flow Guard (XFG) are enabled on binaries
# NOTE: CFG is deprecated and XFG isn't fully ready yet.
# NOTE2: CFG fails on an XFG'd binary
# - Brokers all security/compliance task logs to "Trust Services Automation (TSA)" (https://aka.ms/tsa)
# which is a system that maps all errors into the appropriate bug database
# template for each organization since they all vary. It should also suppress
# new bugs when one already exists for the product.
# This one is set up to go to the OS repository and use the given parameters
# to file bugs to our AzDO product path.
# If we don't use PkgESSecComp to do this for us, we need to install the TSA task
# ourselves in this pipeline to finalize data upload and bug creation.
# !!! NOTE !!! This task goes *LAST* after any other compliance tasks so it catches their logs
- task: PkgESSecComp@10
displayName: 'Security and Compliance tasks'
inputs:
fileNewBugs: false
areaPath: 'OS\WDX\DXP\WinDev\Terminal'
teamProject: 'OS'
iterationPath: 'OS\Future'
bugTags: 'TerminalReleaseCompliance'
scanAll: true
errOnBugs: false
failOnStdErr: true
taskLogVerbosity: Diagnostic
secCompConfigFromTask: |
# Overrides default build sources directory
sourceTargetOverrideAll: $(Build.SourcesDirectory)
# Overrides default build binaries directory when "Scan all" option is specified
binariesTargetOverrideAll: $(Build.SourcesDirectory)\bin

# Set the tools to false if they should not run in the build
tools:
- toolName: CheckCFlags
enable: true
- toolName: CFGCheck
enable: true
- toolName: Policheck
enable: false
- toolName: CredScan
enable: false
- toolName: XFGCheck
enable: false
20 changes: 12 additions & 8 deletions src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,18 @@ Model::Profile CascadiaSettings::CreateNewProfile()
return *newProfile;
}

template<typename T>
static bool isProfilesDefaultsOrigin(const T& profile)
{
return profile && profile.Origin() != winrt::Microsoft::Terminal::Settings::Model::OriginTag::ProfilesDefaults;
}

template<typename T>
static bool isProfilesDefaultsOriginSub(const T& sub)
{
return sub && isProfilesDefaultsOrigin(sub.SourceProfile());
}

Comment on lines +222 to +233
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was because of an internal compiler error. I have a thread out to the team to hopefully debug and fix the compiler. It couldn't handle the other form.

// Method Description:
// - Duplicate a new profile based off another profile's settings
// - This differs from Profile::Copy because it also copies over settings
Expand Down Expand Up @@ -250,14 +262,6 @@ Model::Profile CascadiaSettings::DuplicateProfile(const Model::Profile& source)

const auto duplicated = _createNewProfile(newName);

static constexpr auto isProfilesDefaultsOrigin = [](const auto& profile) -> bool {
return profile && profile.Origin() != OriginTag::ProfilesDefaults;
};

static constexpr auto isProfilesDefaultsOriginSub = [](const auto& sub) -> bool {
return sub && isProfilesDefaultsOrigin(sub.SourceProfile());
};

#define NEEDS_DUPLICATION(settingName) source.Has##settingName() || isProfilesDefaultsOrigin(source.settingName##OverrideSource())
#define NEEDS_DUPLICATION_SUB(source, settingName) source.Has##settingName() || isProfilesDefaultsOriginSub(source.settingName##OverrideSource())

Expand Down
1 change: 1 addition & 0 deletions src/common.build.pre.props
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ignore this. It will make it easier for me to move to XFG later as more flags go here.

</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
Expand Down