Skip to content

Commit

Permalink
Add UntrustedLocation check (#11286)
Browse files Browse the repository at this point in the history
* Add UntrustedLocation check

* Improve the Downloads inference

* Add documentation source
  • Loading branch information
JanKrivanek authored Jan 20, 2025
1 parent 424d660 commit 02aba49
Show file tree
Hide file tree
Showing 17 changed files with 244 additions and 0 deletions.
10 changes: 10 additions & 0 deletions documentation/specs/BuildCheck/Codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Report codes are chosen to conform to suggested guidelines. Those guidelines are
| [BC0201](#bc0201---usage-of-undefined-property) | Warning | Project | 9.0.100 | Usage of undefined property. |
| [BC0202](#bc0202---property-first-declared-after-it-was-used) | Warning | Project | 9.0.100 | Property first declared after it was used. |
| [BC0203](#bc0203----property-declared-but-never-used) | None | Project | 9.0.100 | Property declared but never used. |
| [BC0301](#bc0301---building-from-downloads-folder) | None | Project | 9.0.300 | Building from Downloads folder. |


Notes:
Expand Down Expand Up @@ -176,6 +177,15 @@ Common cases of false positives:
* Property accessing is tracked for each project build request. There might be multiple distinct build requests for a project in a single build. Specific case of this is a call to the [MSBuild task](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task) or [CallTarget task](https://learn.microsoft.com/en-us/visualstudio/msbuild/calltarget-task) that can request a result from a project build, while passing additional or different global properties and/or calling specific target. This happens often as part of common targets - e.g. for [multi-targeted project build parallelization](../../High-level-overview.md#parallelism)
* Incremental build might skip execution of some targets, that might have been accessing properties of interest.

<a name="BC0301"></a>
## BC0301 - Building from Downloads folder.

"Downloads folder is untrusted for projects building."

Placing project files into Downloads folder (or any other folder that cannot be fully trusted including all parent folders up to a root drive) is not recomended, as unintended injection of unrelated MSBuild logic can occur.

Place your projects into trusted locations - including cases when you intend to only open the project in IDE.

<BR/>
<BR/>
<BR/>
Expand Down
97 changes: 97 additions & 0 deletions src/Build/BuildCheck/Checks/UntrustedLocationCheck.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Build.Construction;
using Microsoft.Build.Shared;

namespace Microsoft.Build.Experimental.BuildCheck.Checks;
internal sealed class UntrustedLocationCheck : Check
{
public static CheckRule SupportedRule = new CheckRule(
"BC0301",
"UntrustedLocation",
ResourceUtilities.GetResourceString("BuildCheck_BC0301_Title")!,
ResourceUtilities.GetResourceString("BuildCheck_BC0301_MessageFmt")!,
new CheckConfiguration() { Severity = CheckResultSeverity.Error });

public override string FriendlyName => "DotUtils.UntrustedLocationCheck";

public override IReadOnlyList<CheckRule> SupportedRules { get; } = new List<CheckRule>() { SupportedRule };

public override void Initialize(ConfigurationContext configurationContext)
{
checkedProjects.Clear();
}

internal override bool IsBuiltIn => true;

public override void RegisterActions(IBuildCheckRegistrationContext registrationContext)
{
registrationContext.RegisterEvaluatedPropertiesAction(EvaluatedPropertiesAction);
}

private HashSet<string> checkedProjects = new HashSet<string>();

private void EvaluatedPropertiesAction(BuildCheckDataContext<EvaluatedPropertiesCheckData> context)
{
if (checkedProjects.Add(context.Data.ProjectFilePath) &&
context.Data.ProjectFileDirectory.StartsWith(PathsHelper.Downloads, Shared.FileUtilities.PathComparison))
{
context.ReportResult(BuildCheckResult.Create(
SupportedRule,
ElementLocation.EmptyLocation,
context.Data.ProjectFileDirectory,
context.Data.ProjectFilePath.Substring(context.Data.ProjectFileDirectory.Length + 1)));
}
}

private static class PathsHelper
{
public static readonly string Downloads = GetDownloadsPath();

/// <summary>
/// Returns the current Downloads location. Makes sure the path doesn't end with directory separator
/// (to prevent false negatives during matching)
/// </summary>
private static string GetDownloadsPath()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Unsupported on pre-vista
if (Environment.OSVersion.Version.Major >= 6)
{
try
{
// based on doc (https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath)
// - a final slash is not added
return SHGetKnownFolderPath(new Guid("374DE290-123F-4565-9164-39C4925E467B"), 0, IntPtr.Zero);
}
catch
{
// ignored
}
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
string? locationFromEnv = Environment.GetEnvironmentVariable("XDG_DOWNLOAD_DIR");
if (locationFromEnv != null && Directory.Exists(locationFromEnv))
{
return locationFromEnv.TrimEnd(['\\','/']);
}
}

return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads");
}

[DllImport("shell32",
CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)]
private static extern string SHGetKnownFolderPath(
[MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags,
IntPtr hToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ internal readonly record struct BuiltInCheckFactory(
new BuiltInCheckFactory([NoEnvironmentVariablePropertyCheck.SupportedRule.Id], NoEnvironmentVariablePropertyCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct<NoEnvironmentVariablePropertyCheck>),
new BuiltInCheckFactory([EmbeddedResourceCheck.SupportedRule.Id], EmbeddedResourceCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct<EmbeddedResourceCheck>),
new BuiltInCheckFactory([TargetFrameworkConfusionCheck.SupportedRule.Id], TargetFrameworkConfusionCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct<TargetFrameworkConfusionCheck>),
new BuiltInCheckFactory([UntrustedLocationCheck.SupportedRule.Id], UntrustedLocationCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct<UntrustedLocationCheck>),
],

// BuildCheckDataSource.Execution
Expand Down
6 changes: 6 additions & 0 deletions src/Build/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,12 @@ Utilization: {0} Average Utilization: {1:###.0}</value>
<data name="BuildCheck_BC0203_MessageFmt" xml:space="preserve">
<value>Property: '{0}' was declared/initialized, but it was never used.</value>
</data>
<data name="BuildCheck_BC0301_Title" xml:space="preserve">
<value>Downloads folder is untrusted for projects building.</value>
</data>
<data name="BuildCheck_BC0301_MessageFmt" xml:space="preserve">
<value>Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).</value>
</data>
<data name="GlobExpansionFailed" xml:space="preserve">
<value>An exception occurred while expanding a fileSpec with globs: fileSpec: "{0}", assuming it is a file name. Exception: {1}</value>
</data>
Expand Down
10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Build/Resources/xlf/Strings.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 02aba49

Please sign in to comment.