Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
nullorigin — Kit de ferramentas de robustez de marcas d'água de IA somente para pesquisa: proxy reverso local remove C2PA/EXIF/XMP, Unicode, esteganografia de imagem/áudio, metadados OOXML/PDF e verifica Trojan Source. | Kitploit
Ferramentas/GitHubGitHub/rakib-nyc/nullorigin
Análise Estática de Código (SAST)Proxies Web e InterceptaçãoEsteganografiaPrivacidadeAprendizado e EducaçãoSegurança de IAAtaque Adversário
GitHubrakib-nyc/nullorigin

nullorigin

Kit de ferramentas de robustez de marcas d'água de IA somente para pesquisa: proxy reverso local remove C2PA/EXIF/XMP, Unicode, esteganografia de imagem/áudio, metadados OOXML/PDF e verifica Trojan Source.

Ver Repositório
4há 3 diasAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

NullOrigin 🛡️

Middleware Universal de Proveniência e Sanitização de Marcas d'Água de IA Artefato de pesquisa — somente para avaliação de robustez de marcas d'água.

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


🔬 Somente para uso em pesquisa

NullOrigin é um artefato de pesquisa. Ele é publicado para apoiar o estudo acadêmico e independente da robustez de marcas d'água, e para nenhum outro propósito.

Esquemas de marca d'água são alegações de segurança, e alegações de segurança só são significativas quando alguém tentou quebrá-las. A literatura que este projeto implementa — Kirchenbauer et al. sobre KGW, Krishna et al. sobre ataques de paráfrase, Boucher & Anderson sobre Trojan Source — existe porque pesquisadores publicaram ataques funcionais para que defensores pudessem medir robustez real em vez de assumi-la. Essa é a tradição à qual este repositório pertence.

Usos pretendidos

  • Medir o quanto a detectabilidade de uma marca d'água degrada sob um ataque declarado
  • Reproduzir e estender resultados de robustez publicados
  • Avaliar vazamento de metadados de proveniência em seus próprios documentos antes de compartilhá-los
  • Auditar código-fonte para ataques Trojan Source e homoglifos (CVE-2021-42574, CVE-2021-42694)
  • Ensinar como esses esquemas funcionam e onde eles se sustentam

Não pretendido e não suportado

  • Apresentar trabalho gerado como seu onde uma pessoa ou instituição pediu que você não o fizesse
  • Neutralizar sinais de proveniência em conteúdo que você pretende deturpar
  • Remover atribuição de trabalho que não é seu
  • Contornar qualquer política de plataforma, licença ou integridade acadêmica

Nada aqui é um controle técnico sobre como o código executa. É uma declaração dos termos sob os quais ele é oferecido, e do que seu autor apoiará ou não. O software é fornecido "AS IS", sem garantia de qualquer tipo — consulte LICENSE.

Leia Escopo e limitações honestas antes de tirar qualquer conclusão de um número que esta ferramenta imprimir. Vários dos esquemas que ela alvo não podem ser verificados contra um detector público, e o README afirma isso em vez de dar a entender o contrário.


Conteúdo

  • Somente para uso em pesquisa
  • Escopo e limitações honestas
  • Fundamentos teóricos
  • Instalação
  • Início rápido
  • Docker
  • Implantação além de localhost
  • CLI
  • Benchmarking
  • Fidelidade semântica
  • Configuração
  • Solução de problemas
  • Arquitetura
  • Testes
  • Citação e reutilização
  • Status do projeto
  • Referências
  • Aviso legal e atribuição

⚠️ Escopo e limitações honestas

Leia isto antes de tirar conclusões de qualquer número que esta ferramenta imprimir.

Sobre o detector KGW

KGWStatisticalDetector é uma implementação matematicamente fiel, autoconsistente, do esquema de lista verde/vermelha de Kirchenbauer et al. sobre tokens de espaço em branco. Ele não é um decodificador para a marca d'água de produção de nenhum fornecedor — essas dependem de um segredo privado e do vocabulário BPE do próprio modelo.

