Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
nullorigin — Boîte à outils de robustesse des filigranes IA à usage exclusivement de recherche : un proxy inverse local supprime C2PA/EXIF/XMP, Unicode, la stéganographie image/audio et les métadonnées OOXML/PDF, et détecte Trojan Source. | Kitploit
Outils/GitHubGitHub/rakib-nyc/nullorigin
Analyse Statique de Code (SAST)Proxies Web et InterceptionStéganographieProtection de la Vie PrivéeApprentissage et ÉducationSécurité de l'IAAttaque Adversariale
GitHubrakib-nyc/nullorigin

nullorigin

Boîte à outils de robustesse des filigranes IA à usage exclusivement de recherche : un proxy inverse local supprime C2PA/EXIF/XMP, Unicode, la stéganographie image/audio et les métadonnées OOXML/PDF, et détecte Trojan Source.

Voir le dépôt
4il y a 4 joursPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

NullOrigin 🛡️

Middleware universel de provenance IA et de neutralisation de filigranes Artéfact de recherche — uniquement pour l'évaluation de la robustesse des filigranes.

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


🔬 Usage réservé à la recherche

NullOrigin est un artéfact de recherche. Il est publié pour soutenir l'étude académique et indépendante de la robustesse des filigranes, et pour aucune autre fin.

Les filigranes sont des assertions de sécurité, et ces assertions ne prennent de sens que lorsque quelqu'un a tenté de les briser. La littérature que ce projet implémente — Kirchenbauer et al. sur KGW, Krishna et al. sur les attaques par paraphrase, Boucher & Anderson sur Trojan Source — existe parce que les chercheurs ont publié des attaques fonctionnelles afin que les défenseurs puissent mesurer une robustesse réelle plutôt que de la supposer. C'est la tradition à laquelle appartient ce dépôt.

Usages prévus

  • Mesurer à quel point la détectabilité d'un filigrane se dégrade sous une attaque donnée
  • Reproduire et étendre les résultats de robustesse publiés
  • Évaluer la fuite de métadonnées de provenance dans vos propres documents avant de les partager
  • Auditer le code source contre les attaques Trojan Source et homoglyphes (CVE-2021-42574, CVE-2021-42694)
  • Enseigner comment ces mécanismes fonctionnent, et où ils tiennent

Non prévu, et non pris en charge

  • Faire passer un travail généré pour le vôtre lorsqu'une personne ou une institution vous l'a interdit
  • Contourner les signaux de provenance sur un contenu que vous avez l'intention de présenter de manière erronée
  • Supprimer l'attribution d'un travail qui n'est pas le vôtre
  • Contourner toute politique de plateforme, licence ou intégrité académique

Rien ici n'est un contrôle technique sur la manière dont le code s'exécute. C'est une déclaration des conditions auxquelles il est fourni, et de ce que son auteur soutiendra ou non. Le logiciel est fourni « EN L'ÉTAT », sans garantie d'aucune sorte — voir LICENSE.

Lisez Portée et limites honnêtes avant de tirer une conclusion à partir d'un nombre affiché par cet outil. Plusieurs des mécanismes qu'il cible ne peuvent pas être vérifiés auprès d'un détecteur public, et le README le dit plutôt que de laisser entendre le contraire.


Sommaire

  • Usage réservé à la recherche
  • Portée et limites honnêtes
  • Fondements théoriques
  • Installation
  • Démarrage rapide
  • Docker
  • Déploiement au-delà de localhost
  • CLI
  • Analyse comparative
  • Fidélité sémantique
  • Configuration
  • Dépannage
  • Architecture
  • Tests
  • Citation et réutilisation
  • État du projet
  • Références
  • Avis légal et attribution

⚠️ Portée et limites honnêtes

Lisez ceci avant de tirer des conclusions à partir d'un nombre affiché par cet outil.

À propos du détecteur KGW

KGWStatisticalDetector est une implémentation mathématiquement fidèle et cohérente du mécanisme de liste verte/rouge de Kirchenbauer et al. sur les jetons d'espacement. Ce n'est pas un décodeur pour le filigrane de production d'un fournisseur — ceux-ci reposent sur un secret privé et sur le vocabulaire BPE propre au modèle.

