diff --git a/Documentation/docs-mobile/binding-libs/msbuild-reference/build-items.md b/Documentation/docs-mobile/binding-libs/msbuild-reference/build-items.md index 12c9c97eef5..14ea33bfb9c 100644 --- a/Documentation/docs-mobile/binding-libs/msbuild-reference/build-items.md +++ b/Documentation/docs-mobile/binding-libs/msbuild-reference/build-items.md @@ -173,6 +173,7 @@ hosted in Maven. | - | - | | Version | Required string. The version of the Java library that should be downloaded from Maven. Defaults to `true`. | | Repository | Optional string. Specifies Maven repository to use. Supported values are `Central`, `Google`, or an `https` URL to a Maven repository. Defaults to `Central`. | +| AllowInsecureHttp | Optional boolean. When `Repository` is an `http://` URL, this must be set to `true` to allow the insecure connection. Defaults to `false`. Using HTTPS is strongly recommended for supply-chain security. | | Bind | Optional boolean. Specifies whether the Java library should have a C# binding generated for it. Defaults to `true`. | | Pack | Optional boolean. Specifies whether the Java library should be included in the project output. Defaults to `true`. | diff --git a/Documentation/docs-mobile/building-apps/build-items.md b/Documentation/docs-mobile/building-apps/build-items.md index a978ac0e318..46bd157c07b 100644 --- a/Documentation/docs-mobile/building-apps/build-items.md +++ b/Documentation/docs-mobile/building-apps/build-items.md @@ -296,6 +296,9 @@ The following MSBuild metadata are supported: - `%(Version)`: Required version of the Java library referenced by `%(Include)`. - `%(Repository)`: Optional Maven repository to use. Supported values are `Central` (default), `Google`, or an `https` URL to a Maven repository. +- `%(AllowInsecureHttp)`: Optional boolean. When `%(Repository)` is an `http://` URL, this must be + set to `true` to allow the insecure connection. Defaults to `false`. Using HTTPS is strongly + recommended for supply-chain security. The `` item is translated to [`AndroidLibrary`](#androidlibrary), so any metadata supported by diff --git a/Documentation/docs-mobile/messages/index.md b/Documentation/docs-mobile/messages/index.md index 14d5a6c0442..cef18eb294e 100644 --- a/Documentation/docs-mobile/messages/index.md +++ b/Documentation/docs-mobile/messages/index.md @@ -208,6 +208,7 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla + [XA4248](xa4248.md): Could not find NuGet package '{nugetId}' version '{version}' in lock file. Ensure NuGet Restore has run since this `` was added. + [XA4235](xa4249.md): Maven artifact specification '{artifact}' is invalid. The correct format is 'group_id:artifact_id:version'. + [XA4250](xa4250.md): Manifest-referenced type '{type}' was not found in any scanned assembly. It may be a framework type. ++ [XA4252](xa4252.md): Insecure HTTP Maven repository URL '{url}' is not allowed. Use an HTTPS URL, or set AllowInsecureHttp="true" metadata on the item to override this check. + XA4300: Native library '{library}' will not be bundled because it has an unsupported ABI. + [XA4301](xa4301.md): Apk already contains the item `xxx`. + [XA4302](xa4302.md): Unhandled exception merging \`AndroidManifest.xml\`: {ex} diff --git a/Documentation/docs-mobile/messages/xa4252.md b/Documentation/docs-mobile/messages/xa4252.md new file mode 100644 index 00000000000..72effe92fac --- /dev/null +++ b/Documentation/docs-mobile/messages/xa4252.md @@ -0,0 +1,43 @@ +--- +title: .NET for Android error XA4252 +description: XA4252 error code +ms.date: 05/15/2026 +f1_keywords: + - "XA4252" +--- + +# .NET for Android error XA4252 + +## Example message + +``` +error XA4252: Insecure HTTP Maven repository URL 'http://repo.example.com/maven2/' is not allowed. Use an HTTPS URL, or set AllowInsecureHttp="true" metadata on the item to override this check. +``` + +## Issue + +An `` item specifies a Maven repository URL using `http://` instead of `https://`. +Downloading artifacts over plain HTTP is a security risk because the connection is not encrypted and is +vulnerable to man-in-the-middle attacks and supply-chain compromise. + +This check aligns with default behavior in Gradle (`allowInsecureProtocol`) and Maven (`http://*`) +for defense in depth and supply-chain hardening. + +## Solution + +Use an HTTPS URL for the Maven repository whenever possible: + +```xml + + + +``` + +If the repository does not support HTTPS and you understand the security implications, +set `AllowInsecureHttp="true"` to explicitly opt in to insecure HTTP: + +```xml + + + +``` diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs index 890e2142dd1..8110bb93cf0 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs @@ -1462,6 +1462,15 @@ public static string XA4250 { return ResourceManager.GetString("XA4250", resourceCulture); } } + + /// + /// Looks up a localized string similar to Insecure HTTP Maven repository URL '{0}' is not allowed. Use an HTTPS URL, or set AllowInsecureHttp="true" metadata on the item to override this check.. + /// + public static string XA4252 { + get { + return ResourceManager.GetString("XA4252", resourceCulture); + } + } /// /// Looks up a localized string similar to Type '{0}' uses [JniAddNativeMethodRegistrationAttribute], which is not supported by the trimmable type map. To work around this, do not target the trimmable type map (for example, by switching to the 'llvm-ir' type map implementation), and please report this scenario at https://github.com/dotnet/android/issues so the team can evaluate whether to support it.. diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx index ae4698f69eb..376384209d9 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx @@ -1084,6 +1084,11 @@ To use a custom JDK path for a command line build, set the 'JavaSdkDirectory' MS Type '{0}' uses [JniAddNativeMethodRegistrationAttribute], which is not supported by the trimmable type map. To work around this, do not target the trimmable type map (for example, by switching to the 'llvm-ir' type map implementation), and please report this scenario at https://github.com/dotnet/android/issues so the team can evaluate whether to support it. The following are literal names and should not be translated: JniAddNativeMethodRegistrationAttribute, llvm-ir. {0} - Fully-qualified managed type name + + + Insecure HTTP Maven repository URL '{0}' is not allowed. Use an HTTPS URL, or set AllowInsecureHttp="true" metadata on the item to override this check. + The following are literal names and should not be translated: HTTP, HTTPS, Maven, AllowInsecureHttp +{0} - The insecure HTTP URL Command '{0}' failed.\n{1} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs b/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs index 0521682daf6..96eced16660 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/MavenDownload.cs @@ -146,7 +146,14 @@ public async override System.Threading.Tasks.Task RunTaskAsync () _ => null }; - if (repo is null && type.StartsWith ("http", StringComparison.OrdinalIgnoreCase)) { + if (repo is null && Uri.TryCreate (type, UriKind.Absolute, out var uri) && + (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) { + if (uri.Scheme == Uri.UriSchemeHttp && + !string.Equals (item.GetMetadataOrDefault ("AllowInsecureHttp", "false"), "true", StringComparison.OrdinalIgnoreCase)) { + Log.LogCodedError ("XA4252", Properties.Resources.XA4252, type); + return null; + } + using var hasher = SHA256.Create (); var hash = hasher.ComputeHash (Encoding.UTF8.GetBytes (type)); var cache_name = Convert.ToBase64String (hash); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs index 55d81bb69c3..5f17e684cd6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/MavenDownloadTests.cs @@ -72,6 +72,41 @@ public async Task UnknownRepository () Assert.AreEqual ("Unknown Maven repository: 'bad-repo'.", engine.Errors [0].Message); } + [Test] + public async Task InsecureHttpRepository_Blocked () + { + var engine = new MockBuildEngine (TestContext.Out, new List ()); + var task = new MavenDownload { + BuildEngine = engine, + AndroidMavenLibraries = [CreateMavenTaskItem ("com.google.android.material:material", "1.0.0", "http://repo.example.com/maven2/")], + }; + + await task.RunTaskAsync (); + + Assert.AreEqual (1, engine.Errors.Count); + Assert.AreEqual ("Insecure HTTP Maven repository URL 'http://repo.example.com/maven2/' is not allowed. Use an HTTPS URL, or set AllowInsecureHttp=\"true\" metadata on the item to override this check.", engine.Errors [0].Message); + } + + [Test] + public async Task InsecureHttpRepository_AllowedWithOptIn () + { + var engine = new MockBuildEngine (TestContext.Out, new List ()); + var item = CreateMavenTaskItem ("com.example:dummy", "1.0.0", "http://repo.example.com/maven2/"); + item.SetMetadata ("AllowInsecureHttp", "true"); + + var task = new MavenDownload { + BuildEngine = engine, + MavenCacheDirectory = Path.GetTempPath (), + AndroidMavenLibraries = [item], + }; + + await task.RunTaskAsync (); + + // Should bypass the XA4252 insecure HTTP check and attempt the download, which fails with XA4236 + Assert.AreEqual (1, engine.Errors.Count); + Assert.AreEqual ("XA4236", engine.Errors [0].Code, "Expected a download error (XA4236), not a security error (XA4252)"); + } + [Test] public async Task UnknownArtifact () {