using System.IO; using Xunit; namespace Jellyfin.Drawing.Skia.Tests; public static class SvgSecurityValidatorTests { public static TheoryData ExternalReferenceSvgs => new() { // SSRF via (xlink:href and plain href) "", "", // Local file disclosure "", // Memory exhaustion DoS "", // external reference "", // CSS url() external reference in an attribute "", // @import in a style block "", // Relative path traversal (resolves against the document location -> local file read) "", // XXE via external entity "]>&xxe;", // Entity-expansion (billion laughs) denial of service "]>&f;", // Nested SVG in a base64 data: URI whose inner document references an external resource "", // Nested SVG in a URL-encoded (non-base64) data: URI referencing an external resource "", // Nested gzip-compressed (svgz) data: URI whose inner document references an external resource "", }; public static TheoryData SafeSvgs => new() { "", // Same-document fragment references are allowed "", // Inline data URIs are allowed "", // A DOCTYPE without external entities is allowed "", // An internal general entity with no external reference is allowed (and is expanded by the renderer) "]>", // A nested data:image/svg+xml payload that is itself self-contained is allowed "", // A self-contained gzip-compressed (svgz) data: URI is allowed "", }; [Theory] [MemberData(nameof(ExternalReferenceSvgs))] public static void IsSafe_ExternalReference_ReturnsFalse(string svg) { var path = WriteTemp(svg); try { Assert.False(SvgSecurityValidator.IsSafe(path, out var reason)); Assert.NotNull(reason); } finally { File.Delete(path); } } [Theory] [MemberData(nameof(SafeSvgs))] public static void IsSafe_NoExternalReference_ReturnsTrue(string svg) { var path = WriteTemp(svg); try { Assert.True(SvgSecurityValidator.IsSafe(path, out var reason)); Assert.Null(reason); } finally { File.Delete(path); } } [Fact] public static void IsSafe_MissingFile_ReturnsFalse() { Assert.False(SvgSecurityValidator.IsSafe(Path.Combine(Path.GetTempPath(), "does-not-exist-" + Path.GetRandomFileName() + ".svg"), out var reason)); Assert.NotNull(reason); } private static string WriteTemp(string svg) { var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".svg"); File.WriteAllText(path, svg); return path; } }