Son objectif est de rendre l'analyse comparative réelle : KGWWatermarkEmbedder insère un véritable filigrane, le pipeline l'attaque, et le détecteur correspondant mesure la réduction effective. C'est une mesure réelle de l'attaque contre ce mécanisme. Elle ne se transpose pas au filigrane d'un fournisseur.


📖 Fondements théoriques

Texte — Liste verte/rouge KGW

Le vocabulaire $V$ est partitionné à chaque étape $t$ par un hachage initialisé sur le contexte précédent :

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

en une liste verte $G_t$ de taille $\gamma|V|$ et une liste rouge $R_t$. Un biais $\delta > 0$ est ajouté aux logits verts :

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

La détection compte les occurrences vertes. Sous $H_0$, elles suivent une $\text{Binomiale}(T, \gamma)$, donc :

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

avec $z > 4.0$ ($p < 3\times10^{-5}$) signalé comme synthétique.

Pourquoi la paraphrase l'attaque : le filigrane réside entièrement dans les transitions locales de n-grammes. Réécrire la forme de surface avec un modèle non filigrané réinitialise chaque position. C'est l'attaque de robustesse standard dans la littérature sur les filigranes.

Pourquoi la longueur compte : $z$ croît comme $\sqrt{T}$. Un passage de 100 jetons avec une fraction verte de 0.70 n'atteint que $z \approx 3.9$ — sous le seuil. La détection nécessite quelques centaines de jetons, tout comme des maquettes d'analyse comparative pertinentes.

Image — C2PA et intégration dans le domaine fréquentiel

  • C2PA / JUMBF : des manifestes signés dans les segments JPEG APP11, les blocs PNG tEXt/iTXt, ou les boîtes c2pa WebP/AVIF. Comme la signature couvre les données de pixels, un ré-encodage à partir d'un tampon d'échantillons brut le supprime sans même analyser JUMBF.
  • SynthID-Image : des décalages dans les coefficients DCT/DWT. Attaqué par seuillage doux des sous-bandes de détail haute fréquence.
  • Tree-Ring : un motif à symétrie circulaire dans le latent de diffusion initial, qui survit comme structure cohérente dans la phase de Fourier. Attaqué en randomisant la phase dans des anneaux de fréquences moyennes tout en préservant l'amplitude — c'est pourquoi l'image reste visuellement identique.

Audio — AudioSeal / SynthID-Audio

Modulation de phase sous le seuil et ajouts spectraux de faible amplitude. Attaqué par randomisation de phase au-dessus de la fondamentale de la parole, décalage d'encoches coupe-bande dans les bandes non critiques, et re-quantification psychoacoustique.


📦 Installation

Prérequis

Python3.10, 3.11 ou 3.12
OSLinux, macOS (Intel et Apple Silicon), Windows via WSL2
OptionnelOllama ou tout serveur compatible OpenAI —

Depuis les sources```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:~
### Extras facultatifs```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]"

Sans [nli], la porte de fidélité s'exécute uniquement sur les invariants — un contrôle réel quand même, mais aveugle aux échanges de rôles. Voir Fidélité sémantique.

Vérifier l'installation```bash

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

root@kitploit:~
---

## 🚀 Démarrage rapide

### 1. Configurer un modèle de réécriture local

Le dé-watermarking de texte nécessite un modèle local non filigrané. Sans cela, NullOrigin supprime
les caractères invisibles mais **laisse le filigrane statistique intact** — et le précise.```bash
ollama serve                    # in a separate terminal
ollama pull llama3.2:3b         # or any instruct model you prefer

Vous utilisez un autre modèle ? Pointez NullOrigin dessus :```bash export NULLORIGIN_PARAPHRASER_MODEL=qwen3:4b export NULLORIGIN_PARAPHRASER_TIMEOUT=900 # reasoning models are slow

root@kitploit:~
### 2. Démarrer le proxy```bash
nullorigin run