Seu propósito é tornar o benchmark real: KGWWatermarkEmbedder insere uma marca d'água genuína, o pipeline a ataca, e o detector correspondente mede a redução real. Essa é uma medição verdadeira do ataque contra este esquema. Ela não se transfere para a marca d'água de um fornecedor.


📖 Fundamentos teóricos

Texto — lista verde/vermelha KGW

O vocabulário $V$ é particionado a cada passo $t$ por um hash semeado no contexto anterior:

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

em uma lista verde $G_t$ de tamanho $\gamma|V|$ e uma lista vermelha $R_t$. Um viés $\delta > 0$ é adicionado aos logits verdes:

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

A detecção conta acertos verdes. Sob $H_0$ eles são $\text{Binomial}(T, \gamma)$, portanto:

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

com $z > 4.0$ ($p < 3\times10^{-5}$) marcado como sintético.

Por que a paráfrase ataca isso: a marca d'água vive inteiramente em transições locais de n-gramas. Reescrever a forma de superfície com um modelo sem marca d'água re-semeia cada posição. Este é o ataque de robustez padrão na literatura de marcas d'água.

Por que o comprimento importa: $z$ cresce como $\sqrt{T}$. Uma passagem de 100 tokens com uma fração verde de 0,70 atinge apenas $z \approx 3,9$ — abaixo do limite. A detecção precisa de algumas centenas de tokens, e o mesmo vale para fixtures de benchmark significativos.

Imagem — C2PA e incorporação no domínio da frequência

  • C2PA / JUMBF: manifestos assinados em segmentos JPEG APP11, chunks PNG tEXt/iTXt, ou caixas c2pa WebP/AVIF. Como a assinatura cobre os dados de pixel, a re-codificação a partir de um buffer de amostra puro remove-a sem analisar JUMBF.
  • SynthID-Image: deslocamentos nos coeficientes DCT/DWT. Atacado por limiarização suave das sub-bandas de detalhe de alta frequência.
  • Tree-Ring: um padrão circularmente simétrico no latente inicial da difusão, sobrevivendo como estrutura coerente na fase de Fourier. Atacado por randomização da fase em anéis de média frequência preservando a magnitude — é por isso que a imagem permanece visualmente idêntica.

Áudio — AudioSeal / SynthID-Audio

Modulação de fase abaixo do limiar e adições espectrais de baixa amplitude. Atacado por randomização de fase acima do fundamental da fala, deslocamento de entalhe de rejeição de banda em bandas não críticas e re-quantização psicoacústica.


📦 Instalação

Requisitos

Python3.10, 3.11 ou 3.12
SOLinux, macOS (Intel e Apple Silicon), Windows via WSL2
OpcionalOllama ou qualquer servidor compatível com OpenAI —

A partir do código-fonte```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 opcionais```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]"

Sem [nli], a gate de fidelidade roda apenas sobre invariantes — ainda é uma verificação real, mas cega a trocas de papéis. Veja Fidelidade semântica.

Verificar a instalação```bash

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

root@kitploit:~
---

## 🚀 Início rápido

### 1. Configure um modelo de reescrita local

A remoção de marca d'água em texto precisa de um modelo local sem marca d'água. Sem um, o NullOrigin remove
caracteres invisíveis, mas **deixa a marca d'água estatística intacta** — e avisa isso.```bash
ollama serve                    # in a separate terminal
ollama pull llama3.2:3b         # or any instruct model you prefer

Usando um modelo diferente? Aponte o NullOrigin para ele:```bash export NULLORIGIN_PARAPHRASER_MODEL=qwen3:4b export NULLORIGIN_PARAPHRASER_TIMEOUT=900 # reasoning models are slow

root@kitploit:~
### 2. Inicie o proxy```bash
nullorigin run

