Perl 18:用户提供的点号,不该变成任意字符
假设 CLI 以后按消息中的字面字符串查找,目标是 a.b。如果把它直接插入正则 /$needle/,点号成为“任意字符”,在 aXb a.b 中会先找到错误的 aXb。这是动态模式与用户字面数据混用的缺陷;不需要恶意生产日志,用两段固定合成字符串就能复现。
perl examples/perl-language/experiments/16-20-patterns.pl 18 的 STDOUT 是 unquoted=aXb、literal=a.b;STDERR 空、退出码 0。后者使用 /\Q$needle\E/,把插入部分按字面值处理;等价的准备方式可以使用 quotemeta。这里故意不从用户输入编译任意正则,也不承诺“套上 \Q 就解决整个输入安全问题”:解码、输入长度、字节/字符语义和整体过滤策略仍需各自约束。
旧 logtool.pl query 只接受枚举等级;当前 Course 入口已经新增 --contains,但它仍是字面包含,不是自由正则过滤参数。这也是一个可操作的设计判断:只需要精确包含时,不必先把问题变成运行时正则编译。若日后确需按字段模式筛选,须说明哪些部分是允许语法、哪些部分是用户字面数据,并为双方分别设坏样本。
补充闭包过滤器把同一原则放进工程接口:make_filter(level => 'WARN', contains => 'a.b') 对消息 aXb then a.b 返回真,对 diskXfull 返回假。contains 使用 index 做字面包含,不把点号交给正则引擎解释。这样,用户给出的筛选词和程序员编写的正则语法被分到不同入口,测试也能明确判断“点号是点号”。
脱敏替换是另一个边界:course-semantics.pl regex 先复制 $original_message 到 $redacted_message,再对副本执行 s/token=\w+/token=[REDACTED]/,输出 redacted=token=[REDACTED] keep original=token=abc123 keep。替换可以使用受控正则,但不能顺手改掉原记录;后续审计、错误定位和 raw 字段仍需要原始消息。
[PATTERN] 当输入是数据,就用字符串比较、index 或 quotemeta 后的字面模式;当输入是受控语法,才让它成为正则。两种入口不要共用一个裸 /$needle/。官方 perlre 的 Quoting metacharacters 和 perlfunc 的 quotemeta 只解决元字符解释问题,不能替代长度限制、解码失败处理和整体查询权限。
练习:目标字面值改成 a+b,裸插值会发生什么?+ 会作用于前一个原子,不再代表加号本身;先明确字面匹配才可用 \Q...\E 或 quotemeta。对应金标和断言见 examples/perl-language/evidence/15-20.md;参考 Perl 5.38.2 pod/perlre.pod(Quoting metacharacters)、pod/perlfunc.pod(quotemeta)。









