防止"输入"函数调用函数或访问变量

时间:2015-10-14 11:26:12

标签: matlab input user-input

考虑以下代码

x = input('Input an array: ');

如果用户输入[1 2 3],则会为变量x分配该数字向量。同样,如果他们键入{1, [2 3], 'abc'},变量x将是包含这些值的单元格数组。细

现在,如果用户输入[sqrt(2) sin(pi/3)],则会为变量x分配结果值:[1.414213562373095 0.866025403784439]。这是因为input提供的数据评估

  

input提示用户输入。
      result = input(prompt)在屏幕上显示prompt字符串,等待       对于来自键盘的输入,评估输入中的任何表达,       并返回result中的值。 [...]

这可能会导致问题。例如,如果用户输入addpath('c:\path\to\folder')作为输入会发生什么?由于评估了输入,它实际上是a 命令将由Matlab执行。因此用户可以在路径中添加文件夹。更糟糕的是,如果他们输入path(''),路径将被有效地更改为空,并且Matlab将停止正常工作。

另一个潜在的问题来源是

  

[...]要评估表达式,input 访问当前工作区中的变量

例如,如果用户输入fprintf(1,'%f', varname)并且varname是现有数字数组,则用户将知道其当前值。

这种行为可能是设计上的。输入数据时,Matlab程序的用户是可信的,就像他们被信任不要按 Control - C 来暂停程序(然后发出所有命令或检查所有变量)他们喜欢!)。

但在某些情况下,程序员可能希望获得更多"安全" input函数,我的意思是

  1. 在评估用户时阻止任何函数调用 输入;和
  2. 阻止输入访问程序的变量
  3. 因此[1 2]是有效输入,但由于第1项,[sqrt(2) sin(pi/3)]path''不会;由于第2项,[1 2 3 varname(1)]也无效。

2 个答案:

答案 0 :(得分:6)

我找到了一个不太令人满意的解决方案(而且我喜欢阅读更好的解决方案)。它使用半文档功能,意味着将用户输入保存到临时文件Yair Altman's blog中提到的函数是getcallinfo。根据{{​​1}}:

  

help getcallinfo

     

返回调用函数及其第一行和最后一行       此功能不受支持,可能会更改或删除       在将来的版本中注意。

此函数解决问题1(阻止函数调用)。对于问题2(防止访问变量),评估函数内的输入就足够了,因此它无法看到其他变量。显然(参见下面的示例2),getcallinfo不仅会检测被调用的函数,还会检测变量。无论如何,在功能的孤立范围内进行评估可能是个好主意。

程序是:

  1. 使用getcallinfo的字符串版本来阻止评估:

    input
  2. 将字符串保存到文件中:

    x = input('Input an array: ', 's');
    
  3. 使用filename = 'tmp.m'; fid = fopen(filename,'w'); fprintf(fid, '%s',x); fclose(fid); 检查输入字符串以检测可能的函数调用:

    getcallinfo
  4. 其中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 是以下函数

    evalinput

    示例1

    考虑

    function x = evalinput(x)
    x = eval(x);
    

    用户输入

    x = input('Input an array: ', 's');
    

    然后

    [sqrt(2) sin(pi/3)]
    

    生成非空filename = 'tmp.m'; fid = fopen(filename,'w'); fprintf(fid, '%s',x); fclose(fid); gci = getcallinfo(filename);

    gci.calls.fcnCalls.names

    告诉我们,如果评估,用户输入将调用函数>> gci.calls.fcnCalls.names ans = 'sqrt' 'sin' 'pi' sqrtsin。请注意,pi等运算符不会被检测为函数。

    示例2

    /

    用户输入

    y = [10 20 30];
    x = input('Input an array: ', 's');
    

    然后

    [1 y y.^2]
    

    产生

    filename = 'tmp.m';
    fid = fopen(filename,'w');
    fprintf(fid, '%s',x);
    fclose(fid);
    gci = getcallinfo(filename);
    

    因此>> gci.calls.fcnCalls.names ans = 'y' 'y' 检测到变量就好像它们是函数一样。

答案 1 :(得分:1)

如果我正确理解你的问题,可以使用正则表达式来完成你想要做的事情。

无函数或变量调用

最简单的方法是检查输入字符串中是否没有字母字符。对于包含输入的x,表达式将是

expr = '[a-zA-Z]';
x = input('Input an array: ', 's');
valid = isempty(regexp(x,expr));

仅此一点适用于上面给出的几个例子。

允许某些功能或变量

假设您想要允许用户访问某些变量或函数,可能是简单的trig函数,或者pi或者您有什么,那么它就不再那么简单了。我一直在玩下面的表达式:

expr = '(?!cos\(|pi|sin\()[a-zA-Z]+

但它并没有完全符合预期。它将与in(中的sin匹配。如果你比我更了解正则表达式,你可以按摩它以正常工作。

否则,另一种方法是:

isempty(regexp(regexprep(x,'(sin\(|cos\(|pi|x)',''),expr))

以便删除您感兴趣的字符串。

希望这有帮助。

更新:为了允许虚数/ exp值和路径

要匹配的新表达式变为

expr = '[iIeE][a-zA-Z]+';

这会忽略i / I和e / E(你可以根据需要增加它)。你也可以通过切换到\{2,}来实现两个字符的限制,尽管我的人仍然可以有一个字符匿名函数..

检查输入的另一部分是:

isempty(regexp(regexprep(x,'(sin\(|cos\(|pi|x|''(.*?)'')',''),expr))

现在您可以排除自定义函数(您可以将其作为数组,并通过|将它们连接在一起)和路径。

以下是一些与通常一起测试的例子:

<强>通行证

'[1+2i, 34e12]'
'''this is a path'''
'[cos(5), sin(3+2i)]'

<强>失败

'[1+2ii, 34e12]'
'this is not a path'
'''this is a path'' this is not'