[No content provided 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. Aponte seu cliente para ele```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)

(translated into Portuguese):The following content combines the system/developer instructions and the input received. The Assistant response must comply with the instruction to translate the chunk, so the output below is the Assistant's final response, containing only the translated Markdown text.

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"}]}'

Streaming (SSE) e Gemini (/v1beta/models/...) são tratados da mesma forma. O cabeçalho x-nullorigin-provider seleciona o upstream e é removido antes do encaminhamento; seus cabeçalhos de autenticação passam intocados.


🐳 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:~
A stack Compose executa o NullOrigin junto com um sidecar Ollama em uma rede bridge privada.
O contêiner proxy faz bind de `0.0.0.0` — correto dentro de um contêiner — e apenas a porta 8080 é
publicada para o seu host.

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

Comandos úteis:```bash docker compose logs -f nullorigin docker compose down # stop docker compose down -v # stop and delete the Ollama model volume

root@kitploit:~
---

## 🔒 Implantação além do localhost

**O NullOrigin usa `127.0.0.1` como padrão e se recusa a vincular uma interface pública sem um
token de telemetria.** Ele retransmite as credenciais da sua API upstream, então isso é deliberado:```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

Para expô-lo adequadamente:```bash export NULLORIGIN_TELEMETRY_TOKEN=$(openssl rand -hex 32) nullorigin run --host 0.0.0.0 --port 8080

root@kitploit:~
então coloque-o atrás do nginx, Caddy ou Traefik fornecendo **terminação TLS**, **limitação
de taxa** e uma **camada de autenticação**.

### Modelo de ameaça

O NullOrigin é um **proxy reverso local que retransmite suas credenciais de API do upstream**. Esse
único fato determina sua postura de segurança.

| Controle | Padrão | Motivo |
| --- | --- | --- |
| Endereço de bind | `127.0.0.1` | Somente loopback; um bind público é recusado a menos que um token de telemetria seja definido ou `--allow-public-bind` seja passado. |
| `/telemetry`, `/telemetry/reset` | Aberto no loopback | Controlado por `X-NullOrigin-Token`, comparado em tempo constante, sempre que `proxy.telemetry_token` estiver definido. |
| `/health` | Sempre aberto | Sondas de contêiner precisam dele; expõe versão e mecanismos habilitados, sem segredos. |
| Tamanho do corpo da requisição | 100 MiB | O proxy armazena os corpos em buffer para encaminhá-los; entradas maiores são rejeitadas com `413`. |
| Buffer SSE | 1 MiB | Um upstream que nunca encerra um frame é liberado, não armazenado em buffer indefinidamente. |
| Usuário do contêiner | non-root | O proxy não precisa de privilégios elevados. |

Limitações conhecidas, por design e não defeitos:

* **Sem TLS.** Ele encaminha `Authorization` e `x-api-key` sem alterações por HTTP simples. Coloque-o
  atrás de um proxy reverso que faça a terminação HTTPS em qualquer rede não confiável.
* **Sem autenticação no caminho do proxy.** Qualquer pessoa que consiga alcançar a porta pode fazer proxy
  através dela, usando suas próprias credenciais — o NullOrigin não armazena nem injeta chaves.
* **Sem limitação de taxa.** Aplique-a no proxy reverso.
* **As credenciais nunca são persistidas.** Nenhuma chave de API é gravada em disco ou em logs; a telemetria
  conta apenas requisições e eventos de sanitização.
* A verificação de TLS do upstream permanece ativada, e redirecionamentos não são seguidos.

Para relatar um problema de segurança, envie um e-mail para **[email protected]** com `[NullOrigin Security]` no
assunto.

Telemetria com um token definido:```bash
curl -H "X-NullOrigin-Token: $NULLORIGIN_TELEMETRY_TOKEN" http://localhost:8080/telemetry

/health nunca é protegido, então os probes de contêiner continuam funcionando.


🛠️ 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:~
### Formatos suportados

| Tipo | Extensões | Notas |
| --- | --- | --- |
| **Imagens** | `.png` `.jpg` `.jpeg` `.jfif` `.webp` `.tif` `.tiff` `.bmp` `.gif` `.ico` `.avif` `.jp2` | Todos os modos PIL (RGB, RGBA, L, LA, P, 1, I;16, CMYK, YCbCr). GIF/WebP animados e TIFF multipágina mantêm todos os quadros e sua temporização. Imagens com menos de 64px mantêm as dimensões exatas. |
| **Áudio** | `.wav` `.wave` | Int de 8/16/32 bits, float de 32 bits; de mono a multicanal; qualquer taxa de amostragem. Arquivos de tamanho zero completam o ciclo sem perdas. |
| **Documentos** | `.docx` `.docm` `.dotx` `.pptx` `.pptm` `.xlsx` `.xlsm` | Todos os três dialetos OOXML. Runs de texto sanitizados no corpo, cabeçalhos, rodapés, notas de rodapé, notas e strings compartilhadas; metadados `docProps` removidos; todas as outras partes copiadas byte a byte. |
| **PDF** | `.pdf` | Dicionário `/Info`, pacote XMP, anexos incorporados e JavaScript removidos; páginas, texto e geometria preservados. Arquivos protegidos por senha são recusados. Veja a ressalva abaixo. |
| **Código-fonte** | `.py` `.js` `.ts` `.go` `.rs` `.java` `.c` `.cpp` `.rb` `.php` `.sh` `.sql` + 50 outros | Verificação de Trojan Source e homóglifos. **Sem NFKC, sem parafraseamento** — veja abaixo. |
| **Texto** | qualquer outra coisa decodificável | UTF-8, UTF-8 BOM, UTF-16, UTF-32, CP1252, Latin-1 — detectado automaticamente e **gravado de volta na mesma codificação**. |

Todo o resto é **recusado com orientação específica** em vez de ser lido como UTF-8 e
corrompido — `.mp3` aponta para `ffmpeg -i in.mp3 out.wav`, `.doc`/`.ppt`/`.xls` legados
para re-salvar como OOXML. Um arquivo recusado nunca produz saída.

Verificado contra um corpus de 69 arquivos abrangendo todos os formatos acima: **59 processados corretamente,
10 recusados de forma limpa, zero travamentos, zero saídas corrompidas.**

### O código é mantido fora do reescritor

As respostas do assistente misturam prosa e código em uma única string. Entregar tudo a um
modelo de parafraseamento reescreve o código junto com a prosa — e o z-score cai
de qualquer forma, então nada a jusante percebe.

As respostas são, portanto, segmentadas antes que qualquer coisa seja reescrita:

| Segmento | Tratamento |
| --- | --- |
| Prosa | Higienizada por Unicode, depois reescrita |
| Blocos cercados (``` e ~~~) | Caracteres invisíveis e bidi removidos. **Sem NFKC, nunca reescritos.** |
| Spans inline `` `code` `` | O mesmo |

