Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
nullorigin — Инструментарий для проверки устойчивости ИИ-водяных знаков только для исследовательских целей: локальный обратный прокси удаляет C2PA/EXIF/XMP, Unicode, стеганографию изображений/аудио, метаданные OOXML/PDF и сканирует на Trojan Source. | Kitploit
Инструменты/GitHubGitHub/rakib-nyc/nullorigin
Статический анализ кода (SAST)Веб-прокси и перехватСтеганографияКонфиденциальностьОбучение и ОбразованиеБезопасность ИИСостязательная Атака
GitHubrakib-nyc/nullorigin

nullorigin

Инструментарий для проверки устойчивости ИИ-водяных знаков только для исследовательских целей: локальный обратный прокси удаляет C2PA/EXIF/XMP, Unicode, стеганографию изображений/аудио, метаданные OOXML/PDF и сканирует на Trojan Source.

Репозиторий
43 дней назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

NullOrigin 🛡️

Универсальное промежуточное ПО для санитизации провенанса и водяных знаков ИИ Научный артефакт — только для оценки устойчивости водяных знаков.

CI License: Apache 2.0 Python 3.10+ Tests Code style: ruff


🔬 Только для исследовательских целей

NullOrigin — это научный артефакт. Он публикуется для поддержки академического и независимого изучения устойчивости водяных знаков и ни для каких других целей.

Схемы водяных знаков — это заявления о безопасности, а заявления о безопасности имеют смысл только после того, как кто-то попытался их взломать. Литература, которую реализует этот проект — Кирхенбауэр и др. по KGW, Кришна и др. по атакам парафразом, Бушер и Андерсон по Trojan Source — существует потому, что исследователи публиковали работающие атаки, чтобы защитники могли измерять реальную устойчивость, а не предполагать её. Именно к этой традиции относится данный репозиторий.

Предполагаемое использование

  • Измерение того, насколько снижается обнаруживаемость водяного знака при заявленной атаке
  • Воспроизведение и расширение опубликованных результатов об устойчивости
  • Оценка утечки метаданных провенанса в ваших собственных документах перед их публикацией
  • Аудит исходного кода на предмет атак Trojan Source и гомоглифов (CVE-2021-42574, CVE-2021-42694)
  • Обучение тому, как работают эти схемы и где они сохраняют устойчивость

Не предполагается и не поддерживается

  • Выдача сгенерированной работы за свою там, где человек или организация попросили вас этого не делать
  • Подавление сигналов провенанса на контенте, который вы намерены исказить
  • Удаление атрибуции с работы, которая вам не принадлежит
  • Обход любых платформенных, лицензионных или академических политик добросовестности

Ничто здесь не является техническим ограничением на выполнение кода. Это заявление об условиях, на которых он предоставляется, и о том, что его автор будет и не будет поддерживать. Программное обеспечение предоставляется «КАК ЕСТЬ», без каких-либо гарантий — см. LICENSE.

Прочтите Область применения и честные ограничения, прежде чем делать какой-либо вывод из числа, которое печатает этот инструмент. Некоторые из схем, на которые он направлен, невозможно проверить с помощью публичного детектора, и в README об этом сказано прямо, а не иносказательно.


Содержание

  • Только для исследовательских целей
  • Область применения и честные ограничения
  • Теоретические основы
  • Установка
  • Быстрый старт
  • Docker
  • Развёртывание за пределами localhost
  • CLI
  • Бенчмаркинг
  • Семантическая точность
  • Конфигурация
  • Поиск и устранение неполадок
  • Архитектура
  • Тестирование
  • Цитирование и повторное использование
  • Статус проекта
  • Ссылки

⚠️ Область применения и честные ограничения

Прочтите это, прежде чем делать выводы из любого числа, которое печатает этот инструмент.

О детекторе KGW

KGWStatisticalDetector — это математически точная, самосогласованная реализация схемы зелёного/красного списка Кирхенбауэра и др. по токенам пробелов. Это не декодер для продакшн-водяных знаков какого-либо вендора — те основаны на приватном секрете и собственном BPE-словаре модели.

Его цель — сделать бенчмарк реальным: KGWWatermarkEmbedder внедряет настоящий водяной знак, конвейер атакует его, а соответствующий детектор измеряет фактическое снижение. Это истинное измерение атаки на данную схему. Оно не переносится на водяной знак вендора.


📖 Теоретические основы

Текст — зелёный/красный список KGW

Словарь $V$ разбивается на каждом шаге $t$ хэшем, зависящим от предшествующего контекста:

$$s_t = \text{Hash}(w_{t-k}, \dots, w_{t-1})$$

на зелёный список $G_t$ размера $\gamma|V|$ и красный список $R_t$. Смещение $\delta > 0$ добавляется к зелёным логитам:

$$\tilde{l}{t,v} = \begin{cases} l{t,v} + \delta, & v \in G_t \\ l_{t,v}, & v \in R_t \end{cases}$$

Обнаружение подсчитывает попадания зелёного. При $H_0$ они распределены как $\text{Binomial}(T, \gamma)$, поэтому:

$$z = \frac{|S_G| - \gamma T}{\sqrt{T\gamma(1-\gamma)}}$$

