Consider the following:
example.txt
{TextStyle -> {FontFamily -> "Times"}, Axes -> False, AspectRatio -> Automatic}
{TextStyle -> {FontFamily -> "Arial", FontSize -> 12}, Axes -> True, AspectRatio -> 1}
{Frame -> True, Background -> RGBColor[1, 0, 0]}
{Background -> Red, Axes -> False, AspectRatio -> Automatic}
- you have a large text file (example.txt) with several inputs from non-secure users
- entries that does not correspond to options should be discarded
At first glance you might solve that with OptionQ + ToExpression:
OptionQ[ ToExpression[ line1 ] ]
If line1 is an option then the output is True, otherwise the output is False. However, there is a drawback in this approach. The built-in function ToExpression actualy evaluates the user input, which cause a serious security flaw. Malicious users might run whatever they like in your machine, for example:
{DeleteFile[FileNames[]]}
In this case, ToExpression will evaluate this line and delete all files in your folder, and later OptionQ will tell you that this line is not an option and should be ignored.
Issue
Is there a simple way to check if a text entry is an option without evaluating it?
ToExpressionwith 3 arguments, for example likeToExpression[codestring, StandardForm, HoldComplete], in which case the code will be parsed but not executed, and will be returned to you wrapped inHoldComplete. You can then analyze the code without evaluating it. – Leonid Shifrin Jan 04 '16 at 16:34msg = "{Method->Automatic}"; OptionQ[ ToExpression[ msg, StandardForm, HoldComplete ] ];However, the result is False. If you remove theHoldComplete, then the result correctly states True. The same is true withUnevaluated. – Mark Messa Jan 04 '16 at 16:55OptionQgivesFalseon an expression with theHoldCompletehead. What you may need is slightly more complex, I just suggested the main idea of the solution. The actual code you may need can be something likeSelect[ StringSplit[text, "\n"], ToExpression[#, StandardForm, Function[expr, OptionQ[Unevaluated[expr]], HoldAllComplete]] &]. – Leonid Shifrin Jan 04 '16 at 17:03Cases[Map[ToExpression[#, StandardForm, HoldComplete] &, StringSplit[text, "\n"]], HoldComplete[opt_ /; OptionQ[Unevaluated[opt]]] :> opt]. – Leonid Shifrin Jan 04 '16 at 17:06Uncompressthe string. – Leonid Shifrin Jan 04 '16 at 17:52ToExpression[ "Method->All,", StandardForm, Function[ expr, OptionQ[ Unevaluated[expr] ], HoldAllComplete ]]. Fortunately it seems that this can be easily solved including braces over the string:ToExpression[ "{Method->All,}", StandardForm, Function[ expr, OptionQ[ Unevaluated[expr] ], HoldAllComplete ]]– Mark Messa Jan 04 '16 at 18:30StringSplit[text, "\n"]withSelect[StringSplit[text, "\n"], SyntaxQ]]in the code I suggested. This pre-filtering stage will remove all strings which do not represent valid Mathematica syntax. – Leonid Shifrin Jan 04 '16 at 19:04SyntaxQis the way to go. The only drawback is the annoying "beep" each time the input text is strange, ex:msg = "Method->()". In this caseSelect[StringSplit[msg, "\n"], SyntaxQ]will make a "beep". Of course it can be turn off in 'Option Inspector' -> 'Global Options' -> 'MessageOptions' -> "WarningAction" -> {} – Mark Messa Jan 04 '16 at 19:49OptionQtest, can still contain dangerous code, in right hand side ofRule/RuleDelayed, that will be evaluated when option is un-held, or when option value used. To be safe you would need to know exactly what values of options are acceptable and test them on held option. If an option can, for example, accept a function, then it could be extremely difficult to test whether it contains malicious code. – jkuczm Jan 06 '16 at 13:10OptionQtest, can still contain dangerous code Ok, you are right. I was considering using options because it's fast to program and gives a reasonably high level interface with users. However, test all acceptable values beforehand doesn't seem that fast to program. Unfortunately, I'll have to migrate to a lower level user input. – Mark Messa Jan 06 '16 at 13:55