Nbsp; Detecting the Use of a Custom Attribute Without Creating Attribute-Derived Objects
In this section, I discuss an alternate technique for detecting custom attributes applied to a meta- data entry. In some security-conscious scenarios, this alternate technique ensures that no code
in an Attribute-derived class will execute. After all, when you call Attribute’s GetCustom Attribute(s) methods, internally, these methods call the attribute class’s constructor and can also call property set accessor methods. In addition, the first access to a type causes the CLR to invoke the type’s type constructor (if it exists). The constructor, set accessor, and type constructor methods could contain code that will execute whenever code is just looking for an attribute. This allows unknown code to run in the AppDomain, and this is a potential security vulnerability.
To discover attributes without allowing attribute class code to execute, you use the System.
Reflection.CustomAttributeData class. This class defines one static method for retrieving the attributes associated with a target: GetCustomAttributes. This method has four overloads: one that takes an Assembly, one that takes a Module, one that takes a ParameterInfo, and one that takes
a MemberInfo. This class is defined in the System.Reflection namespace, which is discussed in Chapter 23. Typically, you’ll use the CustomAttributeData class to analyze attributes in metadata for an assembly that is loaded via Assembly’s static ReflectionOnlyLoad method (also discussed in Chapter 23). Briefly, ReflectionOnlyLoad loads an assembly in such a way that prevents the CLR from executing any code in it; this includes type constructors.
CustomAttributeData’s GetCustomAttributes method acts as a factory. That is, when you call it, it returns a collection of CustomAttributeData objects in an object of type IList<Custom AttributeData>. The collection contains one element per custom attribute applied to the specified target. For each CustomAttributeData object, you can query some read-only properties to deter- mine how the attribute object would be constructed and initialized. Specifically, the Constructor
property indicates which constructor method would be called, the ConstructorArguments property returns the arguments that would be passed to this constructor as an instance of IList<Custom AttributeTypedArgument>, and the NamedArguments property returns the fields/properties that would be set as an instance of IList<CustomAttributeNamedArgument>. Notice that I say “would be” in the previous sentences because the constructor and set accessor methods will not actually be called—we get the added security by preventing any attribute class methods from executing.
Here’s a modified version of a previous code sample that uses the CustomAttributeData class to securely obtain the attributes applied to various targets.
using System;
using System.Diagnostics; using System.Reflection;
using System.Collections.Generic;
[assembly: CLSCompliant(true)]
[Serializable]
[DefaultMemberAttribute("Main")]
[DebuggerDisplayAttribute("Richter", Name="Jeff", Target=typeof(Program))] public sealed class Program {
[Conditional("Debug")] [Conditional("Release")] public void DoSomething() { }
public Program() {
}
[CLSCompliant(true)] [STAThread]
public static void Main() {
// Show the set of attributes applied to this type ShowAttributes(typeof(Program));
// Get the set of methods associated with the type MemberInfo[] members = typeof(Program).FindMembers(