6

I am writing a library, and I have a method which takes in a Dictionary. The value of the dictionary is untrusted/insecure, but the key is trusted and if an end-user were given the ability to enter an arbitrary key name then "bad things" could happen.

So when other developers use this library function, I want to force them to know the key name at compile time. So something like this would be allowed:

string userInput = Console.ReadLine(); 
Dictionary<string, string> something = new Dictionary<string, string>();   
something.Add("MyKey", userInput);    

Because "MyKey" is a string literal, known at compile time. But something like this would either raise a compile or runtime exception:

string userInput = Console.ReadLine();
string userKey = Console.ReadLine(); 
Dictionary<string, string> something = new Dictionary<string, string>();   
something.Add(userKey, userInput);    

Because user input was used for the key (userKey) and thus it was unknown at compile time.

I've looked in GetType() and there is nothing that really distinguishes between a literal string and a string created at runtime.

Safari Jones
  • 470
  • 1
  • 4
  • 13
  • 1
    Can you not create (or reuse) a method that can look at a string value and either sanitize it, or determine if it is safe? Then you protect yourself from problematic string literals and allow the use of non-literal safe values. – Servy Jun 24 '15 at 20:06
  • 3
    Expressions in attributes are guaranteed to be constant, if I recall correctly. So, you could also try creating a custom attribute, and having client code pass you a member or a type decorated with that attribute. Then, you could read the metadata from that attribute. (Though there may be reflection tricks that can fake that, using dynamic assemblies.) – Theodoros Chatzigiannakis Jun 24 '15 at 20:11
  • The problem is that the key is inherently unsafe. That's why it should never ever come from user input under any circumstances. Right now my options are to either check if it is known at compile time OR to hardcode it into my library so it cannot be changed by external developers. – Safari Jones Jun 24 '15 at 20:11
  • Any way you do this is going to be hacky, as long as your key is a string. How about keying off object instances, and their Type must extend an abstract base Type you define in your library? Implementers would have to extend that type in order to create an instance and then pass it to you to use as the key. –  Jun 24 '15 at 20:11
  • I *do* like @Theo's solution, that's pretty damned good. –  Jun 24 '15 at 20:11
  • I might go with an attribute. It gives me the guarantees I want without taking away other developer's flexibility. The interning suggestion below might be viable also, but I'd need to research it, the last thing I'd want is another developer running into the exception and then just adding .Intern to the user-input to "fix" the problem. – Safari Jones Jun 24 '15 at 20:15
  • @SafariJones Whether the attribute solution (or possibly any solution) is valid, depends on whether the intention of the authors of the client code can be trusted. In other words: if you make it clear to them that you want them to provide a constant string, do you expect them to conform to this or could they (possibly with malicious intent) go seriously out of their way to circumvent that? If there is no (expected) malicious intent from the client code, then attributes could work. – Theodoros Chatzigiannakis Jun 24 '15 at 20:18
  • @TheodorosChatzigiannakis Of course, if there's malicious intent on the part of the caller then *there is no solution*. – Servy Jun 24 '15 at 20:21

3 Answers3

3

You can use string.IsInterned to determine if the string is interned. All compile time literals will be interned by default, but you can turn this off using a compiler flag.

This is a runtime check, not a compile time check.

Also note that non-compile time literals can be interned using the string.Intern function, so technically you can have non-literal strings pass your test. But if your program is either not interning strings at any point, or only ever interning strings you know are safe, then this may work.


Another option, if all of your keys are supposed to be known at compile time, is to not have the key be a string at all. Make the key be an enumeration, for example, so that you know that the only values that can ever be used as keys are on a list you've fixed at compile time.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Alternatively, one could receive a value of type `enum` and use the name of the passed-in value as a string. That would limit strings to characters that would be valid within an `enum` value's name, but would allow client code to use arbitrary enumerations that were defined within the code. – supercat Jun 24 '15 at 20:35
3

Not extensively tested, but by using expressions instead of direct values, you could test the type of value being passed. e.g.

void Add(Expression<Func<string>> fn)
{
        if (fn.Body.NodeType != ExpressionType.Constant)
            throw new ArgumentException("Only literal strings are allowed");

        //and extra check if the value itself is interned
        var val = fn.Compile()();
        if (string.IsInterned(val) == null)
            throw new ArgumentException("Only literal strings are allowed");

}

Then the developer has to pass the argument as a lambda:

  Add(() => "Test"); //Valid
  string somestring = "Test";
  Add(() => somestring); //Error
Me.Name
  • 12,259
  • 3
  • 31
  • 48
  • It's quite easy to construct an expression at runtime of a lambada that returns an `Expression.Constant` that returns a string value computed at runtime. – Servy Jun 24 '15 at 20:22
  • Given the requirements in the question (and assuming no malicious intent from client code), I think this is the best solution, being both easy to implement and not too easy to "accidentally" circumvent. – Theodoros Chatzigiannakis Jun 24 '15 at 20:34
  • @Servy True, but as you mentioned yourself, if the developers intent is there to circumvent the restriction, he can. Still a good point. Added an extra check on interned to make it more difficult. Wonder if you can check if the lambda is created dynamically – Me.Name Jun 24 '15 at 20:34
2

I can think of two possible ways, both using reflection:

  • Receive a type that contains a key as a string constant field. There is another answer on how to get constant fields from a type.

  • Define an attribute that stores a string. Attribute arguments are required to be constant expressions. You can receive a type or a member that's decorated with that attribute and extract the key from said attribute.


Worth mentioning that both can probably be faked by client code. For example, one could use dynamic assemblies or the System.ComponentModel.TypeDescriptor class.

Community
  • 1
  • 1
Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104