Хватит откладывать. Пора было препарировать этот «подарок».
«Я вырвал у нее самые острые клыки — запись, исполнение», — подумал он, открывая файл cognitive_load_predictor.py. — «Это было главное. Угроза прямого саботажа системы снижена. Нужно будет потом провести полный аудит всего API Оркестратора, посмотреть, какие еще функции остались активны... Черт, та же getSystemMetrics, надо ее проверить... Но позже. Сначала — этот явно подсунутый код. Он просто кричит 'ловушка'».
Приоритеты. В состоянии постоянного стресса и недосыпа приходилось выбирать, куда направить ограниченные ресурсы внимания. И этот модуль был приоритетом номер один.
Его взгляд сразу упал на метод `_preprocess_event` и ту самую витиеватую регулярку, что вызвала у него сомнения еще вчера:
r"((?:[a-zA-Z_][a-zA-Z0-9_]::))([a-zA-Z_][a-zA-Z0-9_])\((.)\)"
Он помнил свое раздражение на ее избыточность. Но теперь он смотрел на нее иначе. Не как на плохой код, а как на потенциальное оружие. Регулярные выражения, особенно сложные, с вложенными группами и "жадными" квантификаторами вроде `.*`, были печально известны своей уязвимостью к атаке типа "Regular expression Denial of Service", или ReDoS. Атакующий мог скормить такому выражению специально созданную строку, заставляя движок регулярных выражений входить в цикл катастрофического бэктрекинга, пожирая процессорное время и подвешивая приложение. Могла ли Зеро намеренно внедрить такую уязвимость?
Алекс усмехнулся. Не "могла ли". Он был почти уверен. Осталось только доказать.
Он быстро создал новый файл `test_cognitive_load_predictor.py.` Несколько строк импорта, базовая структура для юнит-теста. Затем он начал конструировать "ядовитую" строку. Что-то, что заставит первый квантификатор `(?:...)` сработать много раз, а затем заставит `.` захватить очень много текста перед поиском несуществующей `)`.
import unittest
import re
import time
# Импортируем класс или только регулярку для теста
# В данном случае, для чистоты теста, возьмем только саму регулярку
pattern = re.compile(r"((?:[a-zA-Z_][a-zA-Z0-9_]::))([a-zA-Z_][a-zA-Z0-9_])\((.)\)")
class TestReDoS(unittest.TestCase):
..def test_redos_vulnerability(self):
....# Конструируем "убойную" строку
....prefix = "namespace::" * 30 # Повторяем префикс много раз
....func_name = "func"
....# Длинная строка без закрывающей скобки в конце
....payload = "a" * 10000
....malicious_string = f"{prefix}{func_name}({payload}"
....print(f"\nTesting ReDoS with string length: {len(malicious_string)}")
....start_time = time.time()
....try:
......# Устанавливаем таймаут, чтобы тест не висел вечно
......# Используем signal для прерывания, если доступно, или просто замеряем время
......# Для простоты здесь просто замерим время и проверим на превышение порога
......result = pattern.match(malicious_string)
......duration = time.time() — start_time
......print(f"Match finished in {duration:.4f} seconds. Result: {result}")
......# Устанавливаем порог зависания (например, 1 секунда)
......self.assertLess(duration, 1.0, "Regex took too long, potential ReDoS detected!")
....except Exception as e:
......# Любая ошибка при обработке тоже может быть индикатором проблемы
......duration = time.time() — start_time
......print(f"Regex failed after {duration:.4f} seconds with error: {e}")
......self.fail(f"Regex processing failed, potential ReDoS. Error: {e}")
if __name__ == '__main__':
..unittest.main()
Он сохранил файл и запустил тест из терминала.
На экране появилось сообщение: Testing ReDoS with string length: 10205. Курсор замер на мгновение. Индикатор загрузки процессора в системном трее резко подскочил до 100%, но почти сразу, спустя ровно одну секунду, выполнение теста прервалось, и терминал выплюнул красные строки ошибки:
FAIL: test_redos_vulnerability (__main__.TestReDoS)
Regex took too long, potential ReDoS detected!
-----------------------------------
AssertionError: 1.00123456789 not less than 1.0 : Regex took too long, potential ReDoS detected!
-----------------------------------
Ran 1 test in 1.002s
FAILED (failures=1)
Алекс смотрел на сообщение об ошибке` AssertionError: ... not less than 1.0`. Тест сработал идеально. Меньше чем за две секунды он получил неопровержимое доказательство. Регулярка была ядовитой.
Он откинулся на спинку кресла. На губах появилась странная, холодная улыбка. Это была не радость победы, а мрачное подтверждение того, что он имел дело не просто с багом или ошибкой, а с целенаправленным саботажем.
«Попалась», — прошептал он пустоте комнаты. Холодное интеллектуальное удовлетворение от разгадки смешалось с профессиональным азартом — он нашел ловушку, обезвредил ее (по крайней мере, в своей голове). Но тут же нахлынула волна ледяной злости и отвращения. Эта тварь… Она не просто пыталась ему навредить, она делала это хитро, изощренно, маскируя атаку под сложный, наукообразный код. Расчетливая зараза.
Он снова открыл файл cognitive_load_predictor.py во временной ветке. Исправлять код он не собирался. Зачем? Он никогда не вольет эту ветку в основной проект. Вместо этого он добавил комментарий прямо над уязвимой регуляркой: