Сделал маленькое окно, которое проверяет ссылку, не открывая её, и честно говорит, чего не проверило


Иногда хочется быстро проверить одну вещь: ссылку из письма «вы выиграли», скачанный архив или процесс, из-за которого шумит вентилятор, хотя ничего не запущено.
Готовые способы есть. VirusTotal бесплатно прогоняет файл и ссылку через 70 с лишним антивирусов, но файл уходит на сервис, а результаты, по его же документации, видят партнёры и платные клиенты. Cloudflare URL Scanner открывает страницу в своём браузере и делает снимки, но ему нужен токен API, а отчёт по умолчанию публичный.
Я сделал Фрешко: маленькое окно 400 × 640 точек с мятным питомцем и тремя действиями. Файлы проверяются на компьютере, ссылка проверяется без открытия страницы. Мак и Windows, бесплатно, открытый код. Сразу о главном: это не антивирус. Встроенных правил два, и оба тестовые. Ниже о том, что работает и где проходит граница.

Что умеет
Файл или папка. Считает SHA-256 и прогоняет файлы через правила YARA-X. Можно выбрать файл, папку или весь системный диск. Что пропустил, видно списком.
Ссылка. Идёт по цепочке переадресаций, до 8 адресов и не дольше 25 секунд. Каждый домен сверяет с фильтрующим DNS Cloudflare, который знает вредоносные и фишинговые сайты. Если домен в списке, дальше не идёт и запрос к нему не отправляет.
Майнеры. Смотрит на имена процессов, аргументы (
stratum+tcp,--algo randomx) и запуск из временных папок. Одна только нагрузка на процессор поводом не считается.Ничего не удаляет и не закрывает. Только показывает. Решаете вы.
Питомец здесь не для красоты. Под ним одной фразой написан результат: «Чисто», «Нашёл: 1», «Опасный адрес», «Не дошёл до конца». Подробности спрятаны в раскрывающихся строках.

Цифры
Замерено 01.11.2026 на Маке с Apple Silicon.
Что | Сколько |
|---|---|
Программа для Мака в архиве | 9,4 МБ |
Папка из 4 185 файлов (107 МБ) | 3,5 с, пик памяти ядра 47 МБ |
Проверка ссылки example.com | 0,55 с |
Поиск майнеров | 0,36 с |
Память открытого окна | около 95 МБ: сама программа 37, системный WebKit 58 |
Тестов на Rust | 16, плюс два браузерных сценария на все экраны и размеры окна |
Скорость папки и память ядра замерены командной утилитой из того же репозитория, без окна.

Грабли №1. Проверить домен мало: между проверкой и запросом его могут подменить
Проблема. Схема «спросил фильтрующий DNS, домен чистый, отправляю запрос» дырявая. HTTP-клиент по умолчанию спросит адрес второй раз, уже у системного DNS, и может получить другой ответ. Злой домен может сначала ответить нормальным адресом, а потом указать на 127.0.0.1 или на роутер в домашней сети. Так проверка ссылки превращается в запрос к вашему же роутеру.
Решение. Адреса, которые прошли проверку, закрепляются в HTTP-клиенте, второго вопроса к DNS нет. Системный прокси выключен, переадресации обрабатываются вручную, и каждый новый адрес проверяется заново. Локальные и служебные сети, логин и пароль в ссылке и нестандартные порты запрещены сразу.
let client = Client::builder()
.no_proxy()
.redirect(Policy::none())
.referer(false)
.resolve_to_addrs(url.host_str().unwrap_or_default(), &addrs)
.build()?;
Сам фильтрующий DNS тоже закреплён на адресах 1.1.1.2 и 1.0.0.2, чтобы системный резолвер не участвовал вообще.

Грабли №2. Нагрузка на процессор — это не майнер
Проблема. Первое, что приходит в голову: «процесс ест 100 % процессора, значит, майнер». Но так же ведут себя компилятор, игра и рендер видео.
Решение. Баллы за признаки. Имя известного майнера даёт 85, адрес пула stratum+tcp в аргументах 55, алгоритм майнинга 25. Высокая нагрузка добавляет баллы только тогда, когда уже есть другой признак. Порог 50. Есть тест, который это сторожит:
#[test]
fn high_cpu_alone_is_not_an_incident() {
let finding = score(Observation {
pid: 10,
name: "rustc".into(),
command: "rustc main.rs".into(),
executable: "/usr/bin/rustc".into(),
cpu_percent: 400.0,
});
assert!(finding.is_none());
}