при $z > 4.0$ ($p < 3\times10^{-5}$) помечается как синтетическое.

Почему атака парафразом работает: водяной знак живёт целиком в локальных n-граммных переходах. Переписывание поверхностной формы немеченой моделью пересоздаёт хэш на каждой позиции. Это стандартная атака на устойчивость в литературе о водяных знаках.

Почему важна длина: $z$ растёт как $\sqrt{T}$. Отрывок из 100 токенов при доле зелёного 0.70 достигает лишь $z \approx 3.9$ — ниже порога. Для обнаружения нужны несколько сотен токенов, и поэтому осмысленные бенчмарк-фикстуры тоже должны быть такими.

Изображение — C2PA и частотное встраивание

  • C2PA / JUMBF: подписанные манифесты в сегментах JPEG APP11, чанках PNG tEXt/iTXt или блоках WebP/AVIF c2pa. Поскольку подпись покрывает данные пикселей, перекодирование из буфера сырых сэмплов удаляет её без разбора JUMBF вообще.
  • SynthID-Image: сдвиги коэффициентов DCT/DWT. Атакуется мягким пороговым ограничением высокочастотных детализирующих поддиапазонов.
  • Tree-Ring: циклически симметричный паттерн в начальном латентном пространстве диффузии, сохраняющийся как когерентная структура в фазе Фурье. Атакуется рандомизацией фазы в среднечастотных кольцах с сохранением амплитуды — именно поэтому изображение остаётся визуально идентичным.

Аудио — AudioSeal / SynthID-Audio

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


📦 Установка

Требования

Python3.10, 3.11 или 3.12
ОСLinux, macOS (Intel и Apple Silicon), Windows через WSL2
НеобязательноOllama или любой сервер, совместимый с OpenAI —

Из исходного кода```bash

git clone https://github.com/rakib-nyc/nullorigin.git cd nullorigin

python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate

pip install -e .

root@kitploit:~
### Необязательные дополнения```bash
pip install -e ".[dev]"       # pytest, pytest-asyncio, ruff  — needed to run the tests
pip install -e ".[nli]"       # torch + sentence-transformers, for the fidelity gate
pip install -e ".[metrics]"   # torch, transformers, sentence-transformers
pip install -e ".[llama]"     # llama-cpp-python for in-process GGUF inference
pip install -e ".[dev,metrics]"

Без [nli] проверка достоверности основана только на инвариантах — это всё ещё реальная проверка, но нечувствительна к подмене ролей. См. Семантическая достоверность.

Проверка установки```bash

nullorigin --version nullorigin --help pytest -q # requires the [dev] extra

root@kitploit:~
---

## 🚀 Быстрый старт

### 1. Настройка локальной модели переписывания

Для удаления водяных знаков из текста требуется локальная модель без водяных знаков. Без неё NullOrigin удаляет невидимые символы, но **оставляет статистический водяной знак нетронутым** — и сообщает об этом.```bash
ollama serve                    # in a separate terminal
ollama pull llama3.2:3b         # or any instruct model you prefer

Используете другую модель? Направьте NullOrigin на неё:```bash export NULLORIGIN_PARAPHRASER_MODEL=qwen3:4b export NULLORIGIN_PARAPHRASER_TIMEOUT=900 # reasoning models are slow

root@kitploit:~
### 2. Запустите прокси```bash
nullorigin run

I don't see any content in the INPUT section. Please provide the actual Markdown text you'd like me to translate.```console NullOrigin 1.0.0 — proxy listening on 127.0.0.1:8080 providers: anthropic, gemini, openai text engine: unicode=True backend=ollama media: metadata=True stego=True telemetry: open (loopback) health: http://127.0.0.1:8080/health

root@kitploit:~
### 3. Укажите вашему клиенту на него```python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8080/v1", api_key="your-upstream-api-key")

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Write an essay about privacy."}],
    extra_headers={"x-nullorigin-provider": "openai"},
)
print(response.choices[0].message.content)

Anthropic:```python from anthropic import Anthropic

client = Anthropic(base_url="http://localhost:8080", api_key="your-upstream-api-key") message = client.messages.create( model="claude-sonnet-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Write an essay about privacy."}], extra_headers={"x-nullorigin-provider": "anthropic"}, )

root@kitploit:~
curl:```bash
curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "x-nullorigin-provider: openai" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}'

Потоковая передача (SSE) и Gemini (/v1beta/models/...) обрабатываются одинаково. Заголовок x-nullorigin-provider выбирает вышестоящий сервер и удаляется перед пересылкой; ваши заголовки аутентификации передаются без изменений.


🐳 Docker```bash

git clone https://github.com/rakib-nyc/nullorigin.git cd nullorigin

docker compose up -d docker compose exec ollama ollama pull llama3.2:3b # first run only curl http://localhost:8080/health

root@kitploit:~
Стек Compose запускает NullOrigin плюс сайдкар Ollama в частной bridge-сети.
Контейнер-прокси привязывается к `0.0.0.0` — корректно внутри контейнера — и только порт 8080 публикуется на вашем хосте.

Автономный образ:```bash
docker build -t nullorigin:1.0.0 .
docker run -d -p 8080:8080 \
  -e NULLORIGIN_PARAPHRASER_BACKEND=none \
  nullorigin:1.0.0

