Можно ли интерполировать значения массива в токене? - PullRequest
7 голосов
/ 07 января 2020

Я работаю над модулем гомоглифов , и мне нужно создать регулярное выражение, которое может найти гомоглифтованный текст, соответствующий эквиваленту ASCII.

Так, например, у меня есть символ без вариантов гомоглифа:

my $f = 'f';

и символ, который может быть запутан:

my @o = 'o', 'о', 'ο'; # ASCII o, Cyrillic o, Greek omicron

Я могу легко построить регулярное выражение, которое обнаружит гомоглифированную фразу 'foo':

say 'Suspicious!' if $text ~~ / $f @o @o /;

Но Как мне составить такое регулярное выражение, если я не знаю значение для обнаружения во время компиляции? Допустим, я хочу обнаружить фишинг, содержащий гомоглифированное слово «ca sh» в сообщениях. Я могу построить последовательность со всеми альтернативами:

my @lookup = ['c', 'с', 'ϲ', 'ς'], ['a', 'а', 'α'], 's', 'h'; # arbitrary runtime length

Теперь, очевидно, следующее решение не может «распаковать» элементы массива в регулярное выражение:

/ @lookup / # doing LTM, not searching elements in sequence

Я могу обойти это, вручную цитируя каждый Элемент и составить текстовое представление альтернатив, чтобы получить строку, которая может быть оценена как регулярное выражение. И построить токен из этого с использованием строковой интерполяции:

my $regexp-ish = textualize( @lookup ); # string "[ 'c' | 'с' | 'ϲ' | 'ς' ] [ 'a' | 'а' | 'α' ] 's' 'h'"
my $token = token { <$regexp-ish> }

Но это довольно подвержено ошибкам. Есть ли более чистое решение для создания регулярного выражения на лету из произвольного количества элементов, неизвестных во время компиляции ?

Ответы [ 2 ]

5 голосов
/ 07 января 2020

Модуль Unicode::Security реализует сбиваемые значения с помощью таблиц консорциума Unicode . На самом деле он не использует регулярные выражения, просто ищет разные символы в этих таблицах.

4 голосов
/ 07 января 2020

Я не уверен, что это лучший подход к использованию.

Я еще не реализовал модуль 1 в confusables еще в Intl::, хотя я планирую со временем обойти его, вот два разных способа представить токен в виде. 2

my token confusable($source) {
  :my $i = 0;                                    # create a counter var
  [
    <?{                                          # succeed only if
      my $a = self.orig.substr: self.pos+$i, 1;  #   the test character A
      my $b = $source.substr: $i++, 1;           #   the source character B and

      so $a eq $b                                #   are the same or
      || $a eq %*confusables{$b}.any;            #   the A is one of B's confusables
    }> 
    .                                            # because we succeeded, consume a char
  ] ** {$source.chars}                           # repeat for each grapheme in the source
}

Здесь я использовал динамику c га sh %*confusables, которая будет каким-либо образом заполнена - это будет зависеть от вашего модуля и даже не обязательно будет Dynami c (например, имея подпись :($source, %confusables) или ссылаясь на переменную модуля, и т. д. c.

Затем ваш код может работать следующим образом:

say $foo ~~ /<confusable: 'foo'>/

Это, вероятно, лучший способ go о вещах, так как он даст вам гораздо больший контроль - я взял верх у вашего модуля, и ясно, что вы хотите включить 2-к-1 отношения глифа, и в конечном итоге вы, вероятно, захотите чтобы запускать код непосредственно над символами.

Если у вас все в порядке с отношениями 1: 1, вы можете go с гораздо более простым токеном:

my token confusable($source) {
  :my @chars = $source.comb;            # split the source 
  @(                                    # match the array based on 
     |(                                 #   a slip of
        %confusables{@chars.head}       #     the confusables 
        // Empty                        #     (or nothing, if none)
     ),                                 #
     @a.shift                           #   and the char itself
   )                                    #
   ** {$source.chars}                   # repeating for each source char
}

The @(…) структура позволяет эффективно создавать adh o c массив для интерполяции. В этом случае мы просто проскальзываем в оригинале, и все. Вы должны быть осторожны, потому что несуществующий элемент ha sh вернет объект типа (Any), и это все испортит (отсюда // Empty)

В любом случае вам понадобится использовать аргументы с вашим токеном, так как построение регулярных выражений на лету чревато потенциальными ошибками и ошибками интерполяции.


1 Юникод вызывает гомографию как "визуально схожих символов", так и "confusables".

2 Dynami c га sh здесь %confusables может быть заполнено любым количеством способов, и может не обязательно быть динамическим c, так как оно может быть заполнено с помощью аргументов (используя сигнатуру типа :($source, %confusables) или ссылку на переменную модуля.

...