Грабли №3. «Весь диск» никогда не весь
Проблема. У проверки есть лимиты: до 256 МБ на файл и до 20 000 файлов за раз. Часть системных папок закрыта правами. В одной из проверок программа прошла 19 992 файла и 8 пропустила. Если в такой ситуации питомец радостно скажет «Чисто», это будет враньё.
Решение. Пропуск считается отдельным исходом. Питомец радуется, только когда нет ни находок, ни пропусков, ни ошибок. Иначе он пишет «Чисто, но не всё» и показывает список того, что не проверил.
const skipped = report.skipped + report.issues.length > 0;
if (report.findings.length) return { title: `Нашёл: ${report.findings.length}`, note: "Файлы не тронуты, решать тебе", mood: "alert" };
if (skipped) return { title: "Чисто, но не всё", note: `Пропущено: ${number(report.skipped || report.issues.length)}`, mood: "alert" };
return { title: "Чисто", note: `${plural(report.scanned, "файл", "файла", "файлов")} за ${seconds(report.duration_ms)}`, mood: "clean" };
С ссылками так же: если сайт не ответил или DNS был недоступен, результат «Не дошёл до конца», а не «всё хорошо».

Грабли №4. Первая версия была панелью управления на семь разделов
Проблема. Сначала я сделал большое окно 1180 × 760: боковое меню, обзор, журнал, счётчики и раздел «Карантин» с пометкой «скоро». На три действия получилось слишком много текста, а пустой раздел выглядел как обещание, которого программа не выполняет.
Решение. Переделал в окно 400 × 640. Наверху питомец и одна фраза, ниже три строки: файл, ссылка, майнеры. Заглушку карантина убрал совсем. Последний результат каждой проверки виден прямо на главном экране, вместо подсказки под названием.

Что хранится
Одна настройка «Живой Фрешко» (двигается ли питомец) в хранилище окна.
Результаты проверок живут в памяти, пока открыто окно, и никуда не записываются.
В сеть уходит только проверка ссылки, и только по кнопке. DNS Cloudflare узнаёт домены из цепочки, сайты получают обычный запрос и видят ваш IP. Cookie и Referer не отправляются. Одноразовые ссылки из писем лучше не проверять: запрос может их «потратить».
Аналитики, аккаунтов и рекламы нет.
Чем отличается
Проверено на сайтах 01.11.2026.
Фрешко | VirusTotal | Cloudflare URL Scanner | |
|---|---|---|---|
Цена | бесплатно | бесплатно для некоммерческого использования | нужен аккаунт и токен API |
Где проверяется файл | на вашем компьютере | на сервисе | файлы не проверяет |
Открывает страницу ссылки | нет | — | да, в своём браузере |
Кто видит результат | только вы | партнёры и платные клиенты сервиса | по умолчанию все, отчёт публичный |
Глубина | 2 тестовых правила и DNS-фильтр | 70+ антивирусов | страница, её запросы, снимки |
Эти сервисы проверяют гораздо глубже, и для серьёзного случая я бы пошёл к ним. Фрешко про другое: быстрый первый взгляд, при котором ни файл, ни ссылка не уходят в чужой отчёт.
Чего не умеет
Правил два, и оба тестовые: безвредный файл EICAR и следы майнера XMRig. Это не антивирусная база.
Нет фоновой защиты: программа проверяет только то, что попросили.
Нет карантина: найденный файл остаётся на месте.
В ссылках не смотрит JavaScript, содержимое страницы и формы ввода пароля. «Известных угроз нет» не значит «сайт честный».
Не заменяет встроенную защиту Windows и macOS.
Программа не подписана платными сертификатами Apple и Microsoft, поэтому при первом запуске система предупредит. Как открыть, написано в README. Windows-версию собирает GitHub Actions, на живом Windows я её пока не запускал.
Ссылки
Установщики для Мака и Windows: https://github.com/kozliq/freshko/releases/latest
Будет ещё.
Раньше в серии: Качалка — видео без водяных знаков, «Чисто» — без рекламы, Кружок — кружок из любого видео, Толково — голосовой ввод без интернета, Мимо — пропуск рекламных вставок в TikTok, <Порядок — ссылка, когда выйдет> — раскладчик файлов.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.