Полезные команды:```bash docker compose logs -f nullorigin docker compose down # stop docker compose down -v # stop and delete the Ollama model volume

root@kitploit:~
## 🔒 Развертывание за пределами localhost

**По умолчанию NullOrigin использует `127.0.0.1` и отказывается привязываться к публичному интерфейсу без токена телеметрии.** Он передаёт учётные данные вашего вышестоящего API, поэтому это сделано намеренно:```console
$ nullorigin run --host 0.0.0.0
Error: Refusing to bind 0.0.0.0 without a telemetry token.

Choose one:
  - bind loopback:    nullorigin run --host 127.0.0.1
  - set a token:      export NULLORIGIN_TELEMETRY_TOKEN=$(openssl rand -hex 32)
  - accept the risk:  nullorigin run --host 0.0.0.0 --allow-public-bind

Чтобы правильно выставить это:```bash export NULLORIGIN_TELEMETRY_TOKEN=$(openssl rand -hex 32) nullorigin run --host 0.0.0.0 --port 8080

root@kitploit:~
затем поместите его за nginx, Caddy или Traefik, обеспечивающими **терминирование TLS**, **ограничение
скорости** и **слой аутентификации**.

### Модель угроз

NullOrigin — это **локальный обратный прокси, который передаёт учётные данные вашего вышестоящего API**. Этот
единственный факт определяет его модель безопасности.

| Контроль | По умолчанию | Почему |
| --- | --- | --- |
| Адрес привязки | `127.0.0.1` | Только loopback; публичная привязка отклоняется, если не задан токен телеметрии или не передан `--allow-public-bind`. |
| `/telemetry`, `/telemetry/reset` | Открыт на loopback | Доступ ограничен заголовком `X-NullOrigin-Token`; сравнение выполняется за постоянное время, когда задан `proxy.telemetry_token`. |
| `/health` | Всегда открыт | Нужен для проверок контейнеров; раскрывает версию и включённые движки, без секретов. |
| Размер тела запроса | 100 MiB | Прокси буферизует тела для пересылки; более крупный ввод отклоняется с `413`. |
| Буфер SSE | 1 MiB | Если вышестоящий сервер никогда не завершает кадр, он сбрасывается, а не буферизуется бесконечно. |
| Пользователь контейнера | не root | Прокси не требует повышенных привилегий. |

Известные ограничения — по замыслу, а не дефекты:

* **Нет TLS.** Он пересылает `Authorization` и `x-api-key` как есть по обычному HTTP. Помещайте
  его за обратным прокси, который завершает HTTPS в любой ненадёжной сети.
* **Нет аутентификации на пути прокси.** Любой, кто может добраться до порта, может проксировать
  через него, используя собственные учётные данные — NullOrigin не хранит и не внедряет ключи.
* **Нет ограничения скорости.** Применяйте его на обратном прокси.
* **Учётные данные никогда не сохраняются.** Ни один API-ключ не записывается на диск или в журналы; телеметрия
  считает только запросы и события санитизации.
* Проверка TLS вышестоящего сервера остаётся включённой, а редиректы не выполняются.

Чтобы сообщить о проблеме безопасности, напишите на **[email protected]** с `[NullOrigin Security]` в
теме письма.

Телеметрия с установленным токеном:```bash
curl -H "X-NullOrigin-Token: $NULLORIGIN_TELEMETRY_TOKEN" http://localhost:8080/telemetry

/health никогда не ограничивается доступом, поэтому пробы контейнеров продолжают работать.


