Skip to content

Commit

Permalink
Move URI validation to a new file
Browse files Browse the repository at this point in the history
  • Loading branch information
AmelBawa-msft committed Oct 1, 2024
1 parent e369739 commit 7b48281
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 90 deletions.
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@
<ClInclude Include="ContextOrchestrator.h" />
<ClInclude Include="COMContext.h" />
<ClInclude Include="Public\ConfigurationSetProcessorFactoryRemoting.h" />
<ClInclude Include="Workflows\SmartScreenFlow.h" />
<ClInclude Include="Workflows\ConfigurationFlow.h" />
<ClInclude Include="Workflows\DependenciesFlow.h" />
<ClInclude Include="ExecutionArgs.h" />
Expand Down Expand Up @@ -451,6 +452,7 @@
<ClCompile Include="ConfigurationWingetDscModuleUnitValidation.cpp" />
<ClCompile Include="ConfigureExportCommand.cpp" />
<ClCompile Include="ContextOrchestrator.cpp" />
<ClCompile Include="Workflows\SmartScreenFlow.cpp" />
<ClCompile Include="Workflows\ConfigurationFlow.cpp" />
<ClCompile Include="Workflows\DependenciesFlow.cpp" />
<ClCompile Include="PackageCollection.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@
<ClInclude Include="Commands\ConfigureListCommand.h">
<Filter>Commands</Filter>
</ClInclude>
<ClInclude Include="Workflows\SmartScreenFlow.h">
<Filter>Workflows</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
Expand Down Expand Up @@ -484,6 +487,9 @@
<ClCompile Include="Commands\ConfigureListCommand.cpp">
<Filter>Commands</Filter>
</ClCompile>
<ClCompile Include="Workflows\SmartScreenFlow.cpp">
<Filter>Workflows</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/Commands/ConfigureCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "ConfigureValidateCommand.h"
#include "ConfigureExportCommand.h"
#include "Workflows/ConfigurationFlow.h"
#include "Workflows/SmartScreenFlow.h"
#include "Workflows/MSStoreInstallerHandler.h"
#include "ConfigurationCommon.h"

Expand Down Expand Up @@ -77,6 +78,7 @@ namespace AppInstaller::CLI
context <<
VerifyIsFullPackage <<
VerifyFileOrUri(Execution::Args::Type::ConfigurationFile) <<
EvaluateUri <<
CreateConfigurationProcessor <<
OpenConfigurationSet <<
ShowConfigurationSet <<
Expand Down
90 changes: 0 additions & 90 deletions src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
#include <winget/ExperimentalFeature.h>
#include <winget/SelfManagement.h>
#include <winrt/Microsoft.Management.Configuration.h>
#include <UriValidation/UriValidation.h>
#include <urlmon.h>

using namespace AppInstaller::CLI::Execution;
using namespace winrt::Microsoft::Management::Configuration;
Expand Down Expand Up @@ -961,87 +959,6 @@ namespace AppInstaller::CLI::Workflow
set.Path(absolutePath.wstring());
}

// Get Uri zone for a given uri or file path.
Settings::ConfigurationAllowedZonesOptions GetUriZone(const std::string& uri)
{
DWORD dwZone;
auto pInternetSecurityManager = winrt::create_instance<IInternetSecurityManager>(CLSID_InternetSecurityManager, CLSCTX_ALL);
pInternetSecurityManager->MapUrlToZone(AppInstaller::Utility::ConvertToUTF16(uri).c_str(), &dwZone, 0);

// Treat all zones higher than untrusted as untrusted
if (dwZone > static_cast<DWORD>(Settings::ConfigurationAllowedZonesOptions::UntrustedSites))
{
return Settings::ConfigurationAllowedZonesOptions::UntrustedSites;
}

return static_cast<Settings::ConfigurationAllowedZonesOptions>(dwZone);
}

// Validate group policy for a given zone.
bool IsBlockedByGroupPolicy(Execution::Context& context, const Settings::ConfigurationAllowedZonesOptions zone)
{
auto configurationPolicies = Settings::GroupPolicies().GetValue<Settings::ValuePolicy::ConfigurationAllowedZones>();
if (!configurationPolicies.has_value())
{
AICLI_LOG(Config, Warning, << "ConfigurationAllowedZones policy is not set");
return false;
}

if (configurationPolicies->find(zone) == configurationPolicies->end())
{
AICLI_LOG(Config, Warning, << "Configuration is not configured in the zone " << zone);
return false;
}

auto isAllowed = configurationPolicies->at(zone);
if(!isAllowed)
{
context.Reporter.Error() << "Configuration is disabled for Zone: " << zone << std::endl;
return true;
}

AICLI_LOG(Config, Info, << "Configuration is configured in zone " << zone << " with value " << (isAllowed ? "allowed" : "blocked"));
return false;
}

// Validate smart screen for a given url.
bool IsBlockedBySmartScreen(Execution::Context& context, const std::string& url)
{
auto response = AppInstaller::UriValidation::UriValidation(url);
switch (response.Decision())
{
case AppInstaller::UriValidation::UriValidationDecision::Block:
context.Reporter.Error() << std::endl << "Blocked by smart screen" << std::endl << "Feedback: " << response.Feedback() << std::endl;
return true;
case AppInstaller::UriValidation::UriValidationDecision::Allow:
default:
return false;
}
}

bool IsSmartScreenRequired(Settings::ConfigurationAllowedZonesOptions zone)
{
return zone == Settings::ConfigurationAllowedZonesOptions::Internet
|| zone == Settings::ConfigurationAllowedZonesOptions::UntrustedSites;
}

