diff --git a/documentation/specs/BuildCheck/Codes.md b/documentation/specs/BuildCheck/Codes.md
index 9845c431061..9b355f9c89f 100644
--- a/documentation/specs/BuildCheck/Codes.md
+++ b/documentation/specs/BuildCheck/Codes.md
@@ -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:
@@ -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.
+
+## 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.
+
diff --git a/src/Build/BuildCheck/Checks/UntrustedLocationCheck.cs b/src/Build/BuildCheck/Checks/UntrustedLocationCheck.cs
new file mode 100644
index 00000000000..5dcf2c75ea9
--- /dev/null
+++ b/src/Build/BuildCheck/Checks/UntrustedLocationCheck.cs
@@ -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 SupportedRules { get; } = new List() { 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 checkedProjects = new HashSet();
+
+ private void EvaluatedPropertiesAction(BuildCheckDataContext 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();
+
+ ///
+ /// Returns the current Downloads location. Makes sure the path doesn't end with directory separator
+ /// (to prevent false negatives during matching)
+ ///
+ 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);
+ }
+}
diff --git a/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs b/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs
index cc9ee125ac1..c7d6e8d4a9b 100644
--- a/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs
+++ b/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs
@@ -152,6 +152,7 @@ internal readonly record struct BuiltInCheckFactory(
new BuiltInCheckFactory([NoEnvironmentVariablePropertyCheck.SupportedRule.Id], NoEnvironmentVariablePropertyCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct),
new BuiltInCheckFactory([EmbeddedResourceCheck.SupportedRule.Id], EmbeddedResourceCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct),
new BuiltInCheckFactory([TargetFrameworkConfusionCheck.SupportedRule.Id], TargetFrameworkConfusionCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct),
+ new BuiltInCheckFactory([UntrustedLocationCheck.SupportedRule.Id], UntrustedLocationCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct),
],
// BuildCheckDataSource.Execution
diff --git a/src/Build/Resources/Strings.resx b/src/Build/Resources/Strings.resx
index afa2052daca..7d0944aaae4 100644
--- a/src/Build/Resources/Strings.resx
+++ b/src/Build/Resources/Strings.resx
@@ -2216,6 +2216,12 @@ Utilization: {0} Average Utilization: {1:###.0}
Property: '{0}' was declared/initialized, but it was never used.
+
+ Downloads folder is untrusted for projects building.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
An exception occurred while expanding a fileSpec with globs: fileSpec: "{0}", assuming it is a file name. Exception: {1}
diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf
index de10f0f34a9..5a0a67d41e6 100644
--- a/src/Build/Resources/xlf/Strings.cs.xlf
+++ b/src/Build/Resources/xlf/Strings.cs.xlf
@@ -241,6 +241,16 @@
Vlastnost, která se nepoužívá, by se neměla deklarovat.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ Vytvoření otázky SELHALO. Vytváření bylo předčasně ukončeno, protože se při něm narazilo na cíl nebo úlohu, které nebyly aktuální.
diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf
index 7ae4ff64846..eeb010d4816 100644
--- a/src/Build/Resources/xlf/Strings.de.xlf
+++ b/src/Build/Resources/xlf/Strings.de.xlf
@@ -241,6 +241,16 @@
Eine Eigenschaft, die nicht verwendet wird, sollte nicht deklariert werden.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ Fehler beim Erstellen der Frage. Der Build wurde früh beendet, da ein Ziel oder eine Aufgabe gefunden wurde, die nicht aktuell war.
diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf
index f94a047df5b..1d07258d83a 100644
--- a/src/Build/Resources/xlf/Strings.es.xlf
+++ b/src/Build/Resources/xlf/Strings.es.xlf
@@ -241,6 +241,16 @@
No se debe declarar una propiedad que no se use.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ La creación de la pregunta ha FALLADO. La creación finalizó antes de tiempo al encontrar un objetivo o tarea que no estaba actualizado.
diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf
index 56c560c71f7..49915d05bee 100644
--- a/src/Build/Resources/xlf/Strings.fr.xlf
+++ b/src/Build/Resources/xlf/Strings.fr.xlf
@@ -241,6 +241,16 @@
Une propriété qui n'est pas utilisée ne doit pas être déclarée.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ ÉCHEC de la génération de la question. La génération s’est arrêtée tôt, car elle a rencontré une cible ou une tâche qui n’était pas à jour.
diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf
index c70a79dbe40..eac7e974ac2 100644
--- a/src/Build/Resources/xlf/Strings.it.xlf
+++ b/src/Build/Resources/xlf/Strings.it.xlf
@@ -241,6 +241,16 @@
Una proprietà non utilizzata non deve essere dichiarata.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ Compilazione della domanda NON RIUSCITA. La compilazione è terminata in anticipo perché è stata rilevata una destinazione o un'attività non aggiornata.
diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf
index 0981d946bbb..5a4afa752f5 100644
--- a/src/Build/Resources/xlf/Strings.ja.xlf
+++ b/src/Build/Resources/xlf/Strings.ja.xlf
@@ -241,6 +241,16 @@
使用されていないプロパティは宣言しないでください。
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ 質問のビルドに失敗しました。ビルドは、最新ではないターゲットまたはタスクが検出されたため、早期に終了しました。
diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf
index bc6bd7f0df2..11df5f300ff 100644
--- a/src/Build/Resources/xlf/Strings.ko.xlf
+++ b/src/Build/Resources/xlf/Strings.ko.xlf
@@ -241,6 +241,16 @@
사용되지 않는 속성은 선언하면 안 됩니다.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ 질문 빌드에 실패했습니다. 빌드가 최신이 아닌 대상 또는 작업을 발견하여 일찍 종료되었습니다.
diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf
index 197d5dd80b1..111b8435c22 100644
--- a/src/Build/Resources/xlf/Strings.pl.xlf
+++ b/src/Build/Resources/xlf/Strings.pl.xlf
@@ -241,6 +241,16 @@
Nie należy deklarować właściwości, która nie jest używana.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ NIEPOWODZENIE kompilacji pytania. Kompilacja została zakończona wcześniej, ponieważ napotkała element docelowy lub zadanie, które nie było aktualne.
diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf
index 2a5e78cb408..9ca12faebcc 100644
--- a/src/Build/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf
@@ -241,6 +241,16 @@
Uma propriedade que não é usada não deve ser declarada.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ FALHA na compilação da pergunta. A compilação foi encerrada antecipadamente ao se deparar com um alvo ou tarefa que não estava atualizado.
diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf
index 82bb6700ee8..9e429bb5b88 100644
--- a/src/Build/Resources/xlf/Strings.ru.xlf
+++ b/src/Build/Resources/xlf/Strings.ru.xlf
@@ -241,6 +241,16 @@
Не следует объявлять свойство, которое не используется.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ СБОЙ сборки вопроса. Выход из сборки выполнен раньше, так как была обнаружена цель или задача без обновления.
diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf
index a7d019b7276..eb3c8477986 100644
--- a/src/Build/Resources/xlf/Strings.tr.xlf
+++ b/src/Build/Resources/xlf/Strings.tr.xlf
@@ -241,6 +241,16 @@
Kullanılmamış bir özellik bildirilmemelidir.
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ Soru derleme BAŞARISIZ oldu. Güncel olmayan bir hedef veya görev ile karşılaştığından derleme işleminden erken çıkıldı.
diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf
index 66127cb659d..cc466fc0b85 100644
--- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf
@@ -241,6 +241,16 @@
不应声明未使用的属性。
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ 问题生成失败。生成提前退出,因为遇到不是最新的目标或任务。
diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf
index c10b95fe378..55a3af4f87f 100644
--- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf
@@ -241,6 +241,16 @@
不應宣告未使用的屬性。
+
+
+ Location: '{0}' cannot be fully trusted, place your projects outside of that folder (Project: {1}).
+
+
+
+
+ Downloads folder is untrusted for projects building.
+
+ 問題建立失敗。建置提早結束,因為它遇到不是最新的目標或工作。