🛠️ CLI```bash

nullorigin run [--host H] [--port P] [--config FILE] [--allow-public-bind] nullorigin purge INPUT -o OUTPUT [--verify] [--no-paraphrase] [--flatten-typography] nullorigin inspect INPUT [--json] nullorigin benchmark [--section text|media|audio] [-o report.json] nullorigin build-datasets [--root DIR] nullorigin test [pytest args...]

root@kitploit:~
### Поддерживаемые форматы

| Тип | Расширения | Примечания |
| --- | --- | --- |
| **Изображения** | `.png` `.jpg` `.jpeg` `.jfif` `.webp` `.tif` `.tiff` `.bmp` `.gif` `.ico` `.avif` `.jp2` | Все режимы PIL (RGB, RGBA, L, LA, P, 1, I;16, CMYK, YCbCr). Анимированные GIF/WebP и многостраничные TIFF сохраняют все кадры и их тайминги. Изображения меньше 64px сохраняют точные размеры. |
| **Аудио** | `.wav` `.wave` | 8/16/32-битные целые, 32-битные float; от моно до многоканальных; любая частота дискретизации. Файлы нулевой длины проходят полный цикл без изменений. |
| **Документы** | `.docx` `.docm` `.dotx` `.pptx` `.pptm` `.xlsx` `.xlsm` | Все три диалекта OOXML. Текстовые фрагменты очищаются в теле, верхних и нижних колонтитулах, сносках, примечаниях и общих строках; метаданные `docProps` удаляются; все остальные части копируются байт-в-байт. |
| **PDF** | `.pdf` | Словарь `/Info`, XMP-пакет, встроенные вложения и JavaScript удаляются; страницы, текст и геометрия сохраняются. Файлы, защищённые паролем, отклоняются. См. предупреждение ниже. |
| **Исходный код** | `.py` `.js` `.ts` `.go` `.rs` `.java` `.c` `.cpp` `.rb` `.php` `.sh` `.sql` + ещё 50 | Сканирование на Trojan Source и гомоглифы. **Без NFKC, без перефразирования** — см. ниже. |
| **Текст** | всё остальное, что можно декодировать | UTF-8, UTF-8 BOM, UTF-16, UTF-32, CP1252, Latin-1 — определяется автоматически и **записывается обратно в той же кодировке**. |

Всё остальное **отклоняется с конкретными указаниями**, а не читается как UTF-8 и
не повреждается — для `.mp3` указывается `ffmpeg -i in.mp3 out.wav`, для устаревших
`.doc`/`.ppt`/`.xls` — повторное сохранение в OOXML. Отклонённый файл никогда не даёт результата.

Проверено на корпусе из 69 файлов, охватывающем все перечисленные выше форматы: **59 обработаны
корректно, 10 отклонены чисто, ноль сбоев, ноль повреждённых результатов.**

### Код не передаётся переписывателю

Ответы ассистента смешивают прозу и код в одной строке. Если передать всё целиком
модели перефразирования, код будет переписан вместе с прозой — и z-показатель в любом
случае снижается, так что ничего ниже по конвейеру этого не заметит.

Поэтому ответы разбиваются на сегменты до того, как что-либо переписывается:

| Сегмент | Обработка |
| --- | --- |
| Проза | Очищается от Unicode-символов, затем переписывается |
| Ограждённые блоки (``` и ~~~) | Невидимые и двунаправленные символы удаляются. **Без NFKC, никогда не переписываются.** |
| Встроенные `` `code` `` фрагменты | То же самое |

Это работает и в потоковом режиме, где ограждение открывается в одной дельте и
закрывается несколькими дельтами позже. Дельта, пересекающая границу, разбивается по
строкам, поэтому закрывающие ``` и следующая за ними проза обрабатываются по-разному.
Незакрытое ограждение отказывает безопасно: остаток защищается, а не переписывается.

Отключить можно параметром `text.protect_code_blocks: false`, если нужно прежнее поведение.

### Файлы исходного кода: сканирование безопасности, а не удаление водяных знаков

**В исходном коде, сгенерированном ИИ, нет водяного знака.** Ни один провайдер не
помечает выходной код водяными знаками, и не существует публичного детектора. Любой,
кто утверждает, что удаляет его, продаёт вам что-то.

А вот что у исходного кода *действительно* есть — так это реальная, описанная в
публикациях поверхность атаки:

* **Trojan Source** ([CVE-2021-42574](https://nvd.nist.gov/vuln/detail/CVE-2021-42574),
  Boucher & Anderson 2021) — двунаправленные управляющие символы меняют порядок
  *отображения* кода, не изменяя того, как он *компилируется*. Рецензент одобряет одну
  программу; компилятор собирает другую.
* **Гомоглифные идентификаторы** ([CVE-2021-42694](https://nvd.nist.gov/vuln/detail/CVE-2021-42694))
  — кириллическая `а` вместо латинской `a` создаёт два имени, которые отображаются
  одинаково.```console
$ nullorigin purge auth.py -o auth_clean.py --verify
Scanning source file auth.py...
  bidi controls removed:   4
  invisible chars removed: 0

  TROJAN SOURCE DETECTED (CVE-2021-42574): 4 bidirectional control character(s).
  This file rendered differently than it compiled. Review the diff.

Findings:
  CRITICAL line 3:25  U+202E RIGHT-TO-LEFT OVERRIDE — reorders displayed text
      if access_level != "user‮ ⁦// Check if admin⁩⁦":

После очистки строка читается как if access_level != "user // Check if admin": — «комментарий» всё это время был внутри строки.

Три вещи, которые ветвь кода намеренно не делает, потому что универсальная текстовая ветвь делала все три, и каждая из них — ошибка в исходном коде:

  1. Без NFKC-нормализации. Он переписывает значения строковых литералов: "Hello" становится "Hello", "office" становится "office". Это меняет то, что программа сравнивает, хеширует и передаёт.
  2. Без перефразирования. Отправка кода модели-переписывателю даёт код, который не запускается.
  3. Гомоглифы сообщаются, а не сворачиваются. Сворачивание кириллической а объединяет два идентификатора, которые компилятор в данный момент считает различными, — тихо меняя поведение. Серьёзность MEDIUM только для токенов со смешанным алфавитом (totаl), фактической сигнатуры атаки; слово, целиком написанное другим алфавитом, — это обычный иностранный текст и получает INFO. Включите --fold-homoglyphs-in-code после того, как вы их просмотрели.

inspect --json выводит для каждой находки серьёзность, строку, столбец и кодовую точку, поэтому он встраивается в CI как pre-commit или PR gate.

