Considering the following code
x = input('Input an array: ');
If the user types [1 2 3]
, variable x
will be assigned that numeric vector. Similarly, if they type {1, [2 3], 'abc'}
, variable x
will be a cell array containing those values. Fine.
Now, if the user types [sqrt(2) sin(pi/3)]
, variable x
will be assigned the resulting values: [1.414213562373095 0.866025403784439]
. That's because the provided data is evaluated by input
:
input
Prompt for user input.result = input(prompt)
displays theprompt
string on the screen, waits for input from the keyboard, evaluates any expressions in the input, and returns the value inresult
. [...]
This can cause problems. For example, what happens if the user types addpath('c:\path\to\folder')
as input? Since the input is evaluated, it's actually a
command which will be executed by Matlab. So the user can get to add a folder to the path. Worse yet, if they input path('')
, the path will be effectively changed to nothing, and Matlab will stop working properly.
Another potential source of problems is that
[...] To evaluate expressions,
input
accesses variables in the current workspace.
For example, if the user inputs fprintf(1,'%f', varname)
and varname
is an existing numeric array, the user will know its current value.
This behaviour is probably by design. Users of a Matlab program are trusted when inputting data, much like they are trusted not to press Control-C to halt the program (and then issue all commands or inspect all variables they like!).
But in certain cases the programmer may want to have a more "secure" input
function, by which I mean
So [1 2]
would be valid input, but [sqrt(2) sin(pi/3)]
or path('')
would not because of item 1; and [1 2 3 varname(1)]
would be invalid too because of item 2.
I have found a not very satisfactory solution (and I'd love to read about a better one). It uses a semi-documented function and implies saving the user input to a temporary file. The function, referred to in Yair Altman's blog, is getcallinfo
. According to help getcallinfo
:
getcallinfo
Returns called functions and their first and last lines
This function is unsupported and might change or be removed without notice in a future version.
This function solves issue 1 (prevent function calls). As for issue 2 (prevent access to variables), it would suffice to evaluate the input within a function, so that it can't see other variables. Apparently (see example 2 below), getcallinfo
detects not only called functions, but variables too. Anyway, it's probably a good idea to do the evaluation in the isolated scope of a function.
The procedure is then:
Use the string version of input
to prevent evaluation:
x = input('Input an array: ', 's');
Save the string to a file:
filename = 'tmp.m';
fid = fopen(filename,'w');
fprintf(fid, '%s',x);
fclose(fid);
Check the input string with getcallinfo
to detect possible function calls:
gci = getcallinfo(filename);
if ~isempty(gci.calls.fcnCalls.names)
%// Input includes function calls: ignore / ask again / ...
else
x = evalinput(x); %// evaluate input in a function
end
where evalinput
is the following function
function x = evalinput(x)
x = eval(x);
Consider
x = input('Input an array: ', 's');
with user input
[sqrt(2) sin(pi/3)]
Then
filename = 'tmp.m';
fid = fopen(filename,'w');
fprintf(fid, '%s',x);
fclose(fid);
gci = getcallinfo(filename);
produces a non-empty gci.calls.fcnCalls.names
,
>> gci.calls.fcnCalls.names
ans =
'sqrt' 'sin' 'pi'
which tells us that the user input would call functions sqrt
, sin
and pi
if evaluated. Note that operators such as /
are not detected as functions.
y = [10 20 30];
x = input('Input an array: ', 's');
User enters
[1 y y.^2]
Then
filename = 'tmp.m';
fid = fopen(filename,'w');
fprintf(fid, '%s',x);
fclose(fid);
gci = getcallinfo(filename);
produces
>> gci.calls.fcnCalls.names
ans =
'y' 'y'
So variables are detected by getcallinfo
as if they were functions.
If I understand your question correctly, it is possible to use regular expressions to accomplish what you are trying to do.
No function or variable calls
At its simplest, this checks to making sure there are no alphabetical characters in the input string. The expression would then be, for x
containing input:
expr = '[a-zA-Z]';
x = input('Input an array: ', 's');
valid = isempty(regexp(x,expr));
This alone works for the few examples you give above.
Allowing some functions or variables
Suppose you want to allow the user to access some variables or functions, maybe simple trig functions, or pi
or what have you, then it's no longer so simple. I've been playing around with an expression like below:
expr = '(?!cos\(|pi|sin\()[a-zA-Z]+
But it doesn't quite do what is expected. It will match in(
in sin
. If you know regex better than I, you can massage that to work properly.
Otherwise, an alternative would be to do this:
isempty(regexp(regexprep(x,'(sin\(|cos\(|pi|x)',''),expr))
so that you remove the strings you are interested in.
Hope this helps.
Update: In order to allow imaginary/exp values, and paths
The new expression to match becomes
expr = '[iIeE][a-zA-Z]+';
This ignores i/I and e/E (you can augment this as you see fit). You can also do a two character limit by switching to \{2,}
, though I people can still have one character anonymous functions..
The other part, to check for input becomes:
isempty(regexp(regexprep(x,'(sin\(|cos\(|pi|x|''(.*?)'')',''),expr))
now you can exclude custom functions (you can always have this as an array and join them together by a |
) and paths.
Here are a few examples tested along with the usual:
Passes
'[1+2i, 34e12]'
'''this is a path'''
'[cos(5), sin(3+2i)]'
Fails
'[1+2ii, 34e12]'
'this is not a path'
'''this is a path'' this is not'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With