Isso também vale para o caminho de streaming, onde uma cerca abre em um delta e fecha
vários deltas depois. Um delta que atravessa a fronteira é dividido por linha, de modo que o
fechamento ``` e a prosa após ele são tratados de forma diferente. Uma cerca não fechada falha
de forma segura: o restante é protegido em vez de reescrito.

Desative com `text.protect_code_blocks: false` se quiser o comportamento antigo.

### Arquivos de código-fonte: uma verificação de segurança, não uma remoção de marca d'água

**Não há marca d'água em código-fonte gerado por IA.** Nenhum provedor aplica marca d'água
na saída de código, e não existe detector público. Quem afirma remover uma está
vendendo algo para você.

O que o código-fonte *realmente* tem é uma superfície de ataque real e publicada:

* **Trojan Source** ([CVE-2021-42574](https://nvd.nist.gov/vuln/detail/CVE-2021-42574),
  Boucher & Anderson 2021) — caracteres de controle bidirecionais reordenam como o código é
  *exibido* sem alterar como ele *compila*. Um revisor aprova um programa; o
  compilador constrói outro.
* **Identificadores homóglifos** ([CVE-2021-42694](https://nvd.nist.gov/vuln/detail/CVE-2021-42694))
  — `а` cirílico no lugar do `a` latino cria dois nomes que são renderizados de forma idêntica.```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⁩⁦":

