diff --git a/Robust.Shared.Maths/Properties/AssemblyInfo.cs b/Robust.Shared.Maths/Properties/AssemblyInfo.cs index 1609dbc4e..fe5bc30bf 100644 --- a/Robust.Shared.Maths/Properties/AssemblyInfo.cs +++ b/Robust.Shared.Maths/Properties/AssemblyInfo.cs @@ -6,4 +6,3 @@ [assembly: InternalsVisibleTo("Robust.Client")] [assembly: InternalsVisibleTo("Robust.UnitTesting")] -[assembly: InternalsVisibleTo("Content.Benchmarks")] diff --git a/Robust.Shared/ContentPack/AssemblyTypeChecker.Config.cs b/Robust.Shared/ContentPack/AssemblyTypeChecker.Config.cs index 1be49194d..560b71e75 100644 --- a/Robust.Shared/ContentPack/AssemblyTypeChecker.Config.cs +++ b/Robust.Shared/ContentPack/AssemblyTypeChecker.Config.cs @@ -88,6 +88,7 @@ namespace Robust.Shared.ContentPack public string SystemAssemblyName = default!; public HashSet AllowedVerifierErrors = default!; public List WhitelistedNamespaces = default!; + public List AllowedAssemblyPrefixes = default!; public Dictionary> Types = default!; } diff --git a/Robust.Shared/ContentPack/AssemblyTypeChecker.cs b/Robust.Shared/ContentPack/AssemblyTypeChecker.cs index e96d2c024..cbd7a9309 100644 --- a/Robust.Shared/ContentPack/AssemblyTypeChecker.cs +++ b/Robust.Shared/ContentPack/AssemblyTypeChecker.cs @@ -131,6 +131,16 @@ namespace Robust.Shared.ContentPack return false; } +#pragma warning disable RA0004 + var loadedConfig = _config.Result; +#pragma warning restore RA0004 + + if (!loadedConfig.AllowedAssemblyPrefixes.Any(allowedNamePrefix => asmName.StartsWith(allowedNamePrefix))) + { + _sawmill.Error($"Assembly name '{asmName}' is not allowed for a content assembly"); + return false; + } + if (VerifyIL) { if (!DoVerifyIL(asmName, resolver, peReader, reader)) @@ -179,10 +189,6 @@ namespace Robust.Shared.ContentPack return true; } -#pragma warning disable RA0004 - var loadedConfig = _config.Result; -#pragma warning restore RA0004 - var badRefs = new ConcurrentBag(); // We still do explicit type reference scanning, even though the actual whitelists work with raw members. diff --git a/Robust.Shared/ContentPack/ModLoader.cs b/Robust.Shared/ContentPack/ModLoader.cs index 57a91d2ef..931c64290 100644 --- a/Robust.Shared/ContentPack/ModLoader.cs +++ b/Robust.Shared/ContentPack/ModLoader.cs @@ -93,19 +93,23 @@ namespace Robust.Shared.ContentPack { var sw = Stopwatch.StartNew(); Sawmill.Debug("LOADING modules"); - var files = new Dictionary(); + var files = new Dictionary(); // Find all modules we want to load. foreach (var fullPath in paths) { using var asmFile = _res.ContentFileRead(fullPath); - var refData = GetAssemblyReferenceData(asmFile); + var ms = new MemoryStream(); + asmFile.CopyTo(ms); + + ms.Position = 0; + var refData = GetAssemblyReferenceData(ms); if (refData == null) continue; var (asmRefs, asmName) = refData.Value; - if (!files.TryAdd(asmName, (fullPath, asmRefs))) + if (!files.TryAdd(asmName, (fullPath, ms, asmRefs))) { Sawmill.Error("Found multiple modules with the same assembly name " + $"'{asmName}', A: {files[asmName].Path}, B: {fullPath}."); @@ -122,10 +126,10 @@ namespace Robust.Shared.ContentPack Parallel.ForEach(files, pair => { - var (name, (path, _)) = pair; + var (name, (_, data, _)) = pair; - using var stream = _res.ContentFileRead(path); - if (!typeChecker.CheckAssembly(stream, resolver)) + data.Position = 0; + if (!typeChecker.CheckAssembly(data, resolver)) { throw new TypeCheckFailedException($"Assembly {name} failed type checks."); } @@ -137,14 +141,15 @@ namespace Robust.Shared.ContentPack var nodes = TopologicalSort.FromBeforeAfter( files, kv => kv.Key, - kv => kv.Value.Path, + kv => kv.Value, _ => Array.Empty(), kv => kv.Value.references, allowMissing: true); // missing refs would be non-content assemblies so allow that. // Actually load them in the order they depend on each other. - foreach (var path in TopologicalSort.Sort(nodes)) + foreach (var item in TopologicalSort.Sort(nodes)) { + var (path, memory, _) = item; Sawmill.Debug($"Loading module: '{path}'"); try { @@ -156,9 +161,9 @@ namespace Robust.Shared.ContentPack } else { - using var assemblyStream = _res.ContentFileRead(path); + memory.Position = 0; using var symbolsStream = _res.ContentFileReadOrNull(path.WithExtension("pdb")); - LoadGameAssembly(assemblyStream, symbolsStream, skipVerify: true); + LoadGameAssembly(memory, symbolsStream, skipVerify: true); } } catch (Exception e) @@ -174,7 +179,7 @@ namespace Robust.Shared.ContentPack private (string[] refs, string name)? GetAssemblyReferenceData(Stream stream) { - using var reader = ModLoader.MakePEReader(stream); + using var reader = ModLoader.MakePEReader(stream, leaveOpen: true); var metaReader = reader.GetMetadataReader(); var name = metaReader.GetString(metaReader.GetAssemblyDefinition().Name); diff --git a/Robust.Shared/ContentPack/Sandbox.yml b/Robust.Shared/ContentPack/Sandbox.yml index 63b10a3f6..7963e2c59 100644 --- a/Robust.Shared/ContentPack/Sandbox.yml +++ b/Robust.Shared/ContentPack/Sandbox.yml @@ -17,6 +17,10 @@ WhitelistedNamespaces: - Content - OpenDreamShared +AllowedAssemblyPrefixes: +- OpenDream +- Content + # The type whitelist does NOT care about which assembly types come from. # This is because types switch assembly all the time. # Just look up stuff like StreamReader on https://apisof.net. diff --git a/Robust.Shared/Properties/AssemblyInfo.cs b/Robust.Shared/Properties/AssemblyInfo.cs index ca50911fc..86e9a1fad 100644 --- a/Robust.Shared/Properties/AssemblyInfo.cs +++ b/Robust.Shared/Properties/AssemblyInfo.cs @@ -9,7 +9,6 @@ [assembly: InternalsVisibleTo("Robust.UnitTesting")] [assembly: InternalsVisibleTo("OpenToolkit.GraphicsLibraryFramework")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Gives access to Castle(Moq) -[assembly: InternalsVisibleTo("Content.Benchmarks")] [assembly: InternalsVisibleTo("Robust.Benchmarks")] [assembly: InternalsVisibleTo("Robust.Client.WebView")] [assembly: InternalsVisibleTo("Robust.Packaging")]