// Evaluate a given uri for configuration.
HRESULT EvaluateUri(Execution::Context& context, const std::string& uri)
{
auto zone = GetUriZone(uri);
if(IsBlockedByGroupPolicy(context, zone))
{
return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY;
}

if (IsSmartScreenRequired(zone) && IsBlockedBySmartScreen(context, uri))
{
return APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE;
}

return NO_ERROR;
}

void OpenConfigurationSet(Execution::Context& context, const std::string& argPath, bool allowRemote)
{
auto progressScope = context.Reporter.BeginAsyncProgress(true);
Expand All @@ -1060,13 +977,6 @@ namespace AppInstaller::CLI::Workflow
AICLI_TERMINATE_CONTEXT(ERROR_NOT_SUPPORTED);
}

auto uriValidation = EvaluateUri(context, argPath);
if (uriValidation != NO_ERROR)
{
AICLI_LOG(Config, Error, << "URI validation blocked this uri: " << argPath);
AICLI_TERMINATE_CONTEXT(uriValidation);
}

std::ostringstream stringStream;
ProgressCallback emptyCallback;
Utility::DownloadToStream(argPath, stringStream, Utility::DownloadType::ConfigurationFile, emptyCallback);
Expand Down
112 changes: 112 additions & 0 deletions src/AppInstallerCLICore/Workflows/SmartScreenFlow.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include "SmartScreenFlow.h"
#include <AppInstallerDownloader.h>
#include <UriValidation/UriValidation.h>

namespace AppInstaller::CLI::Workflow
{
bool IsSmartScreenRequired(Settings::ConfigurationAllowedZonesOptions zone)
{
return zone == Settings::ConfigurationAllowedZonesOptions::Internet
|| zone == Settings::ConfigurationAllowedZonesOptions::UntrustedSites;
}

// Validate smart screen for a given url.
bool IsBlockedBySmartScreen(Execution::Context& context, const std::string& url)
{
auto response = AppInstaller::UriValidation::UriValidation(url);
switch (response.Decision())
{
case AppInstaller::UriValidation::UriValidationDecision::Block:
context.Reporter.Error() << std::endl << "Blocked by smart screen" << std::endl << "Feedback: " << response.Feedback() << std::endl;
return true;
case AppInstaller::UriValidation::UriValidationDecision::Allow:
default:
return false;
}
}

// Get Uri zone for a given uri or file path.
Settings::ConfigurationAllowedZonesOptions GetUriZone(const std::string& uri)
{
DWORD dwZone;
auto pInternetSecurityManager = winrt::create_instance<IInternetSecurityManager>(CLSID_InternetSecurityManager, CLSCTX_ALL);
pInternetSecurityManager->MapUrlToZone(AppInstaller::Utility::ConvertToUTF16(uri).c_str(), &dwZone, 0);

// Treat all zones higher than untrusted as untrusted
if (dwZone > static_cast<DWORD>(Settings::ConfigurationAllowedZonesOptions::UntrustedSites))
{
return Settings::ConfigurationAllowedZonesOptions::UntrustedSites;
}

return static_cast<Settings::ConfigurationAllowedZonesOptions>(dwZone);
}

// Validate group policy for a given zone.
bool IsBlockedByGroupPolicy(Execution::Context& context, const Settings::ConfigurationAllowedZonesOptions zone)
{
auto configurationPolicies = Settings::GroupPolicies().GetValue<Settings::ValuePolicy::ConfigurationAllowedZones>();
if (!configurationPolicies.has_value())
{
AICLI_LOG(Config, Warning, << "ConfigurationAllowedZones policy is not set");
return false;
}

if (configurationPolicies->find(zone) == configurationPolicies->end())
{
AICLI_LOG(Config, Warning, << "Configuration is not configured in the zone " << zone);
return false;
}

auto isAllowed = configurationPolicies->at(zone);
if(!isAllowed)
{
context.Reporter.Error() << "Configuration is disabled for Zone: " << zone << std::endl;
return true;
}

AICLI_LOG(Config, Info, << "Configuration is configured in zone " << zone << " with value " << (isAllowed ? "allowed" : "blocked"));
return false;
}

HRESULT EvaluateUri(Execution::Context& context, const std::string& uri)
{
auto zone = GetUriZone(uri);
if(IsBlockedByGroupPolicy(context, zone))
{
return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY;
}

if (IsSmartScreenRequired(zone) && IsBlockedBySmartScreen(context, uri))
{
return APPINSTALLER_CLI_ERROR_SOURCE_NOT_SECURE;
}

return NO_ERROR;
}

void EvaluateUri(Execution::Context& context)
{
if (context.Args.Contains(Execution::Args::Type::ConfigurationFile))
{
std::string argPath{ context.Args.GetArg(Execution::Args::Type::ConfigurationFile) };

if (Utility::IsUrlRemote(argPath))
{
context.Reporter.Info() << "Validating Uri: " << argPath;
auto uriValidation = EvaluateUri(context, argPath);
if (uriValidation != NO_ERROR)
{
AICLI_LOG(Config, Error, << "URI validation blocked this uri: " << argPath);
AICLI_TERMINATE_CONTEXT(uriValidation);
}
}
else
{
AICLI_LOG(Config, Info, << "Skipping Uri validation for local file: " << argPath);
}
}
}
}
10 changes: 10 additions & 0 deletions src/AppInstallerCLICore/Workflows/SmartScreenFlow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "ExecutionContext.h"

namespace AppInstaller::CLI::Workflow
{
void EvaluateUri(Execution::Context& context);
}

0 comments on commit 7b48281

Please sign in to comment.