Skip to content

Commit

Permalink
Tag type names in payloads are no longer respected by default.
Browse files Browse the repository at this point in the history
- `SerializerSettings.UnsafeAllowDeserializeFromTagTypeName` is added.
- `SerializerSettings.EmitTags` is set to false by default. (for roundtrip)
- Calling `SerializerSettings.RegisterAssembly` or `SerializerSettings.RegisterTagMapping`, sets `EmitTags` to true.
  • Loading branch information
udaken committed Jul 2, 2022
1 parent f3b1c7b commit 406bd21
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 24 deletions.
38 changes: 29 additions & 9 deletions src/SharpYaml.Tests/Serialization/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,24 @@ public void DeserializeScalar()
}

[Test]
public void DeserializeExplicitType()
public void DeserializeUnsafeExplicitType()
{
var settings = new SerializerSettings();
settings.RegisterAssembly(typeof(SerializationTests).Assembly);

var serializer = new Serializer();
var settings = new SerializerSettings(){ UnsafeAllowDeserializeFromTagTypeName = true };
var serializer = new Serializer(settings);
object result = serializer.Deserialize(YamlFile("explicitType.yaml"), typeof(object));

Assert.True(typeof(Z).IsAssignableFrom(result.GetType()));
Assert.AreEqual("bbb", ((Z)result).aaa);
}

[Test]
public void DeserializeUnregisterdExplicitType()
{
var serializer = new Serializer();

Assert.Throws<YamlException>(() => serializer.Deserialize(YamlFile("explicitType.yaml"), typeof(object)));
}

[Test]
public void DeserializeDictionary()
{
Expand All @@ -206,9 +212,9 @@ public void DeserializeDictionary()
}

[Test]
public void DeserializeExplicitDictionary()
public void DeserializeUnsafeExplicitDictionary()
{
var serializer = new Serializer();
var serializer = new Serializer(new SerializerSettings { UnsafeAllowDeserializeFromTagTypeName = true });
object result = serializer.Deserialize(YamlFile("dictionaryExplicit.yaml"));

Assert.True(typeof(IDictionary<string, int>).IsAssignableFrom(result.GetType()), "The deserialized object has the wrong type.");
Expand All @@ -218,6 +224,13 @@ public void DeserializeExplicitDictionary()
Assert.AreEqual(2, dictionary["key2"]);
}

[Test]
public void DeserializeUnregisterdExplicitDictionary()
{
var serializer = new Serializer();
Assert.Throws<YamlException>(() => serializer.Deserialize(YamlFile("dictionaryExplicit.yaml")));
}

[Test]
public void DeserializeListOfDictionaries()
{
Expand Down Expand Up @@ -248,9 +261,9 @@ public void DeserializeList()
}

[Test]
public void DeserializeExplicitList()
public void DeserializeUnsafeExplicitList()
{
var serializer = new Serializer();
var serializer = new Serializer(new SerializerSettings { UnsafeAllowDeserializeFromTagTypeName = true });
var result = serializer.Deserialize(YamlFile("listExplicit.yaml"));

Assert.True(typeof(IList<int>).IsAssignableFrom(result.GetType()));
Expand All @@ -261,6 +274,13 @@ public void DeserializeExplicitList()
Assert.AreEqual(5, list[2]);
}

[Test]
public void DeserializeUnregisterdExplicitList()
{
var serializer = new Serializer();
Assert.Throws<YamlException>(() => serializer.Deserialize(YamlFile("listExplicit.yaml")));
}

