File: BuildCheck\Checks\UntrustedLocationCheck.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// 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);
    }
}