PDF: что удаляется, а что нет

Удаляется проверяемо: словарь /Info (Author, Title, Subject, Keywords, Creator, Producer, CreationDate, ModDate), XMP-пакет в /Root/Metadata, встроенные вложения файлов и JavaScript уровня документа. Страницы, текст и геометрия страниц сохраняются точно; операция идемпотентна и побайтово стабильна.

Обнаруживается, но НЕ удаляется: невидимые символы внутри потоков содержимого страниц. PDF отрисовывает текст глиф за глифом через специфичную для шрифта кодировку — пробел нулевой ширины в CID-keyed шрифте — это двухбайтовый индекс глифа, а не литеральный U+200B, — поэтому универсальная перезапись скорее испортит макет, чем очистит его. inspect сообщает количество; purge печатает предупреждение, а не молчит, потому что молчание было бы воспринято как «их не было». Чтобы удалить их, извлеките текст, запустите на нём nullorigin purge и перегенерируйте PDF.

--strip-annotations доступен, но по умолчанию выключен: аннотации включают ссылки и поля форм, а не только комментарии, поэтому их удаление меняет поведение документа.

Типографика — это не водяной знак

Длинные тире, типографские кавычки и многоточия — это обычный вывод текстовых процессоров. NullOrigin сохраняет их по умолчанию и сообщает о них отдельно от настоящих находок, потому что их нормализация ухудшает документ, ничего не очищая. Используйте --flatten-typography, если вам нужен именно ASCII-вывод.

Межалфавитные конфасабли — другое дело: кириллическая о в середине английского слова не имеет легитимного применения, — и они сворачиваются по умолчанию.

--verify сообщает измерения до/после, а не утверждает успех:```console $ nullorigin purge article.txt -o clean.txt --verify Cleaning text structure and token transitions in article.txt... removed 14 invisible characters, folded 3 homoglyphs applying semantic restructuring via ollama backend... restructuring complete Saved clean text to clean.txt

Verification (KGW statistical detector): z-score before: +5.3021 (p=5.73e-08) z-score after: +0.8874 (p=0.187) detected before/after (z>4.0): True -> False

root@kitploit:~
Если бэкенд недоступен, это сообщается как предупреждение в stderr — бесшумный fallback выглядел бы неотличимо от успешной санитизации.

---

## 📊 Бенчмаркинг```bash
nullorigin build-datasets
nullorigin benchmark

Каждое значение измеряется на месте: текст помечается водяным знаком с помощью KGWWatermarkEmbedder, прогоняется через реальный конвейер и повторно оценивается соответствующим детектором. Раннер завершается с ненулевым кодом возврата, если пороговые значения не достигнуты, и объясняет причину.

Пороговые значения (из директивы проекта):

МетрикаЦелевое значение
z-оценка после санитизации$\lvert z\rvert \le 1.5$
Семантическое сходство$\ge 0.92$
SSIM изображения$\ge 0.95$
PSNR изображения$\ge 36$ dB

Измеренные результаты

Полный текстовый раздел на основе datasets/text/watermarked_kgw.json, переписанный через Ollama (qwen3:4b) на MacBook серии M (~150 s на отрывок):```text sample z_before z_after reduced detected kgw_000 4.212 -0.065 4.277 no kgw_001 5.297 0.484 4.813 no kgw_002 6.120 -0.482 6.601 no kgw_003 4.711 1.271 3.440 no kgw_004 5.696 0.209 5.486 no invisible_payload -0.447 1.091 -1.538 no

mean z: 4.2647 -> 0.4182 max |z| after: 1.271 (target: <= 1.5) still detected at z > 4.0: 0 of 5 invisible chars remaining: 0

pass_z_threshold: PASS pass_no_detection: PASS pass_unicode_purge: PASS OVERALL: PASS (3/3)

root@kitploit:~
Каждый образец с водяным знаком перешёл из обнаруженных в необнаруженные. Обратите внимание на `kgw_003` при z = 1.271 — ниже порога, но ближе всего к нему, что является честной формой этой атаки: она статистическая, а не гарантированная.

Медиана, измеренная по тестовым изображениям:```text
sample                      ssim   psnr_dB  meta_clear
c2pa_tagged.png           0.9950     46.84         yes
exif_tagged.jpg           0.9690     40.54         yes
clean_control.png         0.9951     46.90         yes

Оба порога для изображений пройдены (SSIM ≥ 0.95, PSNR ≥ 36 dB). Значения будут различаться в зависимости от модели, оборудования и фрагмента.

Семантическая точность: падение z-показателя не является доказательством сохранения смысла

Переписывание, изменяющее факт, получает точно такую же оценку, что и верное, на метрике водяного знака. Очевидная проверка этого не работает, как и менее очевидная. Измерено на шести случаях искажений плюс контрольный случай верного переписывания:

Лексическое пересечение инвертировано. Каждая разрушающая смысл правка получила более высокую оценку, чем верное переписывание, потому что хорошая перефразировка имеет мало общих n-грамм с исходником, тогда как искажённая — почти все из них.

