using System.Security.Cryptography; using FinalFactory.Rendezvous.Server.Provisioning; namespace FinalFactory.Rendezvous.Tests.Provisioning; public sealed class ProductionSecretProviderTests : IDisposable { private readonly List _paths = []; [Fact] public void ReadsBoundedSecretFromAbsoluteReadOnlyFile() { byte[] expected = RandomNumberGenerator.GetBytes(32); string path = CreateSecretFile(expected); EnvironmentSecretProvider provider = new(); bool found = provider.TryGetSecret($"file:{path}", out SecretMaterial? material); Assert.True(found); using (material) { Assert.Equal(expected, material!.CopyBytes()); } } [Fact] public void RejectsRelativeSymlinkEmptyAndOversizedFileReferences() { string empty = CreateSecretFile([]); string oversized = CreateSecretFile(new byte[4097]); string target = CreateSecretFile(RandomNumberGenerator.GetBytes(32)); string symlink = Path.Combine(Path.GetTempPath(), $"rendezvous-secret-link-{Guid.NewGuid():N}"); EnvironmentSecretProvider provider = new(); Assert.False(provider.TryGetSecret("file:relative-secret", out _)); Assert.False(provider.TryGetSecret($"file:{empty}", out _)); Assert.False(provider.TryGetSecret($"file:{oversized}", out _)); Assert.False(provider.TryGetSecret("file:/tmp/invalid\0path", out _)); if (!OperatingSystem.IsWindows()) { File.CreateSymbolicLink(symlink, target); _paths.Add(symlink); Assert.False(provider.TryGetSecret($"file:{symlink}", out _)); } } public void Dispose() { foreach (string path in _paths) { File.Delete(path); } } private string CreateSecretFile(byte[] bytes) { string path = Path.Combine(Path.GetTempPath(), $"rendezvous-secret-{Guid.NewGuid():N}"); File.WriteAllBytes(path, bytes); if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD()) { File.SetUnixFileMode(path, UnixFileMode.UserRead); } _paths.Add(path); return path; } }