Após purgar, a linha fica if access_level != "user // Check if admin": — o "comentário" esteve dentro da string o tempo todo.

Três coisas que o caminho de código deliberadamente não faz, porque o caminho de texto genérico fazia todas as três e cada uma é um bug no código-fonte:

  1. Sem normalização NFKC. Ela reescreve valores de literais de string: "Hello" vira "Hello", "office" vira "office". Isso altera o que um programa compara, aplica hash e transmite.
  2. Sem paráfrase. Enviar código a um modelo de reescrita produz código que não executa.
  3. Homóglifos são relatados, não convertidos. Converter um а cirílico mescla dois identificadores que o compilador atualmente trata como distintos — mudando silenciosamente o comportamento. A severidade é MEDIUM apenas para tokens de scripts mistos (totаl), a assinatura de ataque real; uma palavra escrita inteiramente em outro script é texto estrangeiro comum e recebe INFO. Ative com --fold-homoglyphs-in-code depois de revisá-los.

inspect --json emite, para cada achado, severidade, linha, coluna e ponto de código, então ele se integra à CI como um gate de pre-commit ou PR.

PDF: o que é e o que não é removido

Removido, de forma verificável: o dicionário /Info (Author, Title, Subject, Keywords, Creator, Producer, CreationDate, ModDate), o pacote XMP em /Root/Metadata, anexos de arquivo incorporados e JavaScript em nível de documento. Páginas, texto e geometria de página são preservados exatamente; a operação é idempotente e estável em bytes.

Detectados, mas NÃO removidos: caracteres invisíveis dentro dos fluxos de conteúdo das páginas. O PDF desenha texto glifo a glifo por meio de uma codificação específica da fonte — um espaço de largura zero em uma fonte com codificação CID é um índice de glifo de dois bytes, não um U+200B literal —, portanto, uma reescrita genérica corromperia o layout em vez de limpá-lo. inspect informa a contagem; purge imprime um aviso em vez de permanecer em silêncio, porque o silêncio seria interpretado como "não havia nenhum". Para removê-los, extraia o texto, execute nullorigin purge nele e regenere o PDF.

--strip-annotations está disponível, mas desativado por padrão: as anotações incluem links e campos de formulário, não apenas comentários, portanto removê-las altera o comportamento do documento.

Tipografia não é uma marca d'água

Travessões, aspas curvas e reticências são saída comum de processadores de texto. O NullOrigin os preserva por padrão e os relata separadamente dos achados genuínos, porque achatá-los degrada um documento sem higienizar nada. Use --flatten-typography se você quiser especificamente uma saída ASCII.

Confusíveis entre scripts são diferentes — um о cirílico no meio de uma palavra em inglês não tem uso legítimo — e esses são convertidos por padrão.

--verify relata medições antes/depois em vez de afirmar sucesso:```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:~
Se o backend estiver inacessível, isso é relatado como um aviso no stderr — um
fallback silencioso pareceria idêntico a uma sanitização bem-sucedida.

---

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

Cada valor é medido no local: o texto é marcado com KGWWatermarkEmbedder, executado pelo pipeline real e reavaliado com o detector correspondente. O runner encerra com código não zero quando os limiares não são atingidos e explica o motivo.

Limiares (da diretriz do projeto):

MétricaAlvo
z-score pós-sanitização$\lvert z\rvert \le 1.5$
Similaridade semântica$\ge 0.92$
SSIM da imagem$\ge 0.95$
PSNR da imagem$\ge 36$ dB

Resultados medidos

Seção de texto completo sobre datasets/text/watermarked_kgw.json, reescrita por meio do Ollama (qwen3:4b) em um MacBook da série M (~150 s por passagem):```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:~
Toda amostra com marca d'água passou de detectada para não detectada. Observe `kgw_003` em
z = 1.271 — abaixo do limiar, mas a mais próxima dele, que é a forma honesta deste
ataque: é estatístico, não uma garantia.

Mídia, medida nos fixtures de imagem:```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

Ambos os limiares de imagem passam (SSIM ≥ 0.95, PSNR ≥ 36 dB). Os números variam conforme o modelo, o hardware e o trecho.

Fidelidade semântica: uma queda no z-score não é evidência de significado preservado

Uma reescrita que altera um fato pontua exatamente como uma fiel na métrica de marca d'água. A verificação óbvia para isso não funciona, e a menos óbvia também não. Medido em seis casos de deriva mais um controle fiel:

A sobreposição lexical está invertida. Toda edição que destrói o significado pontuou mais alto do que a reescrita fiel, porque uma boa paráfrase compartilha poucos n-gramas com sua fonte, enquanto uma corrompida compartilha quase todos.

O cosseno do embedding não resolve isso. Três dos seis casos corrompidos ultrapassam um limiar de 0,92. "Alice paid Bob" e "Bob paid Alice" são o mesmo saco de palavras e pontuam 0,985; "must not disable" → "must disable" pontua 0,947. Os embeddings de frases codificam relação temática, não verdade.

Portanto, a fidelidade é verificada em duas camadas, e nenhuma delas é o cosseno:

  1. Invariantes — números, contagem de negações, força modal, força de quantificadores, URLs, e-mails, identificadores, entidades nomeadas. Determinístico, sem dependências e explicável (negation count changed: 1 → 0). Modais e quantificadores são comparados por classe de significado, então may → might passa e may → must falha. Cega a trocas de papéis em que todas as entidades sobrevivem.
  2. Implicação bidirecional — o significado é preservado se, e somente se, cada texto implicar o outro. Captura o que a contagem não consegue, incluindo trocas de papéis, e a assimetria é diagnóstica (remoção de hedge pontuou a⇒b 0,991, b⇒a 0,011). Requer nullorigin[nli]; sem isso, a limitação é relatada, não ocultada.

É uma barreira, não um relatório

Medir a deriva depois do fato não ajuda se o texto danificado já foi retornado. Uma verificação com falha tenta novamente a uma temperatura mais baixa — a deriva é impulsionada pela temperatura — e, após o orçamento de tentativas, retorna o original, com ok=False e o motivo.```yaml text: fidelity: enabled: true max_retries: 2 temperature_step: 0.25 use_nli: true nli_threshold: 0.5

root@kitploit:~
Isto também significa que o ataque e o risco partilham um único dial: aumentar a temperatura reduz o
z-score *e* aumenta a taxa de deriva. O benchmark reporta-os em conjunto, em vez de
verificações independentes.

### Outras métricas

* **Perplexidade** — PPL verdadeiro do GPT-2 com `torch` + `transformers`, caso contrário
  `unigram_entropy_proxy`, marcado como aproximado e **não** comparável ao PPL publicado.
* **Similaridade de cosseno** ainda é reportada como `mean_cosine_or_lexical`, apenas para referência.
  Deixou de ser um critério de aprovação/reprovação, pelas razões na tabela acima.

## ⚙️ Configuração

Ordem de resolução, da precedência mais baixa para a mais alta:

1. Predefinições incorporadas
2. `nullorigin.yaml` (procurado em `./`, `../`, `/app/` ou `$NULLORIGIN_CONFIG`)
3. Variáveis de ambiente `NULLORIGIN_*`
4. Flags explícitas de CLI

### Definições principais

| Definição | Predefinição | Notas |
| --- | --- | --- |
| `proxy.host` | `127.0.0.1` | Loopback. Uma vinculação pública é recusada sem um token de telemetria. |
| `proxy.port` | `8080` | |
| `proxy.default_provider` | `openai` | Usado quando nenhum cabeçalho `x-nullorigin-provider` é enviado. |
| `proxy.telemetry_token` | `""` | Protege `/telemetry` e `/telemetry/reset`. |
| `proxy.max_request_bytes` | `104857600` | 100 MiB; corpos maiores recebem `413`. |
| `text.paraphraser.backend` | `ollama` | `none` \| `ollama` \| `openai_compatible` \| `llama_cpp` \| `lexical`. `none` deixa a marca de água estatística intacta. `lexical` não precisa de modelo, mas é um ataque muito mais fraco. |
| `text.clean_unicode` | `true` | Remoção de zero-width e do bloco Tags. |
| `text.fold_homoglyphs` | `true` | Confundíveis cirílicos/gregos para ASCII. |
| `text.stream_window_tokens` | `40` | Deltas em buffer antes de um span de streaming ser reescrito. |
| `media.crop_mode` | `trim` | `trim` desloca as coordenadas sem reamostragem; `resample` restaura as dimensões exatas, mas custa aproximadamente SSIM 0.81 / PSNR 31 dB mesmo com um corte de 0.5%; `none` desativa a passada geométrica. |
| `audio.low_cut_hz` | `800.0` | A fase abaixo deste valor é preservada para inteligibilidade. |

### Variáveis de ambiente```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

🩺 Solução de problemas

model 'llama3.2:3b' not found O modelo configurado não foi baixado. Execute ollama list para ver o que você tem e, em seguida, use ollama pull llama3.2:3b ou defina NULLORIGIN_PARAPHRASER_MODEL para um modelo que você já possua.

WARNING: ollama backend unavailable (ReadTimeout) A reescrita excedeu text.paraphraser.timeout_seconds (padrão 120 s). Modelos de raciocínio como qwen3 costumam levar 150 s+ por parágrafo na CPU. Aumente: export NULLORIGIN_PARAPHRASER_TIMEOUT=900 ou use um modelo de instrução menor.

nullorigin benchmark sai com código 1 e pass_no_detection: FAIL Funciona como esperado. Nenhum backend de reescrita estava acessível, então apenas a camada unicode foi executada e a marca d'água estatística sobreviveu. Inicie o Ollama ou defina o backend para lexical para uma comparação sem dependências.

semantic_check: INCONCLUSIVE Esperado sem o extra [metrics]. Veja Honestidade das métricas.

Error: Refusing to bind 0.0.0.0 without a telemetry token Intencional. Veja Implantação além do localhost.

Multiple top-level packages discovered in a flat-layout Você está em um checkout antigo. pyproject.toml define uma lista explícita de pacotes; atualize para a versão mais recente.

Testes assíncronos relatam UsageError sobre um plugin assíncrono ausente Deliberado — sem um, o pytest relata testes async def como aprovados sem aguardá-los. Execute pip install -e ".[dev]".

Docker: curl: (7) Failed to connect logo após compose up O healthcheck tem um período inicial de 10 s. Aguarde e então verifique docker compose logs nullorigin.


🏗️ Arquitetura```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:~
### Layout```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

🧪 Testes```bash

pytest -q

root@kitploit:~
384 testes. A suíte cobre o parser de frames SSE contra limites de chunk adversariais, proxy
ciclo de vida de streaming, todos os modos de imagem PIL, SSIM contra tanto um valor de forma fechada quanto uma
implementação de referência por força bruta, e a detectabilidade de marca d'água em conjuntos de dados.

Testes assíncronos falham ruidosamente se nenhum plugin assíncrono estiver instalado, em vez de pular silenciosamente.

---

## 📖 Citação e reutilização

Licenciado sob Apache-2.0, que permite uso, modificação e redistribuição desde que
o aviso de direitos autorais e a atribuição a **Muhammad Rakibul Islam** sejam mantidos. Consulte
[LICENSE](https://github.com/rakib-nyc/nullorigin/blob/HEAD/LICENSE) e [NOTICE](https://github.com/rakib-nyc/nullorigin/blob/HEAD/NOTICE).

Se este trabalho apoiar uma publicação, cite-o como:```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}
}

Este repositório é publicado como um artefato de pesquisa finalizado e não está aceitando pull requests. Você é livre para fazer um fork sob os termos da licença. Perguntas e descobertas são bem-vindas por e-mail em [email protected].


🗺️ Status do projeto

Versão 1.0.0. A suíte está completa conforme sua especificação e totalmente testada, com estes limites conhecidos:

  • A eficácia contra SynthID, Tree-Ring e AudioSeal é inverificável aqui — não há detector público para medi-la. As perturbações são implementadas; a afirmação não é feita.
  • Deriva semântica é controlada por invariantes e NLI opcional. Sem [nli], trocas de papéis que preservam todas as entidades são indetectáveis, e o relatório afirma isso.
  • Latência com um modelo de raciocínio local (~150 s por passagem) torna o caminho de texto inadequado para uso interativo. Um modelo instruct menor é muito mais rápido.
  • Sem TLS e sem autenticação de requisições — por design; execute-o atrás de um proxy reverso.

Consulte CHANGELOG.md para o histórico de versões.


📚 Referências

  • Kirchenbauer et al., A Watermark for Large Language Models (2023) — o esquema 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

📜 Aviso legal e atribuição

  • Autor e Criador: Muhammad Rakibul Islam
  • Contato: [email protected]
  • Licença: Apache License, Version 2.0

Este repositório é um artefato de nível de pesquisa lançado para pesquisa estatística, avaliação de privacidade, benchmarking de robustez de marcas d'água e testes de resiliência criptográfica.```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:~
**SEM GARANTIAS.** O SOFTWARE É FORNECIDO "NO ESTADO EM QUE SE ENCONTRA", SEM GARANTIA DE QUALQUER TIPO,
EXPRESSA OU IMPLÍCITA.
Baixar ferramenta
CamadaO que ela realmente faz
Caracteres invisíveisTotalmente eficaz. Cargas de zero-width, controle bidirecional, seletor de variação e bloco de tags Unicode são removidas completamente, com uma contagem reportada. Confusáveis homoglifos entre escritas (renderização cirílica/grega como ASCII) são normalizados.
Metadados de documento (.docx)Totalmente eficaz. Autor, último editor, contagem de revisões, carimbos de data/hora, modelo e versão do aplicativo são limpos de docProps, com a formatação preservada byte por byte.
C2PA / EXIF / XMPTotalmente eficaz. A imagem é reconstruída a partir de amostras de pixels brutos em um contêiner novo, portanto manifestos JUMBF assinados e todos os metadados são removidos. Verificado por teste contra fixtures marcados.
Marca d'água estatística KGWDepende inteiramente do backend de reescrita. Sem um modelo local configurado, a marca d'água estatística sobrevive — a ferramenta afirma isso em vez de dar a entender o contrário.
SynthID-Text / SynthID-Image / Tree-RingNão verificável aqui. Esses usam chaves privadas e decodificadores proprietários. O NullOrigin aplica as perturbações que a literatura descreve, mas não é feita nenhuma afirmação de que eles derrotam os detectores reais, porque não há detector público para medir.
AudioSeal / SynthID-AudioNão verificável aqui, pela mesma razão.
necessário para remoção de marca d'água em texto
OpcionalDocker 20.10+ com Compose v2
casolexical_f1embedding cosinebidirectional NLI
negação removida0.700.77 ✓0.000 ✓
número 5 → 500.820.81 ✓0.000 ✓
troca de entidade/papel0.810.985 ✗0.000 ✓
quantificador todos → alguns0.880.91 ✓0.000 ✓
hedge removido0.270.953 ✗0.011 ✓
"must not" → "must"0.830.947 ✗0.000 ✓
reescrita fiel0.330.931 ✓0.998 ✓