[Test]
public void DeserializeEnumerable()
{
Expand Down
7 changes: 6 additions & 1 deletion src/SharpYaml.Tests/Serialization/SerializationTests2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,8 @@ public void TestNoEmitTags()
{
var settings = new SerializerSettings() { EmitTags = false };
settings.RegisterTagMapping("ClassWithObjectAndScalar", typeof(ClassWithObjectAndScalar));
Assert.True(settings.EmitTags);
settings.EmitTags = false;
var serializer = new Serializer(settings);
var text = serializer.Serialize(new ClassWithObjectAndScalar { Value4 = new ClassWithObjectAndScalar() });
Assert.False(text.Contains("!"));
Expand Down Expand Up @@ -885,6 +887,7 @@ public void TestClassMemberWithInheritance()
public void TestEmitShortTypeName()
{
var settings = new SerializerSettings() { EmitShortTypeName = true };
settings.RegisterAssembly(typeof(ClassWithObjectAndScalar).Assembly);
SerialRoundTrip(settings, new ClassWithObjectAndScalar());
}

Expand All @@ -899,6 +902,7 @@ public class ClassWithChars
public void TestClassWithChars()
{
var settings = new SerializerSettings() { EmitShortTypeName = true };
settings.RegisterAssembly(typeof(ClassWithChars).Assembly);
SerialRoundTrip(settings, new ClassWithChars()
{
Start = ' ',
Expand All @@ -910,6 +914,7 @@ public void TestClassWithChars()
public void TestClassWithSpecialChars()
{
var settings = new SerializerSettings() { EmitShortTypeName = true };
settings.RegisterAssembly(typeof(ClassWithObjectAndScalar).Assembly);
for (int i = 0; i < 32; i++)
{
SerialRoundTrip(settings, new ClassWithChars()
Expand Down Expand Up @@ -1554,7 +1559,7 @@ public void TestIgnoreNulls()
var text = serializer.Serialize(testObject);
Assert.False(text.Contains("DontSerializeWhenNull: null"));

var deserialized = serializer.Deserialize(new StringReader(text)) as ClassToIgnoreNulls;
var deserialized = serializer.Deserialize<ClassToIgnoreNulls>(new StringReader(text));
Assert.NotNull(deserialized);
Assert.AreEqual(testObject.Id, deserialized.Id);
Assert.Null(deserialized.DontSerializeWhenNull);
Expand Down
7 changes: 4 additions & 3 deletions src/SharpYaml.Tests/YamlNodeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,22 +189,23 @@ public void StyleTest()
}

[Test]
public void TagTest()
public void UnsafeTagTest()
{
var file = System.Reflection.Assembly.GetExecutingAssembly()
.GetManifestResourceStream("SharpYaml.Tests.files.dictionaryExplicit.yaml");

var fileStream = new StreamReader(file);
var stream = YamlStream.Load(fileStream);

var dict = stream[0].Contents.ToObject<object>();
var settings = new SerializerSettings() { UnsafeAllowDeserializeFromTagTypeName = true };
var dict = stream[0].Contents.ToObject<object>(settings);

Assert.AreEqual(typeof(Dictionary<string, int>), dict.GetType());
Assert.AreEqual("!System.Collections.Generic.Dictionary`2[System.String,System.Int32],mscorlib", stream[0].Contents.Tag);

stream[0].Contents.Tag = "!System.Collections.Generic.Dictionary`2[System.String,System.Double],mscorlib";

var dict2 = stream[0].Contents.ToObject<object>();
var dict2 = stream[0].Contents.ToObject<object>(settings);

Assert.AreEqual(typeof(Dictionary<string, double>), dict2.GetType());
}
Expand Down
21 changes: 13 additions & 8 deletions src/SharpYaml/Serialization/AssemblyRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ internal class AssemblyRegistry : ITagTypeRegistry
private readonly List<Assembly> lookupAssemblies;
private readonly object lockCache = new object();

private static readonly List<Assembly> DefaultLookupAssemblies = new List<Assembly>()
{
typeof(int).GetTypeInfo().Assembly,
};

/// <summary>
/// Initializes a new instance of the <see cref="AssemblyRegistry"/> class.
/// </summary>
Expand All @@ -92,6 +87,12 @@ public AssemblyRegistry(IYamlSchema schema)
/// <value><c>true</c> if [use short type name]; otherwise, <c>false</c>.</value>
public bool UseShortTypeName { get; set; }

/// <summary>
/// If set to <c>true</c>, Deserialize using unregistered type names contained in YAML.
/// <b>This should be set up carefully as vulnerabilities can occur.</b>
/// </summary>
public bool UnsafeAllowDeserializeFromTagTypeName { get; set; } = false;

public void RegisterAssembly(Assembly assembly, IAttributeRegistry attributeRegistry)
{
if (assembly == null)
Expand All @@ -100,7 +101,7 @@ public void RegisterAssembly(Assembly assembly, IAttributeRegistry attributeRegi
throw new ArgumentNullException("attributeRegistry");

// Add automatically the assembly for lookup
if (!DefaultLookupAssemblies.Contains(assembly) && !lookupAssemblies.Contains(assembly))
if (!lookupAssemblies.Contains(assembly))
{
lookupAssemblies.Add(assembly);

Expand Down Expand Up @@ -261,14 +262,18 @@ public virtual string TagFromType(Type type)

public virtual Type? ResolveType(string typeName)
{
var type = Type.GetType(typeName);
Type? type = null;
if (UnsafeAllowDeserializeFromTagTypeName)
{
type = Type.GetType(typeName);
}

if (type == null)
{
string? assemblyName = null;

// Find assembly name start (skip up to one space if needed)
// We ignore everything else (version, publickeytoken, etc...)
if (UseShortTypeName)
{
var typeNameEnd = typeName.IndexOf(',');
var assemblyNameStart = typeNameEnd;
Expand Down
19 changes: 16 additions & 3 deletions src/SharpYaml/Serialization/SerializerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ public SerializerSettings(IYamlSchema? schema)
PreferredIndent = 2;
IndentLess = false;
EmitAlias = true;
EmitTags = true;
SortKeyForMapping = true;
EmitJsonCompatible = false;
EmitCapacityForList = false;
Expand Down Expand Up @@ -131,10 +130,10 @@ public int PreferredIndent
public bool EmitAlias { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to emit tags when serializing. Default is true.
/// Gets or sets a value indicating whether to emit tags when serializing. Default is false.
/// </summary>
/// <value><c>true</c> to emit tags when serializing; otherwise, <c>false</c>.</value>
public bool EmitTags { get; set; }
public bool EmitTags { get; set; } = false;

/// <summary>
/// Gets or sets a value indicating whether the identation is trying to less
Expand Down Expand Up @@ -230,6 +229,16 @@ public IMemberNamingConvention NamingConvention
}
}

/// <summary>
/// If set to <c>true</c>, Deserialize using unregistered type names contained in YAML.
/// <b>This should be set up carefully as vulnerabilities can occur.</b>
/// </summary>
public bool UnsafeAllowDeserializeFromTagTypeName
{
get => AssemblyRegistry.UnsafeAllowDeserializeFromTagTypeName;
set => AssemblyRegistry.UnsafeAllowDeserializeFromTagTypeName = value;
}

/// <summary>
/// Gets or sets a value indicating whether to emit short type name (type, assembly name) or full <see cref="Type.AssemblyQualifiedName"/>. Default is false.
/// </summary>
Expand Down Expand Up @@ -340,22 +349,26 @@ public IObjectFactory ObjectFactory

/// <summary>
/// Register a mapping between a tag and a type.
/// When this method is called, <c>EmitTags</c> is then set to true.
/// </summary>
/// <param name="assembly">The assembly.</param>
public void RegisterAssembly(Assembly assembly)
{
AssemblyRegistry.RegisterAssembly(assembly, attributeRegistry);
EmitTags = true;
}

/// <summary>
/// Register a mapping between a tag and a type.
/// When this method is called, <c>EmitTags</c> is then set to true.
/// </summary>
/// <param name="tagName">Name of the tag.</param>
/// <param name="tagType">Type of the tag.</param>
/// <param name="isAlias">if set to <c>true</c> the new tag name is an alias to a type that has already a tag name associated to it.</param>
public void RegisterTagMapping(string tagName, Type tagType, bool isAlias = false)
{
AssemblyRegistry.RegisterTagMapping(tagName, tagType, isAlias);
EmitTags = true;
}

/// <summary>
Expand Down

0 comments on commit 406bd21

Please sign in to comment.