Please provide the Markdown content 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. Pointez votre client dessus```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"}]}'

Le streaming (SSE) et Gemini (/v1beta/models/...) sont traités de la même manière. L'en-tête x-nullorigin-provider sélectionne le fournisseur en amont et est supprimé avant le transfert ; vos en-têtes d'authentification transitent sans modification.


🐳 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:~
La pile Compose exécute NullOrigin plus un sidecar Ollama sur un réseau ponté privé.
Le conteneur proxy se lie à `0.0.0.0` — correct à l'intérieur d'un conteneur — et seul le port 8080 est
publié vers votre hôte.

Image autonome:```bash
docker build -t nullorigin:1.0.0 .
docker run -d -p 8080:8080 \
  -e NULLORIGIN_PARAPHRASER_BACKEND=none \
  nullorigin:1.0.0

Commandes utiles :```bash docker compose logs -f nullorigin docker compose down # stop docker compose down -v # stop and delete the Ollama model volume

root@kitploit:~
---

## 🔒 Déploiement au-delà de localhost

**NullOrigin est configuré par défaut sur `127.0.0.1` et refuse de se lier à une interface publique sans
jeton de télémétrie.** Il relaie vos identifiants API en amont, c'est donc délibéré :```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

Pour l'exposer correctement :```bash export NULLORIGIN_TELEMETRY_TOKEN=$(openssl rand -hex 32) nullorigin run --host 0.0.0.0 --port 8080

root@kitploit:~
puis placez-le derrière nginx, Caddy ou Traefik assurant la **terminaison TLS**, la **limitation
de débit** et une **couche d'authentification**.

### Modèle de menace

NullOrigin est un **proxy inverse local qui relaie vos identifiants d'API en amont**. Cette unique caractéristique détermine sa posture de sécurité.

| Contrôle | Par défaut | Raison |
| --- | --- | --- |
| Adresse d'écoute | `127.0.0.1` | Boucle locale uniquement ; une liaison publique est refusée sauf si un jeton de télémétrie est défini ou si `--allow-public-bind` est passé. |
| `/telemetry`, `/telemetry/reset` | Ouvert en boucle locale | Protégé par `X-NullOrigin-Token`, comparé en temps constant, dès lors que `proxy.telemetry_token` est défini. |
| `/health` | Toujours ouvert | Les sondes de conteneurs en ont besoin ; expose la version et les moteurs activés, aucun secret. |
| Taille du corps de requête | 100 MiB | Le proxy met en mémoire tampon les corps pour les transférer ; toute entrée plus grande est rejetée avec `413`. |
| Tampon SSE | 1 MiB | Un serveur amont qui ne termine jamais une trame est vidé, pas mis en mémoire tampon indéfiniment. |
| Utilisateur du conteneur | non-root | Le proxy n'a besoin d'aucun privilège élevé. |

Limitations connues, par conception et non des défauts :

* **Pas de TLS.** Il transmet `Authorization` et `x-api-key` tels quels sur HTTP en clair. Placez-le derrière un proxy inverse qui termine HTTPS sur tout réseau non fiable.
* **Aucune authentification sur le chemin du proxy.** Toute personne qui peut atteindre le port peut l'utiliser comme proxy avec ses propres identifiants — NullOrigin ne stocke ni n'injecte de clés.
* **Aucune limitation de débit.** Appliquez-la au niveau du proxy inverse.
* **Les identifiants ne sont jamais persistés.** Aucune clé API n'est écrite sur le disque ou dans les journaux ; la télémétrie compte uniquement les requêtes et les événements de nettoyage.
* La vérification TLS en amont reste activée et les redirections ne sont pas suivies.

Pour signaler un problème de sécurité, envoyez un e-mail à **[email protected]** avec `[NullOrigin Security]` dans l'objet.