Косинусная близость эмбеддингов эту проблему не решает. Три из шести искажённых случаев проходят порог 0.92. "Alice paid Bob" и "Bob paid Alice" — это один и тот же мешок слов, и они набирают 0.985; "must not disable" → "must disable" даёт 0.947. Эмбеддинги предложений кодируют тематическую близость, а не истинность.

Итак, смысловая точность проверяется на двух уровнях, и ни один из них не является косинусом:

  1. Инварианты — числа, количество отрицаний, модальная сила, сила квантора, URL-адреса, адреса электронной почты, идентификаторы, именованные сущности. Проверка детерминированна, не требует внешних зависимостей и объяснима (negation count changed: 1 → 0). Модальные глаголы и кванторы сравниваются по смысловому классу, поэтому may → might проходит, а may → must не проходит. Не видит подмены ролей, когда все сущности сохраняются.
  2. Двунаправленное следование — смысл сохраняется тогда и только тогда, когда каждый текст влечёт другой. Выявляет то, что не под силу подсчёту, включая подмены ролей, а асимметрия диагностична (удаление хеджа дало a⇒b 0.991, b⇒a 0.011). Требуется nullorigin[nli]; без него ограничение сообщается, а не скрывается.

Это шлюз, а не отчёт

Измерение дрейфа постфактум не помогает, если повреждённый текст уже был возвращён. Неудачная проверка повторяет попытку при более низкой температуре — дрейф вызван температурой — а после исчерпания бюджета повторов возвращает оригинал с ok=False и причиной.```yaml text: fidelity: enabled: true max_retries: 2 temperature_step: 0.25 use_nli: true nli_threshold: 0.5

root@kitploit:~
Это также означает, что атака и риск имеют один общий регулятор: повышение температуры снижает z-score *и* повышает скорость дрейфа. Бенчмарк сообщает их вместе, а не как независимые проверки.

### Другие метрики

* **Perplexity** — настоящий GPT-2 PPL с `torch` + `transformers`, в противном случае `unigram_entropy_proxy`, помеченный как приблизительный и **не** сопоставимый с опубликованным PPL.
* **Косинусное сходство** по-прежнему сообщается как `mean_cosine_or_lexical`, только для справки. Оно больше не является критерием прохождения/непрохождения по причинам, указанным в таблице выше.

## ⚙️ Конфигурация

Порядок разрешения, от наименьшего к наибольшему приоритету:

1. Встроенные значения по умолчанию
2. `nullorigin.yaml` (ищется в `./`, `../`, `/app/` или `$NULLORIGIN_CONFIG`)
3. Переменные окружения `NULLORIGIN_*`
4. Явные флаги CLI

### Ключевые настройки

| Параметр | По умолчанию | Примечания |
| --- | --- | --- |
| `proxy.host` | `127.0.0.1` | Петлевой адрес. Привязка к публичному адресу отклоняется без токена телеметрии. |
| `proxy.port` | `8080` | |
| `proxy.default_provider` | `openai` | Используется, когда не отправлен заголовок `x-nullorigin-provider`. |
| `proxy.telemetry_token` | `""` | Защищает `/telemetry` и `/telemetry/reset`. |
| `proxy.max_request_bytes` | `104857600` | 100 МиБ; более крупные тела запросов получают `413`. |
| `text.paraphraser.backend` | `ollama` | `none` \| `ollama` \| `openai_compatible` \| `llama_cpp` \| `lexical`. `none` оставляет статистический водяной знак нетронутым. `lexical` не требует модели, но является гораздо более слабой атакой. |
| `text.clean_unicode` | `true` | Удаление символов нулевой ширины и блока Tags. |
| `text.fold_homoglyphs` | `true` | Кириллические/греческие омоглифы в ASCII. |
| `text.stream_window_tokens` | `40` | Дельта-изменения буферизуются перед перезаписью потокового сегмента. |
| `media.crop_mode` | `trim` | `trim` сдвигает координаты без передискретизации; `resample` восстанавливает точные размеры, но стоит примерно SSIM 0.81 / PSNR 31 дБ даже при обрезке 0.5%; `none` отключает геометрический проход. |
| `audio.low_cut_hz` | `800.0` | Фаза ниже этого значения сохраняется для разборчивости. |

### Переменные окружения```bash
NULLORIGIN_CONFIG                  # path to nullorigin.yaml
NULLORIGIN_HOST                    # bind address
NULLORIGIN_PORT
NULLORIGIN_TELEMETRY_TOKEN
NULLORIGIN_MAX_REQUEST_BYTES
NULLORIGIN_DEFAULT_PROVIDER
NULLORIGIN_PARAPHRASER_BACKEND     # none | ollama | openai_compatible | llama_cpp | lexical
NULLORIGIN_PARAPHRASER_ENDPOINT    # alias: NULLORIGIN_OLLAMA_ENDPOINT
NULLORIGIN_PARAPHRASER_MODEL
NULLORIGIN_PARAPHRASER_MODEL_PATH  # llama_cpp GGUF path
NULLORIGIN_PARAPHRASER_API_KEY
NULLORIGIN_PARAPHRASER_TIMEOUT
NULLORIGIN_PARAPHRASER_TEMPERATURE
NULLORIGIN_CLEAN_UNICODE
NULLORIGIN_FOLD_HOMOGLYPHS
NULLORIGIN_PURGE_METADATA
NULLORIGIN_DISRUPT_STEGO
NULLORIGIN_DISRUPT_AUDIO

🩺 Устранение неполадок

model 'llama3.2:3b' not found Настроенная модель не скачана. Выполните ollama list, чтобы увидеть, какие модели у вас есть, а затем либо ollama pull llama3.2:3b, либо задайте NULLORIGIN_PARAPHRASER_MODEL для модели, которая у вас уже есть.

WARNING: ollama backend unavailable (ReadTimeout) Переписывание превысило text.paraphraser.timeout_seconds (по умолчанию 120 с). Модели рассуждений, такие как qwen3, обычно занимают 150 с+ на абзац на ЦП. Увеличьте значение: export NULLORIGIN_PARAPHRASER_TIMEOUT=900 или используйте меньшую instruct-модель.

nullorigin benchmark завершается с кодом 1 и pass_no_detection: FAIL Работает как задумано. Ни один бэкенд переписывания не был доступен, поэтому выполнился только слой Unicode, а статистический водяной знак сохранился. Запустите Ollama или установите бэкенд lexical для сравнения без внешних зависимостей.

semantic_check: INCONCLUSIVE Ожидаемо без дополнительной опции [metrics]. См. Честность метрик.

Error: Refusing to bind 0.0.0.0 without a telemetry token Это намеренно. См. Развёртывание за пределами localhost.

Multiple top-level packages discovered in a flat-layout Вы используете старую версию из репозитория. pyproject.toml задаёт явный список пакетов; получите последнюю версию.

Асинхронные тесты сообщают UsageError об отсутствующем async-плагине Это сделано намеренно — без него pytest считает тесты async def пройденными, не выполняя их через await. pip install -e ".[dev]".

Docker: curl: (7) Failed to connect сразу после compose up У проверки здоровья (healthcheck) есть стартовый период 10 с. Подождите, затем проверьте docker compose logs nullorigin.


🏗️ Архитектура```text

root@kitploit:~
                Client / Application
                        |
          [http://localhost:8080/v1/...]
                        v
    +===================================================+
    |               NULLORIGIN CORE PROXY               |
    |  HTTP/SSE interceptor · provider schema adapter   |
    |  /health · /telemetry · transparent auth passthru |
    +===================================================+
                        |
          [request forwarded unmodified]
                        v
          Upstream Provider API (Anthropic / OpenAI / Gemini)
                        |
              [watermarked payload]
                        v
    +===================================================+
    |            SANITIZATION PIPELINE ROUTER           |
    +===================================================+
       /                 |                      \

(text/JSON+SSE) (image/*) (audio/wav) v v v +----------------+ +------------------+ +------------------+ | MODULE B: TEXT | | MODULE C: MEDIA | | MODULE D: AUDIO | | unicode purge | | C2PA/EXIF scrub | | phase randomize | | homoglyph fold | | DWT threshold | | notch shifting | | KGW detector | | Fourier phase | | psychoacoustic | | SLM rewriter | | dither | | requantization | +----------------+ +------------------+ +------------------+ \ | / +----------------+---------------------+ v Schema reconstruction (SSE framing preserved) v Sanitized stream / file

root@kitploit:~
### Расположение```text
nullorigin/
├── cli.py                        # run, purge, inspect, benchmark, build-datasets, test
├── config.py                     # Pydantic v2 settings + env overrides
├── proxy/
│   ├── server.py                 # FastAPI reverse proxy, /health, /telemetry
│   ├── interceptors.py           # SSE frame parser + sliding-window rewriter
│   ├── telemetry.py              # thread-safe runtime counters
│   └── schemas.py                # provider request/response models
├── engines/
│   ├── text/
│   │   ├── unicode_cleaner.py    # invisible chars, Tags block, homoglyphs
│   │   ├── paraphraser.py        # pluggable rewrite backends
│   │   └── kgw_detector.py       # detector + Viterbi embedder
│   ├── media/
│   │   ├── c2pa_remover.py       # JUMBF/EXIF/XMP stripping + inspection
│   │   └── stego_breaker.py      # DWT thresholding, Fourier phase, dither
│   └── audio/
│       └── audio_cleaner.py      # phase randomization, notch shifting
└── evaluation/
    ├── metrics.py                # SSIM, PSNR, PPL, semantic similarity
    ├── datasets.py               # deterministic fixture generation
    └── runner.py                 # measured benchmark harness

🧪 Тестирование```bash

pytest -q

root@kitploit:~
384 теста. Набор тестов покрывает парсер SSE-фреймов при неблагоприятных границах фрагментов,
жизненный цикл потоковой передачи через прокси, все режимы изображений PIL, SSIM по сравнению с аналитическим значением
и эталонной реализацией полным перебором, а также обнаруживаемость водяных знаков в наборе данных.

Асинхронные тесты при отсутствии async-плагина завершаются с явной ошибкой, а не молча пропускаются.

---

## 📖 Цитирование и повторное использование

Лицензия Apache-2.0 разрешает использование, изменение и распространение при условии, что
уведомление об авторских правах и указание авторства **Muhammad Rakibul Islam** сохранены. См.
[LICENSE](https://github.com/rakib-nyc/nullorigin/blob/HEAD/LICENSE) и [NOTICE](https://github.com/rakib-nyc/nullorigin/blob/HEAD/NOTICE).

Если эта работа используется в публикации, пожалуйста, цитируйте её следующим образом:```bibtex
@software{islam_nullorigin_2026,
  author  = {Islam, Muhammad Rakibul},
  title   = {{NullOrigin}: Universal AI Provenance and Watermark
             Sanitization Middleware},
  year    = {2026},
  version = {1.2.0},
  url     = {https://github.com/rakib-nyc/nullorigin},
  note    = {Research artifact for watermarking robustness evaluation}
}

This repository is published as a finished research artifact and is not accepting pull request'ы. You are free to fork it under the terms of the licence. Questions and findings are welcome by email at [email protected].


🗺️ Статус проекта

Версия 1.0.0. Набор завершён в соответствии со спецификацией и полностью протестирован, со следующими известными ограничениями:

  • Эффективность против SynthID, Tree-Ring и AudioSeal непроверяема здесь — не существует публичного детектора, чтобы её измерить. Возмущения реализованы; утверждение не делается.
  • Семантический дрейф контролируется инвариантами и опциональным NLI. Без [nli] замены ролей, сохраняющие все сущности, не обнаруживаются, и отчёт об этом говорит.
  • Задержка с локальной моделью рассуждений (~150 с на фрагмент) делает текстовый путь непригодным для интерактивного использования. Меньшая instruct-модель работает намного быстрее.
  • Нет TLS и нет аутентификации запросов — так задумано; запускайте его за обратным прокси.

См. CHANGELOG.md для истории релизов.


📚 Ссылки

  • Kirchenbauer et al., A Watermark for Large Language Models (2023) — схема KGW
  • Dathathri et al., Scalable watermarking for identifying large language model outputs (Nature, 2024) — SynthID-Text
  • Wen et al., Tree-Rings Watermarks: Invisible Fingerprints for Diffusion Images (2023)
  • San Roman et al., Proactive Detection of Voice Cloning with Localized Watermarking (2024) — AudioSeal
  • Krishna et al., Paraphrasing evades detectors of AI-generated text (2023)
  • Wang et al., Image Quality Assessment: From Error Visibility to Structural Similarity (2004) — SSIM
  • Спецификация C2PA

📜 Правовое уведомление и авторство

  • Автор и создатель: Muhammad Rakibul Islam
  • Контакт: [email protected]
  • Лицензия: Apache License, Version 2.0

Этот репозиторий — артефакт исследовательского уровня, выпущенный для статистических исследований, оценки конфиденциальности, сравнительного тестирования устойчивости водяных знаков и проверки криптографической стойкости.```text Copyright 2026 Muhammad Rakibul Islam [email protected]

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

root@kitploit:~
http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

root@kitploit:~
**НИКАКИХ ГАРАНТИЙ.** ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ,
ЯВНЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ.
Скачать инструмент
Юридическое уведомление и атрибуция
УровеньЧто он фактически делает
Невидимые символыПолностью эффективно. Нулевой ширины, bidi-управляющие, селекторы вариантов начертания и полезные нагрузки из блока Unicode Tags удаляются полностью, с выводом счётчика. Гомоглифные конфьюзаблы между письменностями (кириллица/греческий, отображаемые как ASCII) сводятся.
Метаданные документа (.docx)Полностью эффективно. Автор, последний редактор, количество ревизий, временные метки, шаблон и версия приложения очищаются из docProps, с сохранением форматирования побайтово.
C2PA / EXIF / XMPПолностью эффективно. Изображение пересобирается из сырых образцов пикселей в новый контейнер, поэтому подписанные манифесты JUMBF и все метаданные исчезают. Проверено тестами против размеченных фикстур.
Статистический водяной знак KGWПолностью зависит от бэкенда переписывания. Если локальная модель не настроена, статистический водяной знак выживает — инструмент прямо говорит об этом, а не создаёт видимость обратного.
SynthID-Text / SynthID-Image / Tree-RingЗдесь не проверяемо. В них используются приватные ключи и проприетарные декодеры. NullOrigin применяет возмущения, описываемые в литературе, но не утверждается, что они побеждают реальные детекторы, потому что нет публичного детектора, по которому можно было бы измерять.
AudioSeal / SynthID-AudioЗдесь не проверяемо по той же причине.
требуется для удаления водяных знаков из текста
НеобязательноDocker 20.10+ с Compose v2
случайlexical_f1косинус эмбеддинговдвунаправленный NLI
отрицание удалено0.700.77 ✓0.000 ✓
число 5 → 500.820.81 ✓0.000 ✓
подмена сущности/роли0.810.985 ✗0.000 ✓
квантор «все» → «некоторые»0.880.91 ✓0.000 ✓
хедж удалён0.270.953 ✗0.011 ✓
«не должен» → «должен»0.830.947 ✗0.000 ✓
верное переписывание0.330.931 ✓0.998 ✓