В августе я открыл codecard. Он рисует сниппет кода или лога как карточку для ленты. Перед тем как рисовать, он читает текст и отказывается, если текст выдаёт кодовую базу. То есть строка пакета, импорт вашего собственного кода, путь сборки с именем пользователя, внутренний хост или что-то похожее на учётные данные. Отказ у него жёсткий: код возврата 2 и никакого файла. Я сделал так потому, что предупреждение в час ночи пролистывают, а картинка остаётся в чужой ленте.
У гарда 11 правил и 171 тест и все они проходят. Но каждый случай в этих тестах написан для этих тестов. Пример в README это файл на Java, который я придумал так, чтобы в нём было ровно 5 утечек. Гард ни разу не видел кода, который писал не я, поэтому я запустил его на чужом коде.
Я взял codecard с GitHub на последнем коммите, f10a09e, на Python 3.12. Рядом положил 9 публичных репозиториев на их последнем коммите до 1 октября. Это 8 языков от Java до Rust, среди них spring-petclinic и vite. Из каждого репозитория я взял до 300 файлов. Из каждого файла взял 2 окна по 15 строк: начало файла и одно случайное окно из остального. Второе окно примерно то, что я копирую, когда хочу показать функцию.
Как часто он отказывает публичному коду
Всё в этом корпусе уже опубликовано, так что каждый отказ здесь это отказ коду, который не секрет. На случайных окнах из середины файла он отказал в 17 случаях из 790, это 2,2%. На первых строках файлов он отказал в 138 из 1200. 132 из них это файлы C# в MediatR, где namespace стоит в первых 15 строках. По его же определению гард тут прав: namespace действительно называет кодовую базу. Эта база публичная и для такого случая есть --guard off.
Часть из 17 это шум. В express он отказал тесту с URL /users/0-2. Правило путей ищет среди прочего /Users/ на macOS и /home/ на Linux. Но codecard компилирует все правила по умолчанию с re.I, поэтому маршрут в нижнем регистре тоже подходит. По всем файлам корпуса правило путей срабатывает на 107 строках и 23 из них только из-за этого флага. В okhttp он отказал импортам org.robolectric и org.bouncycastle. У правила приватных импортов есть белый список из 26 имён групп, а любое другое имя оно считает вашим собственным кодом.
2,2% это цена, которую я принял в августе вслепую. Теперь я знаю число. Отказ стоит мне одного --guard off. С флагом регистра другое дело, его надо чинить.
Строка, которую он обещал ловить
Потом я прогнал гард по строке объявления каждого файла, без выборки и по одной строке. Java дала 106 пойманных из 106, C# дал 133 из 133. Kotlin дал 0 из 569.
Правилу нужна точка с запятой в конце строки пакета, а Kotlin её там не ставит. Когда правило срабатывает, оно печатает причину “a Java or Kotlin package name is your company and product, spelled out”, то есть имя пакета Java или Kotlin это название вашей компании и продукта открытым текстом. Эту фразу и регулярку я писал, глядя на файл Java. Тесты к ним тоже на Java.
Это хуже, чем одна пропущенная строка. Я взял пример с утечками из README и переписал его на Kotlin. Гард всё равно отказывает, с 4 находками: импорт, ключ, хост и путь. Пакета в списке нет. Когда я исправляю 4 вещи, о которых он сказал, карточка рисуется с package com.acmebank.ledger.settlement в первой строке и кодом возврата 0. Тот же файл с точкой с запятой даёт код 2. В час ночи список выглядит полным, так что я бы исправил эти 4 и опубликовал.
Рядом со сканером секретов
Потом я захотел узнать, что гард пропускает. Настоящие утечки я посчитать не могу, поэтому составил список из 31 формы утечки, какие бывают при вставке из редактора или в логе. Я прогнал их через codecard и через gitleaks 8.30.1, сканер, который я бы поставил на репозиторий. Секреты в списке это случайные строки нужного формата, они генерируются при запуске.
Эти 2 инструмента почти не пересекаются. Из 18 форм, которые выдают кодовую базу, codecard поймал 10, а gitleaks ни одной. Это справедливо: gitleaks сделан для секретов. Из 13 секретов gitleaks поймал 12, а codecard 6. Его правило для учётных данных видит значение только в кавычках после имени вроде api_key или password. Строка из файла .env рисуется. Рисуются и пароль в YAML без кавычек и заголовок Authorization: Bearer в логе и URL вебхука Slack. Вот это немного обидно. Тему ember я сделал для упавших логов, а в логе токен Bearer как раз стоит без кавычек.
Вместе они поймали 23 из 31. Все 8, которых не поймал ни один из них, выдают кодовую базу. Среди них пакет Kotlin и внутренний хост без https:// в начале. Труднее всего хост на собственном домене компании и я не представляю, как его ловить, не зная самой компании. В README уже написано, что гард не знает, как называется ваша компания.
Первая строка README
Установка в README начинается с pip install codecard. Я запустил её в чистом virtualenv и pip не нашёл ни одного дистрибутива. Имя на PyPI вообще не зарегистрировано. Выходит, README инструмента, который существует ради защиты от утечек, предлагает ставить имя, которое может зарегистрировать кто угодно. Так было с середины августа, а я не замечал.
Чего я не проверял
31 форма это мой собственный список, поэтому он говорит, какие формы проходят, а не как часто люди их вставляют. Корпус это 9 репозиториев и 1 случайное зерно. Другой размер окна мог бы сдвинуть 2,2%. trufflehog я не запускал. И я ещё ничего не починил. Точка с запятой, флаг регистра, секреты без кавычек и имя на PyPI так и остаются открытыми в codecard на f10a09e.