Télémétrie avec un jeton défini :```bash
curl -H "X-NullOrigin-Token: $NULLORIGIN_TELEMETRY_TOKEN" http://localhost:8080/telemetry

/health n'est jamais protégé, donc les sondes de conteneur continuent de fonctionner.


🛠️ 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:~
### Formats pris en charge

| Type | Extensions | Notes |
| --- | --- | --- |
| **Images** | `.png` `.jpg` `.jpeg` `.jfif` `.webp` `.tif` `.tiff` `.bmp` `.gif` `.ico` `.avif` `.jp2` | Tous les modes PIL (RGB, RGBA, L, LA, P, 1, I;16, CMYK, YCbCr). Les GIF/WebP animés et les TIFF multipages conservent chaque image et leur timing. Les images de moins de 64 px conservent leurs dimensions exactes. |
| **Audio** | `.wav` `.wave` | Entiers sur 8/16/32 bits, flottant 32 bits ; du mono au multicanal ; n'importe quelle fréquence d'échantillonnage. Les fichiers de longueur nulle survivent à l'aller-retour. |
| **Documents** | `.docx` `.docm` `.dotx` `.pptx` `.pptm` `.xlsx` `.xlsm` | Les trois dialectes OOXML. Les séquences de texte sont assainies dans le corps, les en-têtes, les pieds de page, les notes de bas de page, les notes et les chaînes partagées ; les métadonnées `docProps` sont supprimées ; toutes les autres parties sont copiées octet par octet. |
| **PDF** | `.pdf` | Dictionnaire `/Info`, paquet XMP, pièces jointes et JavaScript supprimés ; pages, texte et géométrie préservés. Les fichiers protégés par mot de passe sont refusés. Voir la mise en garde ci-dessous. |
| **Code source** | `.py` `.js` `.ts` `.go` `.rs` `.java` `.c` `.cpp` `.rb` `.php` `.sh` `.sql` + 50 autres | Analyse Trojan Source et homoglyphes. **Pas de NFKC, pas de paraphrase** — voir ci-dessous. |
| **Texte** | tout le reste décodable | UTF-8, UTF-8 BOM, UTF-16, UTF-32, CP1252, Latin-1 — détecté automatiquement et **réécrit dans le même encodage**. |

Tout le reste est **refusé avec une indication précise** plutôt que lu en UTF-8 et corrompu — `.mp3` renvoie vers `ffmpeg -i in.mp3 out.wav`, les anciens `.doc`/`.ppt`/`.xls` vers un réenregistrement en OOXML. Un fichier refusé ne produit jamais de sortie.

Vérifié sur un corpus de 69 fichiers couvrant tous les formats ci-dessus : **59 traités correctement, 10 refusés proprement, zéro plantage, zéro sortie corrompue.**

### Le code est tenu à l'écart de la réécriture

Les réponses de l'assistant mélangent prose et code dans une seule chaîne. Confier l'ensemble à un modèle de paraphrase réécrit le code en même temps que la prose — et le score z chute dans les deux cas, donc rien en aval ne le remarque.

Les réponses sont donc segmentées avant toute réécriture :

| Segment | Traitement |
| --- | --- |
| Prose | Nettoyée Unicode, puis réécrite |
| Blocs délimités (``` et ~~~) | Caractères invisibles et bidi supprimés. **Pas de NFKC, jamais réécrits.** |
| Segments `` `code` `` en ligne | Idem |

Cela vaut aussi sur le chemin de streaming, où un délimiteur s'ouvre dans un delta et se ferme plusieurs deltas plus tard. Un delta à cheval sur la limite est découpé ligne par ligne, si bien que le ``` de fermeture et la prose qui suit sont traités différemment. Un délimiteur non fermé échoue en toute sécurité : le reste est protégé plutôt que réécrit.

Désactivez avec `text.protect_code_blocks: false` si vous voulez l'ancien comportement.

### Fichiers de code source : une analyse de sécurité, pas une suppression de filigrane

**Il n'y a pas de filigrane dans le code source généré par IA.** Aucun fournisseur ne filigrane la sortie de code, et aucun détecteur public n'existe. Quiconque prétend en retirer un vous vend quelque chose.

Ce que le code source *a* en revanche, c'est une surface d'attaque réelle et documentée :

* **Trojan Source** ([CVE-2021-42574](https://nvd.nist.gov/vuln/detail/CVE-2021-42574), Boucher & Anderson 2021) — les caractères de contrôle bidirectionnels réordonnent la façon dont le code est *affiché* sans changer la façon dont il *compile*. Un relecteur approuve un programme ; le compilateur en construit un autre.
* **Identifiants homoglyphes** ([CVE-2021-42694](https://nvd.nist.gov/vuln/detail/CVE-2021-42694)) — le cyrillique `а` pour le latin `a` crée deux noms qui s'affichent de manière identique.```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⁩⁦":

Après purge, on lit if access_level != "user // Check if admin": — le « commentaire » se trouvait en réalité à l'intérieur de la chaîne.

Trois choses que le chemin de code ne fait volontairement pas, parce que le chemin de texte générique faisait les trois et chacune est un bug sur la source :

  1. Pas de normalisation NFKC. Il réécrit les valeurs des littéraux de chaîne : "Hello" devient "Hello", "office" devient "office". Cela change ce qu'un programme compare, hache, et transmet.
  2. Pas de reformulation. Envoyer du code à un modèle de réécriture produit du code qui ne s'exécute pas.
  3. Les homoglyphes sont signalés, pas repliés. Replier un а cyrillique fusionne deux identifiants que le compilateur traite actuellement comme distincts — changeant silencieusement le comportement. La gravité n'est MEDIUM que pour les tokens à écritures mixtes (totаl), la véritable signature d'attaque ; un mot entièrement écrit dans une autre écriture est un texte étranger ordinaire et reçoit INFO. Activez --fold-homoglyphs-in-code une fois que vous les avez examinés.

inspect --json émet pour chaque résultat la gravité, la ligne, la colonne et le point de code, ce qui lui permet de s'intégrer dans un CI comme étape de validation pre-commit ou PR.

PDF : ce qui est supprimé et ce qui ne l'est pas

Supprimés, de manière vérifiable : le dictionnaire /Info (Author, Title, Subject, Keywords, Creator, Producer, CreationDate, ModDate), le paquet XMP dans /Root/Metadata, les pièces jointes intégrées et le JavaScript au niveau du document. Les pages, le texte et la géométrie des pages sont préservés exactement ; l'opération est idempotente et stable au niveau des octets.

Détectés mais PAS supprimés : les caractères invisibles dans les flux de contenu des pages. Le PDF dessine le texte glyphe par glyphe via un encodage spécifique à la police — un espace de largeur nulle dans une police à clés CID est un index de glyphe sur deux octets, pas un U+200B littéral — donc une réécriture générique corromprait la mise en page au lieu de la nettoyer. inspect signale le nombre ; purge affiche un avertissement plutôt que de rester silencieux, car le silence pourrait être interprété comme « il n'y en avait aucun ». Pour les supprimer, extrayez le texte, exécutez nullorigin purge dessus, puis régénérez le PDF.

--strip-annotations est disponible mais désactivé par défaut : les annotations incluent les liens et les champs de formulaire, pas seulement les commentaires, donc les supprimer change le comportement du document.

La typographie n'est pas un filigrane

Les tirets cadratins, les guillemets courbes et les points de suspension sont une sortie ordinaire de traitement de texte. NullOrigin les préserve par défaut et les signale séparément des résultats authentiques, car les aplatir dégrade un document sans rien assainir. Utilisez --flatten-typography si vous voulez spécifiquement une sortie ASCII.

Les confusables entre écritures sont différents — un о cyrillique au milieu d'un mot dans un texte anglais n'a aucun usage légitime — et ceux-ci sont repliés par défaut.

--verify rapporte des mesures avant/après plutôt que d'affirmer le succès :```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:~
Si le backend est inaccessible, cela est signalé comme un avertissement sur stderr — un repli silencieux
serait identique à un assainissement réussi.

---

## 📊 Benchmarking```bash
nullorigin build-datasets
nullorigin benchmark

Chaque valeur est mesurée sur place : le texte est filigrané avec KGWWatermarkEmbedder, exécuté via le pipeline réel, puis ré-évalué avec le détecteur correspondant. Le runner se termine avec un code non nul lorsque les seuils ne sont pas atteints et explique pourquoi.

Seuils (issus de la directive du projet) :

MétriqueCible
z-score post-assainissement$\lvert z\rvert \le 1.5$
Similarité sémantique$\ge 0.92$
SSIM de l'image$\ge 0.95$
PSNR de l'image$\ge 36$ dB

Résultats mesurés

Section de texte intégral sur datasets/text/watermarked_kgw.json, réécrit via Ollama (qwen3:4b) sur un MacBook série M (~150 s par passage) :```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:~
Chaque échantillon filigrané est passé de détecté à non détecté. Notons `kgw_003` à z = 1.271 — sous le seuil mais le plus proche de celui-ci, ce qui est la forme honnête de cette attaque : elle est statistique, pas une garantie.

Média, mesuré sur les fixtures d'images :```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

Les deux seuils d'image sont satisfaits (SSIM ≥ 0.95, PSNR ≥ 36 dB). Les valeurs varieront selon le modèle, le matériel et le passage.

Fidélité sémantique : une baisse du z-score n'est pas une preuve de préservation du sens

Une réécriture qui modifie un fait obtient exactement le même score qu'une réécriture fidèle sur la métrique du filigrane. La vérification évidente pour cela ne fonctionne pas, et la moins évidente non plus. Mesuré sur six cas de dérive plus un témoin fidèle :

Le chevauchement lexical est inversé. Chaque modification détruisant le sens a obtenu un score plus élevé que la réécriture fidèle, car une bonne paraphrase partage peu de n-grammes avec sa source alors qu'une version corrompue en partage presque tous.

Le cosinus des embeddings ne corrige pas cela. Trois des six cas corrompus franchissent un seuil de 0.92. "Alice paid Bob" et "Bob paid Alice" sont le même sac de mots et obtiennent 0.985 ; "must not disable" → "must disable" obtient 0.947. Les embeddings de phrases encodent la parenté thématique, pas la vérité.

La fidélité est donc vérifiée à deux niveaux, et aucun n'est le cosinus :

  1. Invariants — nombres, nombre de négations, force modale, force des quantificateurs, URL, e-mails, identifiants, entités nommées. Déterministe, sans dépendance, et explicable (negation count changed: 1 → 0). Les modaux et quantificateurs sont comparés par classe de sens, donc may → might passe et may → must échoue. Aveugle aux inversions de rôles où chaque entité survit.
  2. Implication bidirectionnelle — le sens est préservé si et seulement si chaque texte implique l'autre. Détecte ce que le comptage ne peut pas, y compris les inversions de rôles, et l'asymétrie est révélatrice (le retrait de l'atténuateur a obtenu a⇒b 0.991, b⇒a 0.011). Nécessite nullorigin[nli] ; sans lui, la limitation est signalée, pas cachée.

C'est une barrière, pas un rapport

Mesurer la dérive après coup n'aide pas si le texte endommagé a déjà été renvoyé. Un contrôle qui échoue réessaie à une température plus basse — la dérive est pilotée par la température — et une fois le budget de réessais épuisé, renvoie l'original, avec ok=False et la raison.```yaml text: fidelity: enabled: true max_retries: 2 temperature_step: 0.25 use_nli: true nli_threshold: 0.5

root@kitploit:~
Cela signifie aussi que l'attaque et le risque partagent un même curseur : augmenter la température abaisse le
z-score *et* augmente le taux de dérive. Le benchmark les rapporte ensemble plutôt que comme
des contrôles indépendants.

### Autres métriques

* **Perplexité** — vraie PPL GPT-2 avec `torch` + `transformers`, sinon
  `unigram_entropy_proxy`, signalée comme approximative et **non** comparable aux PPL publiées.
* **Similarité cosinus** est toujours rapportée comme `mean_cosine_or_lexical`, à titre de référence uniquement.
  Elle ne constitue plus un critère de réussite/échec, pour les raisons indiquées dans le tableau ci-dessus.

## ⚙️ Configuration

Ordre de résolution, de la priorité la plus basse à la plus haute :

1. Valeurs par défaut intégrées
2. `nullorigin.yaml` (recherché dans `./`, `../`, `/app/`, ou `$NULLORIGIN_CONFIG`)
3. Variables d'environnement `NULLORIGIN_*`
4. Drapeaux CLI explicites

### Paramètres clés

| Setting | Default | Notes |
| --- | --- | --- |
| `proxy.host` | `127.0.0.1` | Boucle locale. Une liaison publique est refusée sans jeton de télémétrie. |
| `proxy.port` | `8080` | |
| `proxy.default_provider` | `openai` | Utilisé lorsqu'aucun en-tête `x-nullorigin-provider` n'est envoyé. |
| `proxy.telemetry_token` | `""` | Protège `/telemetry` et `/telemetry/reset`. |
| `proxy.max_request_bytes` | `104857600` | 100 MiB ; les corps plus volumineux reçoivent `413`. |
| `text.paraphraser.backend` | `ollama` | `none` \| `ollama` \| `openai_compatible` \| `llama_cpp` \| `lexical`. `none` laisse le filigrane statistique intact. `lexical` ne nécessite aucun modèle mais constitue une attaque bien plus faible. |
| `text.clean_unicode` | `true` | Suppression des caractères de largeur nulle et du bloc Tags. |
| `text.fold_homoglyphs` | `true` | Confusables cyrilliques/grecs convertis en ASCII. |
| `text.stream_window_tokens` | `40` | Deltas mis en mémoire tampon avant que le segment de streaming soit réécrit. |
| `media.crop_mode` | `trim` | `trim` déplace les coordonnées sans rééchantillonnage ; `resample` restaure les dimensions exactes mais coûte environ SSIM 0,81 / PSNR 31 dB même pour un recadrage de 0,5 % ; `none` désactive le passage géométrique. |
| `audio.low_cut_hz` | `800.0` | La phase en dessous de cette valeur est préservée pour l'intelligibilité. |

### Variables d'environnement```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

🩺 Dépannage

model 'llama3.2:3b' not found Le modèle configuré n'a pas été récupéré. Exécutez ollama list pour voir ce que vous avez, puis soit ollama pull llama3.2:3b, soit définissez NULLORIGIN_PARAPHRASER_MODEL sur un modèle que vous avez déjà.

WARNING: ollama backend unavailable (ReadTimeout) La réécriture a dépassé text.paraphraser.timeout_seconds (120 s par défaut). Les modèles de raisonnement tels que qwen3 prennent régulièrement plus de 150 s par paragraphe sur CPU. Augmentez-le : export NULLORIGIN_PARAPHRASER_TIMEOUT=900, ou utilisez un modèle instruct plus petit.

nullorigin benchmark exits 1 with pass_no_detection: FAIL Comportement attendu. Aucun backend de réécriture n'était joignable, donc seule la couche Unicode a été exécutée et le filigrane statistique a survécu. Démarrez Ollama, ou définissez le backend sur lexical pour une comparaison sans dépendance.

semantic_check: INCONCLUSIVE Attendu sans l'extra [metrics]. Voir Honnêteté des métriques.

Error: Refusing to bind 0.0.0.0 without a telemetry token Volontaire. Voir Déploiement au-delà de localhost.

Multiple top-level packages discovered in a flat-layout Vous êtes sur un ancien checkout. pyproject.toml définit une liste de paquets explicite ; récupérez la dernière version.

Async tests report UsageError about a missing async plugin Délibéré — sans lui, pytest signale les tests async def comme réussis sans les attendre. Exécutez pip install -e ".[dev]".

Docker: curl: (7) Failed to connect right after compose up Le healthcheck a une période de démarrage de 10 s. Attendez, puis vérifiez docker compose logs nullorigin.


🏗️ Architecture```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:~
### Mise en page```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

🧪 Tests```bash

pytest -q

root@kitploit:~
384 tests. La suite de tests couvre l'analyseur de trames SSE face à des frontières de chunks adverses, le cycle de vie
du streaming proxy, chaque mode d'image PIL, le SSIM par rapport à une valeur de forme fermée et à une
implémentation de référence par force brute, ainsi que la détectabilité du filigrane dans les ensembles de données.

Les tests asynchrones échouent bruyamment si aucun plugin asynchrone n'est installé, plutôt que d'être silencieusement ignorés.

---

## 📖 Citation et réutilisation

Sous licence Apache-2.0, qui autorise l'utilisation, la modification et la redistribution à condition
que l'avis de droit d'auteur et l'attribution à **Muhammad Rakibul Islam** soient conservés. Voir
[LICENSE](https://github.com/rakib-nyc/nullorigin/blob/HEAD/LICENSE) et [NOTICE](https://github.com/rakib-nyc/nullorigin/blob/HEAD/NOTICE).

Si ce travail est utilisé à l'appui d'une publication, veuillez le citer comme suit :```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}
}

Ce dépôt est publié comme un artefact de recherche abouti et n'accepte pas les pull requests. Vous êtes libre de le forker selon les termes de la licence. Les questions et les découvertes sont les bienvenues par courriel à [email protected].


🗺️ État du projet

Version 1.0.0. La suite est complète par rapport à sa spécification et entièrement testée, avec les limites connues suivantes :

  • L'efficacité contre SynthID, Tree-Ring et AudioSeal est invérifiable ici — aucun détecteur public n'existe pour la mesurer. Les perturbations sont implémentées ; l'affirmation n'est pas faite.
  • La dérive sémantique est conditionnée par des invariants plus une NLI optionnelle. Sans [nli], les échanges de rôles qui préservent chaque entité sont indétectables, et le rapport le dit.
  • La latence avec un modèle de raisonnement local (~150 s par passage) rend le chemin texte inadapté à une utilisation interactive. Un modèle d'instruction plus petit est beaucoup plus rapide.
  • Pas de TLS et pas d'authentification des requêtes — par conception ; exécutez-le derrière un proxy inverse.

Voir CHANGELOG.md pour l'historique des versions.


📚 Références

  • Kirchenbauer et al., A Watermark for Large Language Models (2023) — le schéma 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 Specification

📜 Mention légale et attribution

  • Auteur et créateur : Muhammad Rakibul Islam
  • Contact : [email protected]
  • Licence : Apache License, Version 2.0

Ce dépôt est un artefact de qualité recherche publié pour la recherche statistique, l'évaluation de la confidentialité, l'analyse comparative de la robustesse des filigranes et les tests de résilience cryptographique.```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:~
**AUCUNE GARANTIE.** LE LOGICIEL EST FOURNI "TEL QUEL", SANS GARANTIE D'AUCUNE SORTE,
EXPRESSE OU IMPLICITE.
Télécharger l’outil
CoucheCe qu'elle fait réellement
Caractères invisiblesTotalement efficace. Les charges utiles à largeur nulle, de contrôle bidi, de sélecteur de variante et du bloc Unicode Tags sont entièrement supprimées, avec un décompte indiqué. Les confusables homoglyphes inter-scriptes (cyrillique/grec rendus en ASCII) sont repliés.
Métadonnées de document (.docx)Totalement efficace. L'auteur, le dernier éditeur, le nombre de révisions, les horodatages, le modèle et la version de l'application sont effacés de docProps, avec mise en forme préservée octet par octet.
C2PA / EXIF / XMPTotalement efficace. L'image est reconstruite à partir des échantillons bruts de pixels dans un nouveau conteneur, de sorte que les manifestes JUMBF signés et toutes les métadonnées disparaissent. Vérifié par des tests sur des maquettes étiquetées.
Filigrane statistique KGWDépend entièrement du backend de réécriture. Sans modèle local configuré, le filigrane statistique survit — l'outil le dit plutôt que de laisser entendre le contraire.
SynthID-Text / SynthID-Image / Tree-RingNon vérifiable ici. Ces mécanismes utilisent des clés privées et des décodeurs propriétaires. NullOrigin applique les perturbations décrites dans la littérature, mais aucune affirmation n'est faite selon laquelle elles neutralisent les véritables détecteurs, car il n'existe aucun détecteur public pour effectuer une mesure.
AudioSeal / SynthID-AudioNon vérifiable ici, pour la même raison.
requis pour la suppression de filigrane sur le texte
OptionnelDocker 20.10+ avec Compose v2
caslexical_f1embedding cosinebidirectional NLI
négation supprimée0.700.77 ✓0.000 ✓
nombre 5 → 500.820.81 ✓0.000 ✓
inversion entité/rôle0.810.985 ✗0.000 ✓
quantificateur all → some0.880.91 ✓0.000 ✓
atténuateur retiré0.270.953 ✗0.011 ✓
"must not" → "must"0.830.947 ✗0.000 ✓
réécriture fidèle0.330.931 ✓0.998 ✓