Skip to content

URI validation for winget cli commands#4707

Open
AmelBawa-msft wants to merge 40 commits intomasterfrom
user/amelbawa/uri-validation
Open

URI validation for winget cli commands#4707
AmelBawa-msft wants to merge 40 commits intomasterfrom
user/amelbawa/uri-validation

Conversation

@AmelBawa-msft
Copy link
Contributor

@AmelBawa-msft AmelBawa-msft commented Aug 5, 2024

Description

flowchart TD
    %%{init:{'flowchart':{"defaultRenderer": "elk"}}}%%
    A[winget cli] --> B
    B{is configuration?} --> |yes| C[Get URI Zone]
    B --> |no| D{Package catalog trusted?}
    D --> |no| C
    D --> |yes| E(Resume operation)
    C --> F{Zone blocked by policy?}
    F -->|yes| G(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY)
    F --> |no| H{Zone Internet or Untrusted?}
    H --> |yes| I{SmartScreen blocked by policy?}
    H --> |no| E
    I --> |no| J{Blocked by SmartScreen?}
    I --> |yes| E
    J --> |yes| K(APPINSTALLER_CLI_ERROR_BLOCKED_BY_REPUTATION_SERVICE)
    J --> |no| E

    style G fill:red,stroke:darkred,color:white
    style K fill:red,stroke:darkred,color:white
    style E fill:green,stroke:darkgreen,color:white
Loading

TODO

  • Finalize strings

Follow ups

  • Add MotW HostUrl validation (TODO link to issue)
Microsoft Reviewers: Open in CodeFlow

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

Choose a reason for hiding this comment

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

TODO:

  • Finalizing strings
  • Move to resource file


If you enable this setting, the specified proxy will be used by default.</string>
<string id="EnableDSCAllowedZones">Enable App Installer Allowed Zones for DSC</string>
<string id="EnableDSCAllowedZonesExplanation"></string>
Copy link
Member

Choose a reason for hiding this comment

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

Waiting for PM input?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but also, we're still thinking about the scope that this policy should cover (DSC only, or more ... )

switch (response.Decision())
{
case AppInstaller::UriValidation::UriValidationDecision::Block:
context.Reporter.Error() << std::endl << "Blocked by smart screen" << std::endl << "Feedback: " << response.Feedback() << std::endl;
Copy link
Member

Choose a reason for hiding this comment

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

If Feedback isn't localized, it should probably only be sent to the log and not stdout.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All strings are not finalized/localized yet, there's an email thread for deciding on a couple things including localization

// The decision made based on the Uri validation.
enum class UriValidationDecision
{
Allow,
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't there also a Warn response that SmartScreen can return?

https://learn.microsoft.com/en-us/defender-endpoint/web-protection-overview#order-of-precedence

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, there's a PUA response as well. However, PUA/Warn will also be treated as Blocked, but the details will be communicated in the logs instead and the logic will be in the injected code. I can potentially reduce this enum to a bool. I will revisit this later once things are a little bit more finalized.

// Uri to give feedback to smart screen about the decision.
std::string m_feedback;
public:
UriValidationResult(UriValidationDecision decision) : m_decision(decision), m_feedback(std::string()) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Should there be a default (enpty) constructor?

<None Include="packages.config" Condition="Exists('$(MSBuildProjectDirectory)\packages.config')" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CppWinRT" Version="2.0.230706.1" TargetFramework="native" />
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesnt seem to match the version of cppwinrt in NOTICE.txt. Does the version need to be bumped across all the solution files, or is there something I’m missing?

Microsoft.Windows.CppWinRT 2.0.210503.1 - MIT

@AmelBawa-msft AmelBawa-msft changed the title Validate configuration Urls URI validation for winget cli commands Oct 17, 2024
@AmelBawa-msft AmelBawa-msft marked this pull request as ready for review October 23, 2024 20:35
@AmelBawa-msft AmelBawa-msft requested a review from a team as a code owner October 23, 2024 20:35
Comment on lines 125 to 128
<string id="EnableWindowsPackageManagerAllowedSecurityZones">Enable App Installer Allowed Zones for the Windows Package Manager</string>
<string id="EnableWindowsPackageManagerAllowedSecurityZonesExplanation"></string>
<string id="EnableWindowsPackageManagerSmartScreenCheck">Enable Microsoft SmartScreen checks for the Windows Package Manager</string>
<string id="EnableWindowsPackageManagerSmartScreenCheckExplanation"></string>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding @RDMacLachlan to help with the strings for the new group policies in the ADML

<comment>{Locked="{0}"} Error message displayed when the provided uri is not well formed. {0} is a placeholder replaced by the provided uri.</comment>
</data>
<data name="UriBlockedBySmartScreen" xml:space="preserve">
<value>This operation was blocked as unsafe by Microsoft Defender SmartScreen.</value>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding @RDMacLachlan to help with the displayed messages (I copied the messages from App Installer with some modifications)

Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Deployment.OutOfProc", "Microsoft.Management.Deployment.OutOfProc\Microsoft.Management.Deployment.OutOfProc.vcxproj", "{0BA531C8-CF0C-405B-8221-0FE51BA529D1}"
ProjectSection(ProjectDependencies) = postProject
{2B00D362-AC92-41F3-A8D2-5B1599BDCA01} = {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Copy link
Member

Choose a reason for hiding this comment

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

We're building for ARM now?

class UriValidationResult
{
public:
UriValidationResult(UriValidationDecision decision) : m_decision(decision), m_feedback(std::string()) {}
Copy link
Member

Choose a reason for hiding this comment

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

I think you don't need to pass the empty string for m_feedback because that's what the default constructor does

Suggested change
UriValidationResult(UriValidationDecision decision) : m_decision(decision), m_feedback(std::string()) {}
UriValidationResult(UriValidationDecision decision) : m_decision(decision), m_feedback() {}

Or you could initialize it to empty in the declaration and not mention it here

{
public:
UriValidationResult(UriValidationDecision decision) : m_decision(decision), m_feedback(std::string()) {}
UriValidationResult(UriValidationDecision decision, std::string feedback) : m_decision(decision), m_feedback(feedback) {}
Copy link
Member

Choose a reason for hiding this comment

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

feedback should probably be a std::string_view or a std::string&& to avoid a copy. Not that it would make much difference in this case

Comment on lines 1304 to 1313
auto progressScope = context.Reporter.BeginAsyncProgress(true);
progressScope->Callback().SetProgressMessage(Resource::String::ConfigurationInitializing());

anon::ConfigureProcessorForUse(context, ConfigurationProcessor{ anon::CreateConfigurationSetProcessorFactory(context) });
}

void CreateConfigurationProcessor(Context& context)
{
context <<
ExecuteUriValidation(UriValidationSource::ConfigurationSource) <<
Copy link
Member

Choose a reason for hiding this comment

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

Could ExecuteUriValidation happen after we set up the progress reporting and message? Just in case it takes a while

<comment>Describes a Group Policy that can enable the use of the --proxy option to set a proxy</comment>
</data>
<data name="PolicyEnableSmartScreenValidation" xml:space="preserve">
<value>Enable Windows Package Manager smart screen validation</value>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<value>Enable Windows Package Manager smart screen validation</value>
<value>Enable Windows Package Manager SmartScreen validation</value>

<comment></comment>
</data>
<data name="PolicyEnableAllowedSecurityZones" xml:space="preserve">
<value>Enable Windows App Installer Allowed Security Zones</value>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<value>Enable Windows App Installer Allowed Security Zones</value>
<value>Enable Windows Package Manager Allowed Security Zones</value>

<comment>Error message displayed when an operation is using a URI that was blocked by Microsoft Defender SmartScreen.</comment>
</data>
<data name="UriSecurityZoneBlockedByPolicy" xml:space="preserve">
<value>The operation you are attempting to apply has been blocked by your administrator.</value>
Copy link
Member

Choose a reason for hiding this comment

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

This message could probably be more specific to zones. I think we already have a generic "not allowed by policy" string

Comment on lines +16 to +17
#define REQUIRE_OUTPUT_HAS_LOC(_output_, _resource_) \
REQUIRE(_output_.str().find(Resource::LocString(_resource_).get()) != std::string::npos);
Copy link
Member

Choose a reason for hiding this comment

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

I like this, we should use it everywhere we do this check

@florelis
Copy link
Member

florelis commented Nov 6, 2024

I think we should add a command line flag to bypass SmartScreen if it fails, and an admin setting to disable the SmartScreen checks without needing to use group policy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants