【问题标题】:Prevent "input" function from calling functions or accessing variables防止“输入”函数调用函数或访问变量
【发布时间】:2015-10-14 11:26:12
【问题描述】:

考虑以下代码

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') 作为输入会发生什么?由于输入被评估,它实际上是一个 Matlab 将执行的命令。因此,用户可以将文件夹添加到路径中。更糟糕的是,如果他们输入path(''),路径实际上将变为空,Matlab 将停止正常工作。

另一个潜在的问题来源是

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

例如,如果用户输入fprintf(1,'%f', varname)varname 是一个现有的数值数组,用户将知道它的当前值。

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

但在某些情况下,程序员可能想要一个更“安全”的input 函数,我的意思是

  1. 在评估用户时阻止任何函数调用 输入;和
  2. 防止输入访问程序的变量

所以[1 2] 将是有效输入,但[sqrt(2) sin(pi/3)]path('') 不会因为第1 项;而[1 2 3 varname(1)] 也会因为第 2 项而无效。

【问题讨论】:

  • 感谢分享!如果只是关于读取数字数组,您可以再次将输入读取为字符串,并在字符串上使用str2num。或者这种方法还有其他缺点吗?
  • @hbaderts 对于这种特殊情况(数字数组),我认为这将是一个非常好的方法:简单而有效。不过,它不适用于更一般的输入(单元格数组、字符数组等)
  • 是否有任何操作,例如*,+,-,/ 等有效吗?或者您只想要数字、文本或数字和字符的组合(存储在单元格中)?所有文本都应该包含在' 中吗?
  • 我想你对小鲍比桌很熟悉吧?未能进行输入验证始终是 BadThing(TM) 。我无法想象为什么(好吧,我可以)MathWorks 允许这样的东西投入生产。
  • @LuisMendo:抱歉,没看到那部分。我对 Carl 的短视评论做出了反应:“我无法想象为什么(好吧,是的,我可以)MathWorks 允许这样的东西投入生产。”

标签: matlab input user-input


【解决方案1】:

我找到了一个不太令人满意的解决方案(我很想了解一个更好的解决方案)。它使用半文档化 功能并暗示将用户输入保存到临时文件。在Yair Altman's blog 中引用的函数是getcallinfo。根据help getcallinfo

getcallinfo

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

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

那么程序是:

  1. 使用input 的字符串版本来防止评估:

    x = input('Input an array: ', 's');
    
  2. 将字符串保存到文件中:

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

    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 是下面的函数

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

示例 1

考虑

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'

这告诉我们,如果评估,用户输入将调用函数sqrtsinpi。请注意,/ 等运算符不会被检测为函数。

示例 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'

所以变量被getcallinfo 检测到就好像它们是函数一样。

【讨论】:

  • @AnderBiguri 谢谢! Yair Altman 的博客是奇怪东西的惊人来源:-)
  • 只是想知道:如果您使用 inputdlg 构建一个对话框,则有一个配置选项 options.Interpreter 。这可以用来强制输入被视为数字或字符串(因此不是函数或路径调用)吗?
  • @CarlWitthoft 该选项似乎控制 prompt 字符串(tex 或plain)es.mathworks.com/help/matlab/ref/inputdlg.html 的呈现
  • 您为什么不能使用正则表达式或某些模式匹配来识别已批准和未批准的字符串,然后评估它们是否通过?
  • @pragmatist1 前几天在处理这个问题的时候就想到了。乍一看,这似乎很复杂。正则表达式应该能够识别函数名称何时在字符串中,并在这种情况下忽略它。但也许它比我想的更简单:一个潜在的函数名在一个字符串中当且仅当它的左边有奇数个引号。这是一个很有前途的方法。想写一个关于这个的答案吗?如果你不这样做,也许我将来会研究这个。无论如何感谢您的建议!
【解决方案2】:

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

无函数或变量调用

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

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

仅此一项就适用于您在上面给出的几个示例。

允许一些函数或变量

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

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

但它并没有达到预期的效果。它将匹配sin 中的in(。如果您比我更了解正则表达式,则可以对其进行按摩以使其正常工作。

否则,另一种方法是这样做:

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'

【讨论】:

  • 谢谢。您应该允许字符eEij,以便接受-1.2e-3j 等数字。我认为筛选功能的一个很好的标准可能是禁止 两个 字母字符在一起。此外,'Fear is the path to the dark side' 之类的东西也应该被允许(path 出现但在字符串中)
  • 啊,是的,我不知道为什么我没有想到。我会相应地更新我的答案。
  • 好的,我更新了。让我知道是否需要更改。我不是正则表达式大师,但希望这能起到作用!
  • 实际上我不想从字符串中删除坏部分,只是为了检测字符串是否包含至少一个这样的坏部分(并在这种情况下拒绝整个字符串)。所以我认为像regexp(x, '^[^'']*(''[^'']*''[^'']*)*[a-zA-Z]{2}') 这样的东西可以工作(如果字符串在字符串之外有两个字母,则给出非空)。我必须进行更彻底的测试,但很有希望。
  • 是的,我使用 regexprep 不是为了清理字符串,而是在针对它运行通用表达式之前删除可能允许的函数/变量。无论哪种情况,希望它对你有用!
猜你喜欢
  • 1970-01-01
  • 2015-04-20
  • 2017-01-08
  • 2013-04-24
  • 2022-01-15
  • 2016-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多