﻿# V5340\. OWASP\. Code contains invisible characters that may alter its logic\. Consider enabling the display of invisible characters in the code editor\.

Анализатор обнаружил в тексте программы символы, которые могут ввести программиста в заблуждение\. Эти символы могут не отображаться и изменять видимое представление кода в среде разработки\. Комбинации таких символов могут привести к тому, что человек и компилятор будут интерпретировать код по\-разному\.

В некоторых случаях это делается намеренно\. Такой вид атаки называется [Trojan Source](https://arxiv.org/abs/2111.00169)\. Подробнее:

* [Атака Trojan Source для внедрения в код изменений, незаметных для разработчика](https://pvs-studio.ru/ru/blog/posts/cpp/0933/);
* [Атака Trojan Source: скрытые уязвимости](https://pvs-studio.ru/ru/blog/posts/cpp/0935/)\.

|Обозначение|Код|Название|Описание|
|---|---|---|---|
|LRE|U\\\+202A|LEFT\\\-TO\\\-RIGHT EMBEDDING|Текст после символа LRE интерпретируется как вставленный и отображается слева направо\\\. Действие LRE прерывается символом PDF или символом перевода строки\\\.|
|RLE|U\\\+202B|RIGHT\\\-TO\\\-LEFT EMBEDDING|Текст после символа RLE интерпретируется как вставленный и отображается справа налево\\\. Действие RLE прерывается символом PDF или символом перевода строки\\\.|
|LRO|U\\\+202D|LEFT\\\-TO\\\-RIGHT OVERRIDE|Текст после символа LRO принудительно отображается слева направо\\\. Действие LRO прерывается символом PDF или символом перевода строки\\\.|
|RLO|U\\\+202E|RIGHT\\\-TO\\\-LEFT OVERRIDE|Текст после символа RLO принудительно отображается справа налево\\\. Действие RLO прерывается символом PDF или символом перевода строки\\\.|
|PDF|U\\\+202C|POP DIRECTIONAL FORMATTING|Символ PDF прерывает действие одного из символов LRE, RLE, LRO или RLO, встреченного ранее\\\. Прерывает ровно один символ, последний из встреченных\\\.|
|LRI|U\\\+2066|LEFT\-TO\-RIGHT ISOLATE|Текст после символа LRI отображается слева направо и интерпретируется как изолированный\\\. Это означает, что другие управляющие символы не влияют на отображение этого фрагмента текста\\\. Действие LRI прерывается символом PDI или символом перевода строки\\\.|
|RLI|U\\\+2067|RIGHT\-TO\-LEFT ISOLATE|Текст после символа RLI отображается справа налево и интерпретируется как изолированный\\\. Это означает, что другие управляющие символы не влияют на отображение этого фрагмента текста\\\. Действие RLI прерывается символом PDI или символом перевода строки\\\.|
|FSI|U\\\+2068  |FIRST STRONG ISOLATE|Направление текста после символа FSI задаётся первым управляющим символом, не входящим в этот фрагмент текста\\\. Другие управляющие символы не влияют на отображение этого текста\\\. Действие FSI прерывается символом PDI или символом перевода строки\\\.|
|PDI|U\\\+2069|POP DIRECTIONAL ISOLATE|Символ PDI прерывает действие одного из символов LRI, RLI или FSI, встреченного ранее\\\. Прерывает ровно один символ — последний из встреченных\\\.|
|LRM|U\\\+200E|LEFT\\\-TO\\\-RIGHT MARK|Текст после символа LRM отображается слева направо\\\. Действие LRM прерывается символом перевода строки\\\.|
|RLM|U\\\+200F|RIGHT\\\-TO\\\-LEFT MARK|Текст после символа RLM отображается справа налево\\\. Действие RLM прерывается символом перевода строки\\\.|
|ALM|U\\\+061C|ARABIC LETTER MARK|Текст после символа ALM отображается справа налево\\\. Действие ALM прерывается символом перевода строки\\\.|
|ZWSP|U\\\+200B|ZERO WIDTH SPACE|Неотображаемый пробельный символ\\\. Использование символа ZWSP может привести к тому, что разные строки будут отображаться одинаково\\\. Например, \`str\[ZWSP\]ing\` отображается как \`string\`\\\.|

Рассмотрим следующий фрагмент кода:

```cpp
boolean isAdmin = false;
/*[RLO] } [LRI] if (isAdmin)[PDI] [LRI] begin admins only */ // (1)
  System.out.println("You are an admin.");
/* end admins only [RLO]{ [LRI]*/                            // (2)
```

Изучим детально строку `(1)`:

```cpp
[LRI] if (isAdmin)[PDI]
```

Здесь символ `[LRI]` действует до символа `[PDI]`\. Строка `if (isAdmin)` будет отображаться слева направо и считается изолированной\. Получаем `if (isAdmin)`\.

```cpp
[LRI] begin admins only */
```

Здесь символ `[LRI]` действует до конца строки\. Получаем изолированную строку `begin admins only */`\.

```cpp
[RLO] {пробел1}, '}', {пробел2}, 'if (isAdmin)', 'begin admins only */'
```

Здесь символ `[RLO]` действует до конца строки и отображает текст справа налево\. Каждая из полученных в предыдущих пунктах изолированных строк рассматривается как отдельный неделимый символ\. Получаем такую последовательность:

```cpp
'begin admins only */', 'if (isAdmin)', {пробел2}, '{', {пробел1}
```

Обратите внимание, что символ закрывающей фигурной скобки теперь отображается как `{` вместо `}`\.

Итоговый вид строки `(1)`, который может быть отображён в редакторе:

```cpp
/* begin admins only */ if (isAdmin) {
```

Похожие преобразования затронут и строку `(2)`, которая отобразится так:

```cpp
/* end admins only */ }
```

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

```cpp
boolean isAdmin = false;
/* begin admins only */ if (isAdmin) { 
  System.out.println("You are an admin.");
/* end admins only */ }
```

Ревьюер может посчитать, что в коде выполняется некоторая проверка перед выводом сообщения\. Он проигнорирует комментарии и подумает, что код должен выполняться так:

```cpp
boolean isAdmin = false;
if (isAdmin) { 
  System.out.println("You are an admin.");
}
```

Однако на самом деле проверки нет\. Для компилятора рассмотренный код выглядит так:

```cpp
boolean isAdmin = false;
System.out.println("You are an admin.");
```

Теперь рассмотрим более простой и в то же время более опасный пример использования неотображаемых символов:

```cpp
enum BlockCipherType { DES, TripleDES, AES };

BlockCipherType getBlockCipherType(String cipherTypeName) {
  if (cipherTypeName.equals("AES[ZWSP]")) {
    return BlockCipherType.AES;
  } else if (cipherTypeName.equals("TripleDES[ZWSP]")) {
    return BlockCipherType.TripleDES;
  } else {
    return BlockCipherType.DES;
  }
}
```

Метод `getBlockCipherType` производит конвертацию строки в одно из значений перечисления `BlockCipherType`\. По коду можно сделать вывод, что функция возвращает три разных значения, однако это не так\. Из\-за того, что в конце каждого строкового литерала добавлен неотображаемый пробельный символ `[ZWSP]`, проверки на равенство со строками `AES` и `TripleDES` будут ложными\. В итоге из трёх ожидаемых значений функция будет возвращать лишь `BlockCipherType.DES`\. В то же время код в редакторе может отображаться следующим образом:

```cpp
enum BlockCipherType { DES, TripleDES, AES };

BlockCipherType getBlockCipherType(String cipherTypeName) {
  if (cipherTypeName.equals("AES")) {
    return BlockCipherType.AES;
  } else if (cipherTypeName.equals("TripleDES")) {
    return BlockCipherType.TripleDES;
  } else {
    return BlockCipherType.DES;
  }
}
```

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