From 30f923b6fd44c246a038ebfef828760ca9b4043e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 03:38:31 +0000 Subject: [PATCH 1/3] Add a public sanitizer-restore robustness bench Ship fuckmark-robustness-bench-v1 as `fuckmark robustness`: 180 hashed cells of live mix versus Unicode sanitizers, with no GPT-2 or SynthID rerun. Detector numbers stay on the sealed Gate v2 scorecard. Co-authored-by: Byte271 --- CHANGELOG.md | 1 + README.md | 13 +- docs/cli.md | 13 + docs/limits.md | 2 +- docs/research.md | 3 +- docs/robustness.md | 92 + fuckmark/cli.py | 7 + fuckmark/robustness.py | 433 +++ .../fuckmark-robustness-bench-v1.freeze.json | 12 + .../fuckmark-robustness-bench-v1.protocol.md | 80 + .../fuckmark-robustness-bench-v1.vectors.json | 3363 +++++++++++++++++ tests/test_robustness.py | 227 ++ 12 files changed, 4243 insertions(+), 3 deletions(-) create mode 100644 docs/robustness.md create mode 100644 fuckmark/robustness.py create mode 100644 specs/fuckmark-robustness-bench-v1.freeze.json create mode 100644 specs/fuckmark-robustness-bench-v1.protocol.md create mode 100644 specs/fuckmark-robustness-bench-v1.vectors.json create mode 100644 tests/test_robustness.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d6375ed..d57f9bb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - WASM build of `fuckmark-hidden-scan-v1` (`crates/fuckmark-scan`, committed `fuckmark_scan.wasm`). The scan page and the Chromium popup load it when `fetch` works so classification stays on-device; `scan.js` remains the `file://` fallback. Lone UTF-16 surrogates route through the JS engine (UTF-8 cannot carry them). Empty category arrays stay empty selections. The extension CSP allows `wasm-unsafe-eval` so Chromium can instantiate the module. Page reveal and paste-safe keep the synchronous JS port. Rebuild with `crates/fuckmark-scan/build-wasm.sh`. - Language bindings for the same Rust scan ABI: C header (`crates/fuckmark-scan/include/fuckmark_scan.h`), optional Python ctypes loader (`fuckmark.native_scan`), and a Node package under `bindings/node` (WASM + JS fallback). Host library build: `crates/fuckmark-scan/build-native.sh`. Lone UTF-16 surrogates cannot cross the UTF-8 ABI, so Python `scan_text` / `clean_text` delegate those strings to the Python reference scanner, and the WASM wrapper normalizes the JS fallback result to the same `index` / `truncated` / `highest_severity` / `source_length` schema. The C example is linked with `-rpath,$ORIGIN` and written next to `libfuckmark_scan.so`. Overview: `bindings/README.md`. - OS clipboard watcher (`fuckmark-clipboard-watch-v1`). `fuckmark clipboard scan` / `clean` / `watch` poll the system clipboard (pbpaste, wl-paste, xclip, xsel, PowerShell Get-Clipboard) for the same security category set as `fuckmark lint`. `--clean` rewrites the clipboard in place after a digest re-read so a newer copy is not overwritten. Snapshots hash with UTF-8 `surrogatepass` so lone surrogates do not crash the watcher. Linux read uses `wl-paste` without `--no-newline`. Emoji ZWJ sequences (both neighbors emoji) and info-level variation selectors are kept, matching browser paste-safe. `--json`, `-q`, `--select`, `--once`, `--exit-on-find`, `--interval`, `--max-seconds`. Exit `0` clean, `1` findings, `2` usage, `3` clipboard unavailable. Reference: `docs/clipboard.md`. Python: `evaluate_clipboard_text`, `watch_clipboard`, `clean_clipboard_text` (injectable reader/writer for tests). +- Public sanitizer-restore robustness bench (`fuckmark-robustness-bench-v1`). `fuckmark robustness` replays 10 short fixtures times 18 Unicode sanitizer attacks (180 cells) against the live five-layer mix and frozen vectors. It does not rerun GPT-2 or SynthID; the Gate v2 confirmation scorecard stays the sealed detector track (identity 188/192, mix 0/192 after required sanitizers). Mixed letter/emoji fixtures do not restore; `digits` has no eligible site so mix is a no-op and every attack restores. `--json`, `-q`, `--fixture`, `--attack`. Exit `0` match, `1` mismatch, `2` usage. Spec: `specs/fuckmark-robustness-bench-v1.{protocol.md,vectors.json,freeze.json}`. Reference: `docs/robustness.md`. - `fuckmark normalize` (`fuckmark-normalize-v1`): NFC, optional identifier lookalike fold (UTS #39-inspired subset, not a full confusable map), then strip of the security category set, plus a JSON receipt (`input_sha256` / `output_sha256` / `steps` / `report_hash`). `--clean` remains the blunt strip with no receipt. HTTP: `POST /api/normalize`. Python: `normalize_text`, `skeleton_fold`. Reference: `docs/normalize.md`. - VS Code / Cursor: `fuckmark.cleanOnSave` (default off) strips hidden Unicode on save. - Frozen confirmation files, hashes, mix mechanisms, and the published wheel are unchanged. Package version stays 0.4.1. diff --git a/README.md b/README.md index 136366b5..d5a1cebc 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,17 @@ fuckmark clipboard watch --clean Needs a clipboard tool (`pbpaste`, `wl-paste` / `xclip` / `xsel`, or PowerShell `Get-Clipboard`). Full reference: [`docs/clipboard.md`](docs/clipboard.md). +## Replay the sanitizer-restore bench + +`fuckmark robustness` is the public, hashed restore grid for the live mix. It does not rerun GPT-2 or SynthID. Detector numbers stay on the sealed Gate v2 scorecard. Mixed letter/emoji fixtures in the v1 catalog do not restore after the listed Unicode sanitizers; `digits` has no eligible site, so mix is a no-op. + +```text +fuckmark robustness +fuckmark robustness --json +``` + +Full reference: [`docs/robustness.md`](docs/robustness.md). Protocol and freeze: [`specs/fuckmark-robustness-bench-v1.protocol.md`](specs/fuckmark-robustness-bench-v1.protocol.md). + ## Guard model input (LLM prompt-injection smuggling) Hidden Unicode — especially Unicode **tag** characters — can smuggle a second instruction into a prompt that a human reviewer will not see. `protect()` strips that payload before the text reaches a model, and can decode the smuggled ASCII so you can log it. It is not a semantic jailbreak detector. @@ -260,7 +271,7 @@ Eligible Latin, Greek, Cyrillic, Han, Kana, Hangul syllable, and emoji clusters ## Research -Frozen evidence, hashes, and protocols: [`docs/research.md`](docs/research.md). Product contract: [`docs/product-contract.md`](docs/product-contract.md). CLI: [`docs/cli.md`](docs/cli.md). Limits matrix: [`docs/limits.md`](docs/limits.md). Demo: [`docs/demo.html`](docs/demo.html). +Frozen evidence, hashes, and protocols: [`docs/research.md`](docs/research.md). Public sanitizer-restore bench: [`docs/robustness.md`](docs/robustness.md). Product contract: [`docs/product-contract.md`](docs/product-contract.md). CLI: [`docs/cli.md`](docs/cli.md). Limits matrix: [`docs/limits.md`](docs/limits.md). Demo: [`docs/demo.html`](docs/demo.html). ## Language bindings (Rust scan ABI) diff --git a/docs/cli.md b/docs/cli.md index 94a03105..c942239e 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -70,6 +70,7 @@ fuckmark guard --json < messages.json fuckmark normalize --receipt < notes.txt fuckmark clipboard scan fuckmark clipboard watch --clean +fuckmark robustness --json fuckmark web fuckmark --text "I don’t agree." --status ``` @@ -157,6 +158,18 @@ fuckmark clipboard watch --once --exit-on-find `scan` reads once. `clean` rewrites the clipboard in place when findings exist. `watch` polls (default 0.5s) and warns when the clipboard contents change to a hidden payload. `--select security|all|CATS` chooses categories. Exit status for this subcommand: `0` clean, `1` findings (including after a successful clean), `2` usage, `3` clipboard tool missing or failed. +### `fuckmark robustness` + +Replay the public sanitizer-restore bench (`fuckmark-robustness-bench-v1`). 10 short fixtures times 18 Unicode sanitizer attacks. Does not rerun GPT-2 or SynthID; the Gate v2 confirmation scorecard is hashed as a sealed detector track. Reference: [`robustness.md`](robustness.md). + +```text +fuckmark robustness +fuckmark robustness --json +fuckmark robustness --fixture digits --attack identity +``` + +Exit status for this subcommand: `0` cells match frozen vectors, `1` mix or sanitizer drift, `2` usage. A file named `robustness` is not read; use `fuckmark --file robustness`. + ### `fuckmark web` Open the local browser tool (same UI as `docs/mark.html`). Aimed at beginners who prefer a page over pipes and flags. The server also exposes a Python API: `GET /api/health`, `POST /api/remove-marks`, `POST /api/scan`, `POST /api/guard` (sanitize text or chat messages before a model call), and `POST /api/normalize`. Hidden-Unicode reveal is `/scan.html` (WASM engine with `scan.js` fallback; zero-install copy at `docs/scan.html`). diff --git a/docs/limits.md b/docs/limits.md index 152254de..14d42398 100644 --- a/docs/limits.md +++ b/docs/limits.md @@ -23,7 +23,7 @@ No-install walkthrough of these limits: [`demo.html`](demo.html). | Mn then Me then frozen cf_strip then UnicodeSanitizer | **yes** (closed-set remainder) | n/a | | required-bundle then UnicodeSanitizer | **no** (exploratory 0/192) | n/a (bundle already strips marks) | -Live mix (`u034f-ufe00-cc-me-cf-ia-letter-alt-v1`) leaves Me/Cc/Cf residuals after Mn-strip, default-ignorable strip, UnicodeSanitizer orderings, Mn then Me then UnicodeSanitizer, and the required sanitizer bundle. UnicodeSanitizer turns U+FFF9-U+FFFB into spaces, so frozen Cf-strip after that path does not restore. Frozen Gate v2 confirmation is the historical mark-only arm and is not rewritten. Exploratory rescores: `evidence/cycle8-dual-layer-stress-exploratory-2026-08-28/`, `evidence/cycle8-combo-stress-exploratory-n192-2026-08-28/` (historical triple-layer detector scores), and `evidence/cycle8-quad-layer-restore-exploratory-2026-08-29/` (four-layer restore census). +Live mix (`u034f-ufe00-cc-me-cf-ia-letter-alt-v1`) leaves Me/Cc/Cf residuals after Mn-strip, default-ignorable strip, UnicodeSanitizer orderings, Mn then Me then UnicodeSanitizer, and the required sanitizer bundle. UnicodeSanitizer turns U+FFF9-U+FFFB into spaces, so frozen Cf-strip after that path does not restore. Frozen Gate v2 confirmation is the historical mark-only arm and is not rewritten. The public hashed restore grid is `fuckmark robustness` ([`robustness.md`](robustness.md)); it does not rerun detectors. Exploratory rescores: `evidence/cycle8-dual-layer-stress-exploratory-2026-08-28/`, `evidence/cycle8-combo-stress-exploratory-n192-2026-08-28/` (historical triple-layer detector scores), and `evidence/cycle8-quad-layer-restore-exploratory-2026-08-29/` (four-layer restore census). ## L02 — Input domain diff --git a/docs/research.md b/docs/research.md index 742635f4..a8b60b71 100644 --- a/docs/research.md +++ b/docs/research.md @@ -1,6 +1,6 @@ # Research archive -This directory's user manuals are [`install.md`](install.md), [`cli.md`](cli.md), [`limits.md`](limits.md), [`website.md`](website.md), and the no-install [`demo.html`](demo.html). The demo uses fixed samples and frozen scores; it is not a live detector. +This directory's user manuals are [`install.md`](install.md), [`cli.md`](cli.md), [`limits.md`](limits.md), [`website.md`](website.md), the public sanitizer-restore bench [`robustness.md`](robustness.md), and the no-install [`demo.html`](demo.html). The demo uses fixed samples and frozen scores; it is not a live detector. Frozen scientific records are not the user manual. They stay in the repository so the product claims can be audited. @@ -15,6 +15,7 @@ Frozen scientific records are not the user manual. They stay in the repository s ## Evidence index +- Public sanitizer-restore bench (`fuckmark-robustness-bench-v1`, model-free, does not rerun detectors): [`robustness.md`](robustness.md), [`specs/fuckmark-robustness-bench-v1.protocol.md`](../specs/fuckmark-robustness-bench-v1.protocol.md) - Gate v2 confirmation: [`evidence/cycle8-gate-v2-confirmation-2026-08-27/README.md`](../evidence/cycle8-gate-v2-confirmation-2026-08-27/README.md) - DeepMind 30-key transfer: [`evidence/cycle8-mix-deepmind-transfer-2026-08-27/README.md`](../evidence/cycle8-mix-deepmind-transfer-2026-08-27/README.md) - DistilGPT2 n=16: [`evidence/cycle8-mix-distilgpt2-1090000-n16-2026-08-27/README.md`](../evidence/cycle8-mix-distilgpt2-1090000-n16-2026-08-27/README.md) diff --git a/docs/robustness.md b/docs/robustness.md new file mode 100644 index 00000000..9128d657 --- /dev/null +++ b/docs/robustness.md @@ -0,0 +1,92 @@ +# Public sanitizer-restore bench + +`fuckmark robustness` replays `fuckmark-robustness-bench-v1`: a hashed, model-free +grid of short fixtures times Unicode sanitizer attacks. It answers whether the +live five-layer mix still restores the original source after those attacks. + +It does **not** rerun GPT-2, SynthID, or any neural detector. Detector numbers +stay on the sealed Gate v2 confirmation scorecard. This is a restore and +residual-category bench, not a platform-evasion rate. + +Protocol: [`specs/fuckmark-robustness-bench-v1.protocol.md`](../specs/fuckmark-robustness-bench-v1.protocol.md). +Vectors (sources as codepoint arrays only): +[`specs/fuckmark-robustness-bench-v1.vectors.json`](../specs/fuckmark-robustness-bench-v1.vectors.json). +Freeze hashes: [`specs/fuckmark-robustness-bench-v1.freeze.json`](../specs/fuckmark-robustness-bench-v1.freeze.json). + +## Command line + +```text +fuckmark robustness +fuckmark robustness --json +fuckmark robustness -q +fuckmark robustness --fixture digits --attack identity +fuckmark robustness --json --fixture ascii_prose --attack mn_me_us_cf +``` + +Default is every public fixture and every frozen attack (180 cells). `--fixture` +and `--attack` are repeatable. `--json` writes the full report to stdout. Human +status goes to stderr. `-q` is silent on success. + +A file named `robustness` in the current directory is not read; use +`fuckmark --file robustness` for that. + +## What a cell records + +For source `S` and attack `A`: mix `M`, then `T = A(M)`. + +| Field | Meaning | +| --- | --- | +| `restores_source` | `T == S` | +| `mix_projection_equals_source` | visible projection of `M` equals `S` | +| `projection_equals_source` | visible projection of `T` equals `S` | +| `carrier_detected` | closed-set FuckMark insertion scan of `T` | +| `residual_categories` | `fuckmark-hidden-scan-v1` categories still in `T` | +| `mix_sha256` / `output_sha256` | SHA-256 of UTF-8 `M` and `T` | + +Visible projection is `project_visible_v1` (approved carriers removed; Me, Cc, +and Cf residuals remain). UnicodeSanitizer turns interlinear annotation +controls into spaces, so Cf-strip after that path cannot rebuild the original +spacing. + +## Frozen outcome + +Live measure of the v1 grid: + +- 10 fixtures, 18 attacks, 180 cells +- mix visible projection holds on every cell (180/180) +- `digits` has no eligible letter or emoji site, so mix is a no-op and every + attack restores (18/18) +- mixed letter/emoji fixtures do not restore under the catalog +- `ascii_prose` / `mn_me_us_cf`: restore false, carrier false, projection false + (spaces from UnicodeSanitizer) + +Sealed detector track (hashed, not rerun): Gate v2 confirmation identity +**188/192** detected; mix **0/192** after required sanitizers; visible +**192/192**. Do not rewrite that scorecard. + +## Exit status + +| Status | Meaning | +| ---: | ---: | +| 0 | Selected cells match the frozen vectors and the sealed scorecard hash still matches. | +| 1 | Mix or sanitizer drift, or the sealed scorecard hash no longer matches. | +| 2 | Usage (unknown `--fixture` / `--attack`, or argparse). | + +## Python + +```text +from fuckmark.robustness import measure_cell, run_robustness_bench + +cell = measure_cell("ascii_prose", "mn_strip") +report = run_robustness_bench() +``` + +`tests/test_robustness.py` binds the freeze hashes and replays every cell. + +## Honest limits + +This v1 freeze is local and deterministic. It is not a leaderboard of third-party +detectors. A later track may add opt-in adapters; it is not this freeze. Short +public fixtures are not the GPT-2 Gate v2 corpus. Sanitizer restore is not the +same as detector miss, and detector miss on GPT-2 / SynthID is not a general +AI-detector rate. diff --git a/fuckmark/cli.py b/fuckmark/cli.py index b234259f..c611b5f3 100644 --- a/fuckmark/cli.py +++ b/fuckmark/cli.py @@ -36,6 +36,7 @@ from .guard import run_guard_argv from .lint import run_lint_argv from .product.normalize import run_normalize_argv +from .robustness import run_robustness_argv from .web import run_web_argv from .product.domain import ( PRODUCT_MAX_INPUT_CHARS, @@ -233,6 +234,7 @@ def _parser() -> argparse.ArgumentParser: " fuckmark guard --json < messages.json\n" " fuckmark normalize --receipt < notes.txt\n" " fuckmark clipboard watch --clean\n" + " fuckmark robustness --json\n" " fuckmark web\n" "\n" "Latin, Greek, Cyrillic, Han, Kana, Hangul syllable, and emoji sites are processed\n" @@ -256,6 +258,7 @@ def _parser() -> argparse.ArgumentParser: "Use fuckmark guard to strip hidden Unicode from text or JSON before a model call.\n" "Use fuckmark normalize to NFC-fold, optionally skeleton-fold lookalikes, and strip hidden Unicode.\n" "Use fuckmark clipboard to scan, clean, or watch the OS clipboard for hidden Unicode.\n" + "Use fuckmark robustness to replay the public sanitizer-restore bench (no detector rerun).\n" "Use fuckmark web to open the local browser tool (beginner-friendly).\n" "That server also runs the Python detect/strip API.\n" "Mn-strip, default-ignorable strip, UnicodeSanitizer combinations, and Cf-strip after UnicodeSanitizer leave Me/Cc/Cf residuals and spaces." @@ -972,6 +975,10 @@ def _run( _ensure_utf8(output) _ensure_utf8(errors) return run_clipboard_argv(raw_argv[1:], output, errors) + if raw_argv and raw_argv[0] == "robustness": + _ensure_utf8(output) + _ensure_utf8(errors) + return run_robustness_argv(raw_argv[1:], output, errors) if raw_argv and raw_argv[0] == "lint": _ensure_utf8(output) _ensure_utf8(errors) diff --git a/fuckmark/robustness.py b/fuckmark/robustness.py new file mode 100644 index 00000000..03a42ffd --- /dev/null +++ b/fuckmark/robustness.py @@ -0,0 +1,433 @@ +from __future__ import annotations + +import argparse +import json +import re +import unicodedata +from collections.abc import Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import TextIO + +from .cycle8.letter_mix import LETTER_MIX_MECHANISM_ID, apply_letter_alternating_mix +from .cycle8.unicode_meta import is_default_ignorable_v1 +from .hashing import sha256_file, sha256_json, sha256_text +from .product.detect import detect_fuckmark_insertions +from .product.scan import scan_hidden_characters +from .product.visible_projection import project_visible_v1 +from .sanitizer_robustness import nfkc_normalize, strip_unicode_format_characters + + +ROBUSTNESS_ALGORITHM_VERSION = "fuckmark-robustness-bench-v1" +ROBUSTNESS_EXIT_OK = 0 +ROBUSTNESS_EXIT_MISMATCH = 1 +ROBUSTNESS_EXIT_USAGE = 2 +SPEC_DIR = Path(__file__).resolve().parents[1] / "specs" +PROTOCOL_PATH = SPEC_DIR / "fuckmark-robustness-bench-v1.protocol.md" +VECTORS_PATH = SPEC_DIR / "fuckmark-robustness-bench-v1.vectors.json" +FREEZE_PATH = SPEC_DIR / "fuckmark-robustness-bench-v1.freeze.json" +SEALED_DETECTOR_SCORECARD_PATH = "specs/cycle8/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json" +SEALED_DETECTOR_SCORECARD_HASH = "3df98598fa1f9fb3951029f105b43dfd5f3e83a9ec69fd5c160b31686b0ad6c9" +_UNICODE_SANITIZER_PATTERN = re.compile( + "[\u00a0\u1680\u180e\u2000-\u200b\u200c\u200d\u200e\u200f\u2060\u2063\u202f\u205f\u3000" + "\ufeff\uffa0\ufff9\ufffa\ufffb\ufe00\ufe01\ufe02\ufe03\ufe04\ufe05\ufe06\ufe07\ufe08\ufe09" + "\ufe0a\ufe0b\ufe0c\ufe0d\ufe0e\ufe0f\u3164\u202a\u202b\u202c\u202d\u202e\u202f]" +) + +ATTACK_IDS = ( + "identity", + "mn_strip", + "default_ignorable_strip", + "nfc", + "nfkc", + "nfkd", + "cf_strip", + "me_strip", + "cc_strip", + "unicode_sanitizer", + "mn_then_us", + "di_then_us", + "us_then_mn", + "required_bundle", + "required_bundle_then_us", + "mn_me_us", + "mn_me_us_cf", + "di_me_us_cf", +) + +_FIXTURES: tuple[tuple[str, tuple[int, ...]], ...] = ( + ("ascii_prose", tuple(ord(ch) for ch in "I do not agree.")), + ("ascii_clause", tuple(ord(ch) for ch in "We cannot continue.")), + ("nfd_latin", (0x65, 0x0301)), + ("emoji", (0x1F600,)), + ("greek", (0x03B1, 0x03B2)), + ("cyrillic", (0x0430, 0x0431)), + ("han", (0x4E2D,)), + ("hangul", (0xAC00,)), + ("url_mixed", tuple(ord(ch) for ch in "See https://example.com/x I do not agree.")), + ("digits", tuple(ord(ch) for ch in "123.")), +) + + +@dataclass(frozen=True, slots=True) +class RobustnessCell: + fixture_id: str + attack_id: str + source: str + mixed: str + attacked: str + restores_source: bool + mix_projection_equals_source: bool + projection_equals_source: bool + carrier_detected: bool + residual_categories: tuple[str, ...] + mix_sha256: str + output_sha256: str + + +def fixture_ids() -> tuple[str, ...]: + return tuple(item[0] for item in _FIXTURES) + + +def fixture_source(fixture_id: str) -> str: + for name, codes in _FIXTURES: + if name == fixture_id: + return "".join(chr(code) for code in codes) + raise ValueError(f"unknown robustness fixture: {fixture_id}") + + +def _strip_category(text: str, category: str) -> str: + return "".join(character for character in text if unicodedata.category(character) != category) + + +def strip_nonspacing_marks(text: str) -> str: + if not isinstance(text, str): + raise TypeError("text must be a string") + return _strip_category(text, "Mn") + + +def strip_enclosing_marks(text: str) -> str: + if not isinstance(text, str): + raise TypeError("text must be a string") + return _strip_category(text, "Me") + + +def strip_other_controls(text: str) -> str: + if not isinstance(text, str): + raise TypeError("text must be a string") + return _strip_category(text, "Cc") + + +def strip_default_ignorable(text: str) -> str: + if not isinstance(text, str): + raise TypeError("text must be a string") + return "".join(character for character in text if not is_default_ignorable_v1(ord(character))) + + +def unicode_sanitizer(text: str) -> str: + if not isinstance(text, str): + raise TypeError("text must be a string") + cleaned = unicodedata.normalize("NFC", text) + cleaned = _UNICODE_SANITIZER_PATTERN.sub(" ", cleaned) + cleaned = re.sub(" +", " ", cleaned) + return _strip_category(cleaned, "Cc") + + +def apply_required_sanitizer_bundle(text: str) -> str: + if not isinstance(text, str): + raise TypeError("text must be a string") + cleaned = unicodedata.normalize("NFC", text) + cleaned = unicodedata.normalize("NFKC", cleaned) + cleaned = strip_nonspacing_marks(cleaned) + cleaned = strip_default_ignorable(cleaned) + return strip_unicode_format_characters(cleaned) + + +def apply_attack(attack_id: str, text: str) -> str: + if not isinstance(text, str): + raise TypeError("text must be a string") + if attack_id == "identity": + return text + if attack_id == "mn_strip": + return strip_nonspacing_marks(text) + if attack_id == "default_ignorable_strip": + return strip_default_ignorable(text) + if attack_id == "nfc": + return unicodedata.normalize("NFC", text) + if attack_id == "nfkc": + return nfkc_normalize(text) + if attack_id == "nfkd": + return unicodedata.normalize("NFKD", text) + if attack_id == "cf_strip": + return strip_unicode_format_characters(text) + if attack_id == "me_strip": + return strip_enclosing_marks(text) + if attack_id == "cc_strip": + return strip_other_controls(text) + if attack_id == "unicode_sanitizer": + return unicode_sanitizer(text) + if attack_id == "mn_then_us": + return unicode_sanitizer(strip_nonspacing_marks(text)) + if attack_id == "di_then_us": + return unicode_sanitizer(strip_default_ignorable(text)) + if attack_id == "us_then_mn": + return strip_nonspacing_marks(unicode_sanitizer(text)) + if attack_id == "required_bundle": + return apply_required_sanitizer_bundle(text) + if attack_id == "required_bundle_then_us": + return unicode_sanitizer(apply_required_sanitizer_bundle(text)) + if attack_id == "mn_me_us": + return unicode_sanitizer(strip_enclosing_marks(strip_nonspacing_marks(text))) + if attack_id == "mn_me_us_cf": + return strip_unicode_format_characters( + unicode_sanitizer(strip_enclosing_marks(strip_nonspacing_marks(text))) + ) + if attack_id == "di_me_us_cf": + return strip_unicode_format_characters( + unicode_sanitizer(strip_enclosing_marks(strip_default_ignorable(text))) + ) + raise ValueError(f"unknown robustness attack: {attack_id}") + + +def measure_cell(fixture_id: str, attack_id: str) -> RobustnessCell: + if attack_id not in ATTACK_IDS: + raise ValueError(f"unknown robustness attack: {attack_id}") + source = fixture_source(fixture_id) + mixed = apply_letter_alternating_mix(source) + attacked = apply_attack(attack_id, mixed) + scan = scan_hidden_characters(attacked, max_findings=max(len(attacked), 1)) + return RobustnessCell( + fixture_id=fixture_id, + attack_id=attack_id, + source=source, + mixed=mixed, + attacked=attacked, + restores_source=attacked == source, + mix_projection_equals_source=project_visible_v1(mixed) == source, + projection_equals_source=project_visible_v1(attacked) == source, + carrier_detected=detect_fuckmark_insertions(attacked).detected, + residual_categories=scan.active_categories(), + mix_sha256=sha256_text(mixed), + output_sha256=sha256_text(attacked), + ) + + +def cell_expect(cell: RobustnessCell) -> dict[str, object]: + return { + "restores_source": cell.restores_source, + "mix_projection_equals_source": cell.mix_projection_equals_source, + "projection_equals_source": cell.projection_equals_source, + "carrier_detected": cell.carrier_detected, + "residual_categories": list(cell.residual_categories), + "mix_sha256": cell.mix_sha256, + "output_sha256": cell.output_sha256, + } + + +def cell_dict(cell: RobustnessCell) -> dict[str, object]: + return { + "id": f"{cell.fixture_id}/{cell.attack_id}", + "fixture": cell.fixture_id, + "attack": cell.attack_id, + "expect": cell_expect(cell), + } + + +def sealed_detector_track() -> dict[str, object]: + path = Path(__file__).resolve().parents[1] / SEALED_DETECTOR_SCORECARD_PATH + payload = json.loads(path.read_text(encoding="utf-8")) + return { + "id": "cycle8-gate-v2-confirmation-scorecard-v1", + "path": SEALED_DETECTOR_SCORECARD_PATH, + "file_sha256": sha256_file(path), + "scorecard_hash": payload["scorecard_hash"], + "expected_scorecard_hash": SEALED_DETECTOR_SCORECARD_HASH, + "identity_watermarked_detected": payload["identity_watermarked_detected"], + "identity_watermarked_of": 192, + "mix_watermarked_detected_by_required_sanitizer": payload["mix_watermarked_detected_by_required_sanitizer"], + "visible_pass_rate": payload["visible_pass_rate"], + "notes": ( + "Sealed historical GPT-2 / SynthID Gate v2 scorecard. " + "This bench does not rerun detectors. Do not rewrite the scorecard." + ), + } + + +def iter_cells( + *, + fixtures: Sequence[str] | None = None, + attacks: Sequence[str] | None = None, +) -> list[RobustnessCell]: + selected_fixtures = tuple(fixtures) if fixtures is not None else fixture_ids() + selected_attacks = tuple(attacks) if attacks is not None else ATTACK_IDS + unknown_fixtures = [name for name in selected_fixtures if name not in fixture_ids()] + if unknown_fixtures: + raise ValueError(f"unknown robustness fixtures: {unknown_fixtures}") + unknown_attacks = [name for name in selected_attacks if name not in ATTACK_IDS] + if unknown_attacks: + raise ValueError(f"unknown robustness attacks: {unknown_attacks}") + return [measure_cell(fixture_id, attack_id) for fixture_id in selected_fixtures for attack_id in selected_attacks] + + +def build_vectors_payload() -> dict[str, object]: + cells = iter_cells() + fixtures = [] + for fixture_id, codes in _FIXTURES: + sample = next(cell for cell in cells if cell.fixture_id == fixture_id) + fixtures.append( + { + "id": fixture_id, + "source": list(codes), + "mix_sha256": sample.mix_sha256, + "mix_projection_equals_source": sample.mix_projection_equals_source, + "mixed": sample.mixed != sample.source, + } + ) + return { + "algorithm_version": ROBUSTNESS_ALGORITHM_VERSION, + "mix_mechanism_id": LETTER_MIX_MECHANISM_ID, + "attacks": list(ATTACK_IDS), + "fixtures": fixtures, + "cells": [cell_dict(cell) for cell in cells], + } + + +def load_vectors() -> dict[str, object]: + return json.loads(VECTORS_PATH.read_text(encoding="utf-8")) + + +def compare_to_vectors(cells: Sequence[RobustnessCell], vectors: dict[str, object]) -> list[dict[str, object]]: + expected = {item["id"]: item["expect"] for item in vectors["cells"]} + mismatches: list[dict[str, object]] = [] + for cell in cells: + key = f"{cell.fixture_id}/{cell.attack_id}" + want = expected.get(key) + got = cell_expect(cell) + if want != got: + mismatches.append({"id": key, "expected": want, "actual": got}) + return mismatches + + +def summary_dict(cells: Sequence[RobustnessCell], mismatches: Sequence[dict[str, object]]) -> dict[str, object]: + restores = sum(1 for cell in cells if cell.restores_source) + mix_ok = sum(1 for cell in cells if cell.mix_projection_equals_source) + return { + "fixtures": len({cell.fixture_id for cell in cells}), + "attacks": len({cell.attack_id for cell in cells}), + "cells": len(cells), + "restores_source": restores, + "mix_projection_equals_source": mix_ok, + "mismatches": len(mismatches), + } + + +def run_robustness_bench( + *, + fixtures: Sequence[str] | None = None, + attacks: Sequence[str] | None = None, +) -> dict[str, object]: + cells = iter_cells(fixtures=fixtures, attacks=attacks) + vectors = load_vectors() + mismatches = compare_to_vectors(cells, vectors) + sealed = sealed_detector_track() + sealed_ok = sealed["scorecard_hash"] == sealed["expected_scorecard_hash"] + return { + "algorithm_version": ROBUSTNESS_ALGORITHM_VERSION, + "mix_mechanism_id": LETTER_MIX_MECHANISM_ID, + "summary": summary_dict(cells, mismatches), + "sealed_detector_track": sealed, + "sealed_detector_ok": sealed_ok, + "mismatches": mismatches, + "cells": [cell_dict(cell) for cell in cells], + } + + +def freeze_bindings() -> dict[str, object]: + vectors = load_vectors() + return { + "algorithm_version": ROBUSTNESS_ALGORITHM_VERSION, + "protocol_sha256": sha256_file(PROTOCOL_PATH), + "vectors_file_sha256": sha256_file(VECTORS_PATH), + "vectors_canonical_sha256": sha256_json(vectors), + "sealed_detector_scorecard_path": SEALED_DETECTOR_SCORECARD_PATH, + "sealed_detector_scorecard_hash": SEALED_DETECTOR_SCORECARD_HASH, + "sealed_detector_scorecard_file_sha256": sha256_file( + Path(__file__).resolve().parents[1] / SEALED_DETECTOR_SCORECARD_PATH + ), + } + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + prog="fuckmark robustness", + description=( + "Run the public sanitizer-restore robustness bench " + "(fuckmark-robustness-bench-v1). Does not rerun GPT-2 or SynthID." + ), + ) + parser.add_argument("--json", action="store_true", dest="json_mode") + parser.add_argument("-q", "--quiet", action="store_true") + parser.add_argument( + "--fixture", + action="append", + dest="fixtures", + help="fixture id; repeatable. Default: all public fixtures.", + ) + parser.add_argument( + "--attack", + action="append", + dest="attacks", + help="attack id; repeatable. Default: the frozen attack catalog.", + ) + return parser + + +def _emit(stream: TextIO, text: str) -> None: + stream.write(text) + if not text.endswith("\n"): + stream.write("\n") + stream.flush() + + +def run_robustness_argv(argv: list[str], output: TextIO, errors: TextIO) -> int: + try: + arguments = _parser().parse_args(argv) + except SystemExit as error: + code = error.code + if code is None: + return ROBUSTNESS_EXIT_OK + return int(code) if isinstance(code, int) else ROBUSTNESS_EXIT_USAGE + try: + report = run_robustness_bench(fixtures=arguments.fixtures, attacks=arguments.attacks) + except ValueError as error: + errors.write(f"FuckMark: {error}\n") + errors.flush() + return ROBUSTNESS_EXIT_USAGE + summary = report["summary"] + assert isinstance(summary, dict) + mismatches = report["mismatches"] + assert isinstance(mismatches, list) + if arguments.json_mode: + _emit(output, json.dumps(report, ensure_ascii=False)) + elif not arguments.quiet: + _emit( + errors, + ( + f"FuckMark robustness: {summary['cells']} cells, " + f"{summary['restores_source']} restore the source, " + f"{summary['mismatches']} mismatches versus frozen vectors." + ), + ) + _emit( + errors, + ( + "Sealed detector track is Gate v2 confirmation " + f"({report['sealed_detector_track']['identity_watermarked_detected']}/192 identity; " + "mix required-sanitizer detections stay 0). Detectors were not rerun." + ), + ) + if mismatches: + for item in mismatches[:12]: + _emit(errors, f" mismatch {item['id']}") + failed = bool(mismatches) or not report["sealed_detector_ok"] + return ROBUSTNESS_EXIT_MISMATCH if failed else ROBUSTNESS_EXIT_OK diff --git a/specs/fuckmark-robustness-bench-v1.freeze.json b/specs/fuckmark-robustness-bench-v1.freeze.json new file mode 100644 index 00000000..2c0f2eda --- /dev/null +++ b/specs/fuckmark-robustness-bench-v1.freeze.json @@ -0,0 +1,12 @@ +{ + "algorithm_version": "fuckmark-robustness-bench-v1", + "freeze_record_version": "fuckmark-robustness-bench-v1-freeze-v1", + "frozen_at_utc": "2026-09-02T03:30:00+00:00", + "protocol_id": "fuckmark-robustness-bench-v1", + "protocol_sha256": "9808da06fce59d952c98464dcdd0f0df45968b363b57d00307c01ed794813cbe", + "vectors_file_sha256": "c84fdb3f4ab0a0582b9bfc6254af1f7957d88682d01e0abb40922cf17643aa3c", + "vectors_canonical_sha256": "ceb7a7e0ac5608740ddf4271994315b28c4b052d20e68dba4e0a45c2e8ed8ba1", + "sealed_detector_scorecard_path": "specs/cycle8/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json", + "sealed_detector_scorecard_hash": "3df98598fa1f9fb3951029f105b43dfd5f3e83a9ec69fd5c160b31686b0ad6c9", + "sealed_detector_scorecard_file_sha256": "818fe948cd126c83fe8508847b581ebe5778073555752974fbb7e99ebfa8e6fa" +} diff --git a/specs/fuckmark-robustness-bench-v1.protocol.md b/specs/fuckmark-robustness-bench-v1.protocol.md new file mode 100644 index 00000000..f5427790 --- /dev/null +++ b/specs/fuckmark-robustness-bench-v1.protocol.md @@ -0,0 +1,80 @@ +# fuckmark-robustness-bench-v1 + +Public sanitizer-restore robustness bench for the live five-layer mix. +Hash-binding freeze record: `specs/fuckmark-robustness-bench-v1.freeze.json`. +Conformance vectors: `specs/fuckmark-robustness-bench-v1.vectors.json` +(source strings as codepoint arrays only). + +This bench answers: after the public mix, do standard Unicode sanitizers restore +the original source? It does **not** rerun GPT-2, SynthID, or any neural +detector. Detector numbers stay on the sealed Gate v2 scorecard. + +## 1. Identity + +- Algorithm version: `fuckmark-robustness-bench-v1` +- Mix mechanism: `u034f-ufe00-cc-me-cf-ia-letter-alt-v1` +- Python reference: `fuckmark.robustness` +- CLI: `fuckmark robustness` +- Contact: `Fhelp@q1z.org` + +A later v2 may add detector adapters from `fuckmark.adapters` as an opt-in +track. This v1 freeze is local, deterministic, and model-free. + +## 2. Cell + +For each fixture S and attack A: + +1. Mix: `M = apply_letter_alternating_mix(S)`. +2. Attack: `T = A(M)`. +3. Record: + - `restores_source`: `T == S` + - `mix_projection_equals_source`: visible projection of `M` equals `S` + - `projection_equals_source`: visible projection of `T` equals `S` + - `carrier_detected`: closed-set FuckMark insertion scan of `T` + - `residual_categories`: `fuckmark-hidden-scan-v1` categories still present in `T` + - `mix_sha256` / `output_sha256`: SHA-256 of UTF-8 `M` and `T` + +Visible projection is `project_visible_v1` (approved carriers removed; Me, Cc, +and Cf residuals remain). + +## 3. Attacks + +Applied to the mixed string, in this order in the catalog: + +1. `identity` — no sanitizer +2. `mn_strip` — drop general category Mn +3. `default_ignorable_strip` — drop default-ignorable codepoints +4. `nfc` / `nfkc` / `nfkd` +5. `cf_strip` — drop general category Cf +6. `me_strip` — drop general category Me +7. `cc_strip` — drop general category Cc +8. `unicode_sanitizer` — frozen copy of lm-watermarking `UnicodeSanitizer` + (NFC, BMP regex to space, collapse spaces, drop Cc) +9. `mn_then_us` / `di_then_us` / `us_then_mn` — orderings of Mn/DI strip and + UnicodeSanitizer +10. `required_bundle` — NFC, NFKC, Mn-strip, default-ignorable strip, Cf-strip +11. `required_bundle_then_us` +12. `mn_me_us` — Mn-strip, Me-strip, UnicodeSanitizer +13. `mn_me_us_cf` / `di_me_us_cf` — that path plus frozen Cf-strip + +UnicodeSanitizer turns interlinear annotation controls into spaces, so Cf-strip +after it cannot rebuild the original spacing. + +## 4. Fixtures + +Public fixtures are short. They are not the GPT-2 Gate v2 corpus. `digits` has +no eligible letter or emoji site, so mix is a no-op and every attack restores. +Mixed letter/emoji fixtures must not restore under the attacks above. + +## 5. Sealed detector track + +The Gate v2 confirmation scorecard remains the historical GPT-2 / SynthID +record (identity 188/192 detected; mix 0/192 after required sanitizers; +visible 192/192). This bench hashes that file and the inner `scorecard_hash`. +It does not regenerate those corpora. + +## 6. Exit status + +`fuckmark robustness` exits 0 when every selected cell matches the frozen +vectors and the sealed scorecard hash still matches. Exit 1 is a mismatch +(mix or sanitizer drift). Exit 2 is usage. diff --git a/specs/fuckmark-robustness-bench-v1.vectors.json b/specs/fuckmark-robustness-bench-v1.vectors.json new file mode 100644 index 00000000..299d8ee9 --- /dev/null +++ b/specs/fuckmark-robustness-bench-v1.vectors.json @@ -0,0 +1,3363 @@ +{ + "algorithm_version": "fuckmark-robustness-bench-v1", + "mix_mechanism_id": "u034f-ufe00-cc-me-cf-ia-letter-alt-v1", + "attacks": [ + "identity", + "mn_strip", + "default_ignorable_strip", + "nfc", + "nfkc", + "nfkd", + "cf_strip", + "me_strip", + "cc_strip", + "unicode_sanitizer", + "mn_then_us", + "di_then_us", + "us_then_mn", + "required_bundle", + "required_bundle_then_us", + "mn_me_us", + "mn_me_us_cf", + "di_me_us_cf" + ], + "fixtures": [ + { + "id": "ascii_prose", + "source": [ + 73, + 32, + 100, + 111, + 32, + 110, + 111, + 116, + 32, + 97, + 103, + 114, + 101, + 101, + 46 + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "ascii_clause", + "source": [ + 87, + 101, + 32, + 99, + 97, + 110, + 110, + 111, + 116, + 32, + 99, + 111, + 110, + 116, + 105, + 110, + 117, + 101, + 46 + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "nfd_latin", + "source": [ + 101, + 769 + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "emoji", + "source": [ + 128512 + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "greek", + "source": [ + 945, + 946 + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "cyrillic", + "source": [ + 1072, + 1073 + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "han", + "source": [ + 20013 + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "hangul", + "source": [ + 44032 + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "url_mixed", + "source": [ + 83, + 101, + 101, + 32, + 104, + 116, + 116, + 112, + 115, + 58, + 47, + 47, + 101, + 120, + 97, + 109, + 112, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 120, + 32, + 73, + 32, + 100, + 111, + 32, + 110, + 111, + 116, + 32, + 97, + 103, + 114, + 101, + 101, + 46 + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "digits", + "source": [ + 49, + 50, + 51, + 46 + ], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "mix_projection_equals_source": true, + "mixed": false + } + ], + "cells": [ + { + "id": "ascii_prose/identity", + "fixture": "ascii_prose", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d" + } + }, + { + "id": "ascii_prose/mn_strip", + "fixture": "ascii_prose", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "de800a87e319075d777c57ade25aa784cf7dd3136633a7c315d66acb87dfb78c" + } + }, + { + "id": "ascii_prose/default_ignorable_strip", + "fixture": "ascii_prose", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "de800a87e319075d777c57ade25aa784cf7dd3136633a7c315d66acb87dfb78c" + } + }, + { + "id": "ascii_prose/nfc", + "fixture": "ascii_prose", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d" + } + }, + { + "id": "ascii_prose/nfkc", + "fixture": "ascii_prose", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d" + } + }, + { + "id": "ascii_prose/nfkd", + "fixture": "ascii_prose", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d" + } + }, + { + "id": "ascii_prose/cf_strip", + "fixture": "ascii_prose", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "293f2ed34f680ff94635a6c0bf1f0fbabd39d5096dc7984c944808733a9856a1" + } + }, + { + "id": "ascii_prose/me_strip", + "fixture": "ascii_prose", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "75d683d5bc4fd3bcf1bd62e1999c12eed2b52f825747824bbf772ec5ee0ef758" + } + }, + { + "id": "ascii_prose/cc_strip", + "fixture": "ascii_prose", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "ea501f223eec60f3526b8e8b728cbbd57eb6368ac2e519e7425f52ab2e31ebe3" + } + }, + { + "id": "ascii_prose/unicode_sanitizer", + "fixture": "ascii_prose", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "7bb52edfb678cc9e2ee18f5425f6d6ee7f496a085b954eba4e27f2121812f4a1" + } + }, + { + "id": "ascii_prose/mn_then_us", + "fixture": "ascii_prose", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "a6c9d5f6deb1fac818d3154f2f1dae9bef79bb6b77e280bcdc3acb52b6ec8430" + } + }, + { + "id": "ascii_prose/di_then_us", + "fixture": "ascii_prose", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "a6c9d5f6deb1fac818d3154f2f1dae9bef79bb6b77e280bcdc3acb52b6ec8430" + } + }, + { + "id": "ascii_prose/us_then_mn", + "fixture": "ascii_prose", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "d1e2a542701f9e9a2f5a46742e9a1de8fd1c187576e8e190b79967b735b3957a" + } + }, + { + "id": "ascii_prose/required_bundle", + "fixture": "ascii_prose", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "8c7bc121f9be9af25515387405045d3cd6f72c4464e7f2a93f70f962e94309f7" + } + }, + { + "id": "ascii_prose/required_bundle_then_us", + "fixture": "ascii_prose", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "4e5ba236ac48180e149528d8b21a8c9689ff77afdb379da5cddaeccd7bc92fa4" + } + }, + { + "id": "ascii_prose/mn_me_us", + "fixture": "ascii_prose", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "c3385fd0a4b986619c38d8eaf47451efa8955b724b8cae425dbd06d0ca0ef172" + } + }, + { + "id": "ascii_prose/mn_me_us_cf", + "fixture": "ascii_prose", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "1fb6f4419886c7ee5aca8d44a2c9aaadf7295ad523978c2c88270f1465263a45" + } + }, + { + "id": "ascii_prose/di_me_us_cf", + "fixture": "ascii_prose", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "1fb6f4419886c7ee5aca8d44a2c9aaadf7295ad523978c2c88270f1465263a45" + } + }, + { + "id": "ascii_clause/identity", + "fixture": "ascii_clause", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be" + } + }, + { + "id": "ascii_clause/mn_strip", + "fixture": "ascii_clause", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "371d49e40a3ac79e705f800954f775b31b4753f51012b31cf2b43c49b3e57417" + } + }, + { + "id": "ascii_clause/default_ignorable_strip", + "fixture": "ascii_clause", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "371d49e40a3ac79e705f800954f775b31b4753f51012b31cf2b43c49b3e57417" + } + }, + { + "id": "ascii_clause/nfc", + "fixture": "ascii_clause", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be" + } + }, + { + "id": "ascii_clause/nfkc", + "fixture": "ascii_clause", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be" + } + }, + { + "id": "ascii_clause/nfkd", + "fixture": "ascii_clause", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be" + } + }, + { + "id": "ascii_clause/cf_strip", + "fixture": "ascii_clause", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "b23bdc51893b3ae1f9165ab54f20d0ae4b1ee2ff905f2e069df0923e65f3e868" + } + }, + { + "id": "ascii_clause/me_strip", + "fixture": "ascii_clause", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "5c825ca966354c3a9256f7824c8a2e165cd5d072322347937096305a86450206" + } + }, + { + "id": "ascii_clause/cc_strip", + "fixture": "ascii_clause", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "38545d9c10b505ac8546a7c9c36f209770e546fe920ff8409029060c018a9305" + } + }, + { + "id": "ascii_clause/unicode_sanitizer", + "fixture": "ascii_clause", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "15accdaee1ece4ccdb49e9781f72c4703fdb562702ee1b216b810340cf5abc3f" + } + }, + { + "id": "ascii_clause/mn_then_us", + "fixture": "ascii_clause", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "5a0271e7b209eb9d4a3211758a730c0458d9593f3fd80535bd57338de45fd552" + } + }, + { + "id": "ascii_clause/di_then_us", + "fixture": "ascii_clause", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "5a0271e7b209eb9d4a3211758a730c0458d9593f3fd80535bd57338de45fd552" + } + }, + { + "id": "ascii_clause/us_then_mn", + "fixture": "ascii_clause", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "d4021102da682894624efc8f1d5e2616aed69e57115219e44b43ec5941466734" + } + }, + { + "id": "ascii_clause/required_bundle", + "fixture": "ascii_clause", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "d84fdcc0e23eeb2fffe125624c831fc6d711f739d341265858809dc0080c4508" + } + }, + { + "id": "ascii_clause/required_bundle_then_us", + "fixture": "ascii_clause", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "2787d0fb72c685a51c8704b6040e139dc47e7e570b63548393877b3f9c6583a2" + } + }, + { + "id": "ascii_clause/mn_me_us", + "fixture": "ascii_clause", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "3d9de8f43f39da89baa9059c58f51bbc33a64311139418e49c2a82ef83fdfffa" + } + }, + { + "id": "ascii_clause/mn_me_us_cf", + "fixture": "ascii_clause", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "dc98014ade366930c16b0df96c315d530199cc67012191f911af14d81a525849" + } + }, + { + "id": "ascii_clause/di_me_us_cf", + "fixture": "ascii_clause", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "dc98014ade366930c16b0df96c315d530199cc67012191f911af14d81a525849" + } + }, + { + "id": "nfd_latin/identity", + "fixture": "nfd_latin", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded" + } + }, + { + "id": "nfd_latin/mn_strip", + "fixture": "nfd_latin", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "7b9b226c1bcb13f7f2f6cca28f26329d7e6045e0d8947acfe5551d5529eba57b" + } + }, + { + "id": "nfd_latin/default_ignorable_strip", + "fixture": "nfd_latin", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "dd80e8f315fd2b338b94e183453b102a55ff46e9092093db9fbe15c223907db0" + } + }, + { + "id": "nfd_latin/nfc", + "fixture": "nfd_latin", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "b4671680b248de718c2ccc7c63812057e947f4e8e15f8abba5b62b21ba03edf9" + } + }, + { + "id": "nfd_latin/nfkc", + "fixture": "nfd_latin", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "b4671680b248de718c2ccc7c63812057e947f4e8e15f8abba5b62b21ba03edf9" + } + }, + { + "id": "nfd_latin/nfkd", + "fixture": "nfd_latin", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded" + } + }, + { + "id": "nfd_latin/cf_strip", + "fixture": "nfd_latin", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "b55d3b119c9e1c061a78fdc174b9ace3bda1107d76c616236e27aa7b2425be97" + } + }, + { + "id": "nfd_latin/me_strip", + "fixture": "nfd_latin", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "8d5b963f6782a65c7ad7003d4735a69b93e3212123ee4b4223f500fdaf39b89c" + } + }, + { + "id": "nfd_latin/cc_strip", + "fixture": "nfd_latin", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "64794315449e462eeebc11e1fd349e2401d67051ef780ff01652961d6bbcac75" + } + }, + { + "id": "nfd_latin/unicode_sanitizer", + "fixture": "nfd_latin", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "80d58e24d10fe955dac8c097cad84c819afd4f4423238de8f3680d508322296c" + } + }, + { + "id": "nfd_latin/mn_then_us", + "fixture": "nfd_latin", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "9a132ef10232336d2bd8ede21b5a9fbd286f95a33ed0c861ed04d1b1506246b6" + } + }, + { + "id": "nfd_latin/di_then_us", + "fixture": "nfd_latin", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "3b5e0713a3bdb94f14b227829743d85f17d46b1a93d36b04e3fd93ec99d1ffce" + } + }, + { + "id": "nfd_latin/us_then_mn", + "fixture": "nfd_latin", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "3b5e0713a3bdb94f14b227829743d85f17d46b1a93d36b04e3fd93ec99d1ffce" + } + }, + { + "id": "nfd_latin/required_bundle", + "fixture": "nfd_latin", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "6fc4cb93945152f0abb0ed322cf7f48b89f296ae87f6f98dedc6e45fd172ef01" + } + }, + { + "id": "nfd_latin/required_bundle_then_us", + "fixture": "nfd_latin", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "072af84bd61b5f8385796ac016234b01a2987801793d7dff44626b53385aec18" + } + }, + { + "id": "nfd_latin/mn_me_us", + "fixture": "nfd_latin", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "83f540d3d88ecd32a1f72c4ffd53586f16cf327c49d6d3b0c346c3cc904c4425" + } + }, + { + "id": "nfd_latin/mn_me_us_cf", + "fixture": "nfd_latin", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "f3401e2083d1f1d4a098c1091a3a9592d0c197a98d0f352d5f97be8c5ddf63d1" + } + }, + { + "id": "nfd_latin/di_me_us_cf", + "fixture": "nfd_latin", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "7a8d067d39df262ae963939a2e6777b7aadcdcd1202fc68dc9ad426d36d89a27" + } + }, + { + "id": "emoji/identity", + "fixture": "emoji", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb" + } + }, + { + "id": "emoji/mn_strip", + "fixture": "emoji", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "48af8e8b3f001394d9206ecc62bcf52c14876fc180e162d201362a7bb47287dc" + } + }, + { + "id": "emoji/default_ignorable_strip", + "fixture": "emoji", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "48af8e8b3f001394d9206ecc62bcf52c14876fc180e162d201362a7bb47287dc" + } + }, + { + "id": "emoji/nfc", + "fixture": "emoji", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb" + } + }, + { + "id": "emoji/nfkc", + "fixture": "emoji", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb" + } + }, + { + "id": "emoji/nfkd", + "fixture": "emoji", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb" + } + }, + { + "id": "emoji/cf_strip", + "fixture": "emoji", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "7a1d3e4147bbf4c5fdfccaa888d4a70b20f5503cddcc4f1c66c620ba145b57dd" + } + }, + { + "id": "emoji/me_strip", + "fixture": "emoji", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "4d8e828688cbf1b2bb00c65ba0e961508472d7fd231d366b7d66a731defc3dbd" + } + }, + { + "id": "emoji/cc_strip", + "fixture": "emoji", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "ab2ca1e0360f7f7bf340b36116229f4d11aacc1724d4e4216a18c1d9c4d0224c" + } + }, + { + "id": "emoji/unicode_sanitizer", + "fixture": "emoji", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "d954eff1a9729d9ac878145690ab720c395f8a779a7503e94e2f652f9adefd19" + } + }, + { + "id": "emoji/mn_then_us", + "fixture": "emoji", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "b9f3659b0ee88a007a3f56ede750c4a56da7f6e41fd3f00e3ce0a232cf422c2d" + } + }, + { + "id": "emoji/di_then_us", + "fixture": "emoji", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "b9f3659b0ee88a007a3f56ede750c4a56da7f6e41fd3f00e3ce0a232cf422c2d" + } + }, + { + "id": "emoji/us_then_mn", + "fixture": "emoji", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "b9f3659b0ee88a007a3f56ede750c4a56da7f6e41fd3f00e3ce0a232cf422c2d" + } + }, + { + "id": "emoji/required_bundle", + "fixture": "emoji", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "89381612c80309456d58e616fe47d0ec5f6fb8c4caaa74a2c1a56f3303672f12" + } + }, + { + "id": "emoji/required_bundle_then_us", + "fixture": "emoji", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "34171f913e14bc1e28a49548c3ac79a138bd57a4621692abb3c047b3de71667d" + } + }, + { + "id": "emoji/mn_me_us", + "fixture": "emoji", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "e262d8fef01b7bca769465669213701652a317f4ecf57f738d2bd1fe79e93439" + } + }, + { + "id": "emoji/mn_me_us_cf", + "fixture": "emoji", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "db1759a9ace89ffd17820559db65f66c17028658cbaa2d3125e0c4d77cfada51" + } + }, + { + "id": "emoji/di_me_us_cf", + "fixture": "emoji", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "db1759a9ace89ffd17820559db65f66c17028658cbaa2d3125e0c4d77cfada51" + } + }, + { + "id": "greek/identity", + "fixture": "greek", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c" + } + }, + { + "id": "greek/mn_strip", + "fixture": "greek", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "bca9bc765d450a1423e424a7492b8c6e6f508a8af7989b0b61f4509fb029dbd2" + } + }, + { + "id": "greek/default_ignorable_strip", + "fixture": "greek", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "bca9bc765d450a1423e424a7492b8c6e6f508a8af7989b0b61f4509fb029dbd2" + } + }, + { + "id": "greek/nfc", + "fixture": "greek", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c" + } + }, + { + "id": "greek/nfkc", + "fixture": "greek", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c" + } + }, + { + "id": "greek/nfkd", + "fixture": "greek", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c" + } + }, + { + "id": "greek/cf_strip", + "fixture": "greek", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "6aac9e373252935a4b0baceeb0b866d7370d771137bf87b0c6f91d7bcedc769c" + } + }, + { + "id": "greek/me_strip", + "fixture": "greek", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "55c7913fe2a506126741bf445630af732571c7d84d84731e951d2b18556fe2f5" + } + }, + { + "id": "greek/cc_strip", + "fixture": "greek", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "f32ed8bad164a8c33a7c3b35e082fa24ae8152277f9bbd843276fbbef20dd5a0" + } + }, + { + "id": "greek/unicode_sanitizer", + "fixture": "greek", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "8ad14ba81c6a26c23ebec84f5455b83dfeec204779b4e7962f232b997edd1fdc" + } + }, + { + "id": "greek/mn_then_us", + "fixture": "greek", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "2cc227b489a2b195b1978e74670a7ccfd00aed4c6ffd38720f3efd84858999fb" + } + }, + { + "id": "greek/di_then_us", + "fixture": "greek", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "2cc227b489a2b195b1978e74670a7ccfd00aed4c6ffd38720f3efd84858999fb" + } + }, + { + "id": "greek/us_then_mn", + "fixture": "greek", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "cdcda385f5e8f2b72a88734cd4567f5c3af92a152ef7c2183ca840570e7ef98d" + } + }, + { + "id": "greek/required_bundle", + "fixture": "greek", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "8fc04360f470f69fcd4eb1300c9ecbaa2a2e07b7a99973abb5d76975560b91bd" + } + }, + { + "id": "greek/required_bundle_then_us", + "fixture": "greek", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "e0b89e94bf89ddf228234020ae983e915f0564b6ebf11824a290c9555db24359" + } + }, + { + "id": "greek/mn_me_us", + "fixture": "greek", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "4eb80d82ce3fdb0d8592ea8dabcebb842080b09ca16a132f1c2427f8d2c8625f" + } + }, + { + "id": "greek/mn_me_us_cf", + "fixture": "greek", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "fa9944962ebd5fc5e1ec7459ceedde46eec5e5dbf4e0f734218dd07cc33ff45a" + } + }, + { + "id": "greek/di_me_us_cf", + "fixture": "greek", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "fa9944962ebd5fc5e1ec7459ceedde46eec5e5dbf4e0f734218dd07cc33ff45a" + } + }, + { + "id": "cyrillic/identity", + "fixture": "cyrillic", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d" + } + }, + { + "id": "cyrillic/mn_strip", + "fixture": "cyrillic", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "47240465a2170ea848a42b5a4e1523f23b2c67e4baef978b27a8be0f90e32352" + } + }, + { + "id": "cyrillic/default_ignorable_strip", + "fixture": "cyrillic", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "47240465a2170ea848a42b5a4e1523f23b2c67e4baef978b27a8be0f90e32352" + } + }, + { + "id": "cyrillic/nfc", + "fixture": "cyrillic", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d" + } + }, + { + "id": "cyrillic/nfkc", + "fixture": "cyrillic", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d" + } + }, + { + "id": "cyrillic/nfkd", + "fixture": "cyrillic", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d" + } + }, + { + "id": "cyrillic/cf_strip", + "fixture": "cyrillic", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "7573d27a7e446269185bbc62c0b066a5c7423bef8b04c910cab02d1e10d4b8d0" + } + }, + { + "id": "cyrillic/me_strip", + "fixture": "cyrillic", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "a5cabf7f514e1869d71ade5d25f41509267ca07e7312325fd7650c29272d9e9a" + } + }, + { + "id": "cyrillic/cc_strip", + "fixture": "cyrillic", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "6dd2b56a60748ab51729ad5c1cae599caf65f0de7e34fb77c96bd304c4f61f66" + } + }, + { + "id": "cyrillic/unicode_sanitizer", + "fixture": "cyrillic", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "0d662b1590b8e86653c4eaf2cfd7c00da1e8b18262473c19c163162ff09e70ba" + } + }, + { + "id": "cyrillic/mn_then_us", + "fixture": "cyrillic", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "530127fbc4472a1b6360293df5e112088646df4cd0cbf496d0c1e4c6a98947f3" + } + }, + { + "id": "cyrillic/di_then_us", + "fixture": "cyrillic", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "530127fbc4472a1b6360293df5e112088646df4cd0cbf496d0c1e4c6a98947f3" + } + }, + { + "id": "cyrillic/us_then_mn", + "fixture": "cyrillic", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "3315cc4e425d92eb595c8865330929d25d0c4d46b5e6dd750c2c6746762a8e95" + } + }, + { + "id": "cyrillic/required_bundle", + "fixture": "cyrillic", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "e1e44049062afbf5c16999f422fc706beff319247693c9dc1dc76da4d933a506" + } + }, + { + "id": "cyrillic/required_bundle_then_us", + "fixture": "cyrillic", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "b58f7ec5af563bf91eca3b56920e3d3b7c919ccbb4f5e8a228af2bb495f1d316" + } + }, + { + "id": "cyrillic/mn_me_us", + "fixture": "cyrillic", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "70e1e2ffc6b4518941744a87cbc1e184f42c7a1d77fb1cf7bfab7a18f71e8ca3" + } + }, + { + "id": "cyrillic/mn_me_us_cf", + "fixture": "cyrillic", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "1474ce7c15d02096e17484aa5981c662989982e51262e0075c021e636600ecf4" + } + }, + { + "id": "cyrillic/di_me_us_cf", + "fixture": "cyrillic", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "1474ce7c15d02096e17484aa5981c662989982e51262e0075c021e636600ecf4" + } + }, + { + "id": "han/identity", + "fixture": "han", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9" + } + }, + { + "id": "han/mn_strip", + "fixture": "han", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "3e1b8cfef2619b3e061b35d3a0c6139c02b27d990addcd40b7b4d0f5f6756792" + } + }, + { + "id": "han/default_ignorable_strip", + "fixture": "han", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "3e1b8cfef2619b3e061b35d3a0c6139c02b27d990addcd40b7b4d0f5f6756792" + } + }, + { + "id": "han/nfc", + "fixture": "han", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9" + } + }, + { + "id": "han/nfkc", + "fixture": "han", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9" + } + }, + { + "id": "han/nfkd", + "fixture": "han", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9" + } + }, + { + "id": "han/cf_strip", + "fixture": "han", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "44abc3e256af1175b1a2ef740658e23b69f83c60d7051e44ad49ea4b86b2ec9f" + } + }, + { + "id": "han/me_strip", + "fixture": "han", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "045637d021cb61e2f4c9bf961be9d9a4a523fac6cce29de01d3e174d65c16bed" + } + }, + { + "id": "han/cc_strip", + "fixture": "han", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "a55a7bec3899619e5ee683f4c6153c8038b4bf6910bb6f4bec07fc2aacf4e468" + } + }, + { + "id": "han/unicode_sanitizer", + "fixture": "han", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "52330acff1fe76fed1a913d6a9587eef7911f88169dcdb62b4a18a00cbc1230d" + } + }, + { + "id": "han/mn_then_us", + "fixture": "han", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "9f9796db4c35bb524cebb46d5f72dba4ec3f62091c284e76092bf06d22292bcd" + } + }, + { + "id": "han/di_then_us", + "fixture": "han", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "9f9796db4c35bb524cebb46d5f72dba4ec3f62091c284e76092bf06d22292bcd" + } + }, + { + "id": "han/us_then_mn", + "fixture": "han", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "9f9796db4c35bb524cebb46d5f72dba4ec3f62091c284e76092bf06d22292bcd" + } + }, + { + "id": "han/required_bundle", + "fixture": "han", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "e801ce4fa3e2ea8a080aa7d482a162fa944e91529072b650b8cf274a31ee5264" + } + }, + { + "id": "han/required_bundle_then_us", + "fixture": "han", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "7f91f015be843006ed4d6da3ae3b48892f281dd6c43a18d6ce72d3b2304f4a46" + } + }, + { + "id": "han/mn_me_us", + "fixture": "han", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "439b6f273b0dd83a1f344eb1f054988ad86adf6f6e9bb8f2b00670f3c3778d73" + } + }, + { + "id": "han/mn_me_us_cf", + "fixture": "han", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "2f932a5b0329537dc6ea38ae01a160a223a47256864662c55ae2c72360554d4b" + } + }, + { + "id": "han/di_me_us_cf", + "fixture": "han", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "2f932a5b0329537dc6ea38ae01a160a223a47256864662c55ae2c72360554d4b" + } + }, + { + "id": "hangul/identity", + "fixture": "hangul", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664" + } + }, + { + "id": "hangul/mn_strip", + "fixture": "hangul", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "edc2bb560e4971c0cb3a6e35b3d0e7c701e5f1bdd30f4501b41f8ef8b65d2925" + } + }, + { + "id": "hangul/default_ignorable_strip", + "fixture": "hangul", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "edc2bb560e4971c0cb3a6e35b3d0e7c701e5f1bdd30f4501b41f8ef8b65d2925" + } + }, + { + "id": "hangul/nfc", + "fixture": "hangul", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664" + } + }, + { + "id": "hangul/nfkc", + "fixture": "hangul", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664" + } + }, + { + "id": "hangul/nfkd", + "fixture": "hangul", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "c4d19198ade11637f2d6974c530613e3d01584b6ce3c7d1e49914d1769d18b00" + } + }, + { + "id": "hangul/cf_strip", + "fixture": "hangul", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "43359e805d74c04c7a67daa07f5bf03c9b61bcf2e7fa665bb0ca8efd3bb146d9" + } + }, + { + "id": "hangul/me_strip", + "fixture": "hangul", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "9ea5664a5a152bb0def1cfe9fb6c5eef832357e4dea52cf3fc5510d187dbd127" + } + }, + { + "id": "hangul/cc_strip", + "fixture": "hangul", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "9763f9284245147d406a93c3a2d943473d59cf34e9fe4ea5120980f146b628ff" + } + }, + { + "id": "hangul/unicode_sanitizer", + "fixture": "hangul", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "b616729a4d762db9e6a1252bdbf179da5ccaa51cb28a98ae3f9f52cb3fd4f0c0" + } + }, + { + "id": "hangul/mn_then_us", + "fixture": "hangul", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "e648c1a588c3598c88b34b6636274646852e770702343b29fba327e4824901cd" + } + }, + { + "id": "hangul/di_then_us", + "fixture": "hangul", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "e648c1a588c3598c88b34b6636274646852e770702343b29fba327e4824901cd" + } + }, + { + "id": "hangul/us_then_mn", + "fixture": "hangul", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "e648c1a588c3598c88b34b6636274646852e770702343b29fba327e4824901cd" + } + }, + { + "id": "hangul/required_bundle", + "fixture": "hangul", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "e2224e298113b8a82a3d98e36ecb2f8f8239cb5d0be3ff089f1e8123a9d0e4dc" + } + }, + { + "id": "hangul/required_bundle_then_us", + "fixture": "hangul", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "523a21039028d365e14c2af243b7c2f65a0cbe2ae191440bd27ed79b85dff8e9" + } + }, + { + "id": "hangul/mn_me_us", + "fixture": "hangul", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "583b37586c4ca3428f5c17516c4b475eadfd3e61f90df918367bd51ff52d3987" + } + }, + { + "id": "hangul/mn_me_us_cf", + "fixture": "hangul", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "c5421efe9a5b8374f020097c81c3bf425c43ee52ad15a9b5b8a042a4f7bee9ed" + } + }, + { + "id": "hangul/di_me_us_cf", + "fixture": "hangul", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "c5421efe9a5b8374f020097c81c3bf425c43ee52ad15a9b5b8a042a4f7bee9ed" + } + }, + { + "id": "url_mixed/identity", + "fixture": "url_mixed", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67" + } + }, + { + "id": "url_mixed/mn_strip", + "fixture": "url_mixed", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "8818678c99cc9407ea16572307966cd7e77e9d86b33938806b556c9c55e919b3" + } + }, + { + "id": "url_mixed/default_ignorable_strip", + "fixture": "url_mixed", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "8818678c99cc9407ea16572307966cd7e77e9d86b33938806b556c9c55e919b3" + } + }, + { + "id": "url_mixed/nfc", + "fixture": "url_mixed", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67" + } + }, + { + "id": "url_mixed/nfkc", + "fixture": "url_mixed", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67" + } + }, + { + "id": "url_mixed/nfkd", + "fixture": "url_mixed", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67" + } + }, + { + "id": "url_mixed/cf_strip", + "fixture": "url_mixed", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "2f515d68cac2ede2814597c9db1a1524b717ee333960506de956f217281f9dfd" + } + }, + { + "id": "url_mixed/me_strip", + "fixture": "url_mixed", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "d3606380dcf2b614bd12c51be7ab2203c02ed7f92fcbc95f51aeac3580747c55" + } + }, + { + "id": "url_mixed/cc_strip", + "fixture": "url_mixed", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "41670f567a3564b84857c4fa5dfd3833a4e8744a362e59d60a990d219385eec3" + } + }, + { + "id": "url_mixed/unicode_sanitizer", + "fixture": "url_mixed", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "7a09e8170f3fdd540f04666a69f7a7c08577caa56a8898496f5cd040fe2b599c" + } + }, + { + "id": "url_mixed/mn_then_us", + "fixture": "url_mixed", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "61acd38e2b1db49bc43553f6ff723b2d2bd78c38bc7e37b3d4e6565c9e92b458" + } + }, + { + "id": "url_mixed/di_then_us", + "fixture": "url_mixed", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "61acd38e2b1db49bc43553f6ff723b2d2bd78c38bc7e37b3d4e6565c9e92b458" + } + }, + { + "id": "url_mixed/us_then_mn", + "fixture": "url_mixed", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "a1fb089533a4ee4448c23b3e959a6c91156b33c5cbc979a0fd669434eab6f985" + } + }, + { + "id": "url_mixed/required_bundle", + "fixture": "url_mixed", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "808f70cfcac9e1ca14e66e9c62353135738e936cf52555938b0b671a4ea232c4" + } + }, + { + "id": "url_mixed/required_bundle_then_us", + "fixture": "url_mixed", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "461ad0cbe74d538a00d8e019f86794f3a6722ca6374a1795117076eaf3b14081" + } + }, + { + "id": "url_mixed/mn_me_us", + "fixture": "url_mixed", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "fa0fba7d062e379969908643c0e68b50f550b99dfe319729d7eca3b27e9c8238" + } + }, + { + "id": "url_mixed/mn_me_us_cf", + "fixture": "url_mixed", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "38c2d97d995f358011fa4779d88f2cca5d9398e2facf61c81b1ff8f7bdfb4262" + } + }, + { + "id": "url_mixed/di_me_us_cf", + "fixture": "url_mixed", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "38c2d97d995f358011fa4779d88f2cca5d9398e2facf61c81b1ff8f7bdfb4262" + } + }, + { + "id": "digits/identity", + "fixture": "digits", + "attack": "identity", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/mn_strip", + "fixture": "digits", + "attack": "mn_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/default_ignorable_strip", + "fixture": "digits", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/nfc", + "fixture": "digits", + "attack": "nfc", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/nfkc", + "fixture": "digits", + "attack": "nfkc", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/nfkd", + "fixture": "digits", + "attack": "nfkd", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/cf_strip", + "fixture": "digits", + "attack": "cf_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/me_strip", + "fixture": "digits", + "attack": "me_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/cc_strip", + "fixture": "digits", + "attack": "cc_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/unicode_sanitizer", + "fixture": "digits", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/mn_then_us", + "fixture": "digits", + "attack": "mn_then_us", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/di_then_us", + "fixture": "digits", + "attack": "di_then_us", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/us_then_mn", + "fixture": "digits", + "attack": "us_then_mn", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/required_bundle", + "fixture": "digits", + "attack": "required_bundle", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/required_bundle_then_us", + "fixture": "digits", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/mn_me_us", + "fixture": "digits", + "attack": "mn_me_us", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/mn_me_us_cf", + "fixture": "digits", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/di_me_us_cf", + "fixture": "digits", + "attack": "di_me_us_cf", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + } + ] +} diff --git a/tests/test_robustness.py b/tests/test_robustness.py new file mode 100644 index 00000000..8d2a5621 --- /dev/null +++ b/tests/test_robustness.py @@ -0,0 +1,227 @@ +import json +from io import StringIO +from pathlib import Path + +import pytest + +from fuckmark.cli import main +from fuckmark.cycle8.gate_v2 import ( + CYCLE8_GATE_V2_CONFIRMATION_SCORECARD_HASH, + CYCLE8_GATE_V2_CONFIRMATION_SCORECARD_PATH, +) +from fuckmark.hashing import sha256_file, sha256_json +from fuckmark.robustness import ( + ATTACK_IDS, + FREEZE_PATH, + PROTOCOL_PATH, + ROBUSTNESS_ALGORITHM_VERSION, + ROBUSTNESS_EXIT_MISMATCH, + ROBUSTNESS_EXIT_OK, + ROBUSTNESS_EXIT_USAGE, + SEALED_DETECTOR_SCORECARD_HASH, + SEALED_DETECTOR_SCORECARD_PATH, + VECTORS_PATH, + apply_attack, + apply_required_sanitizer_bundle, + build_vectors_payload, + freeze_bindings, + fixture_ids, + fixture_source, + iter_cells, + load_vectors, + measure_cell, + run_robustness_argv, + run_robustness_bench, + strip_default_ignorable, + strip_enclosing_marks, + strip_nonspacing_marks, + strip_other_controls, + unicode_sanitizer, +) + + +ROOT = Path(__file__).resolve().parents[1] +MIXED_FIXTURES = tuple(name for name in fixture_ids() if name != "digits") + + +def _run(argv: list[str]) -> tuple[int, str, str]: + out, err = StringIO(), StringIO() + code = run_robustness_argv(argv, out, err) + return code, out.getvalue(), err.getvalue() + + +def test_robustness_freeze_binds_protocol_vectors_and_scorecard() -> None: + freeze = json.loads(FREEZE_PATH.read_text(encoding="utf-8")) + payload = load_vectors() + live = freeze_bindings() + assert freeze["algorithm_version"] == ROBUSTNESS_ALGORITHM_VERSION + assert freeze["protocol_id"] == "fuckmark-robustness-bench-v1" + assert freeze["protocol_sha256"] == sha256_file(PROTOCOL_PATH) == live["protocol_sha256"] + assert freeze["vectors_file_sha256"] == sha256_file(VECTORS_PATH) == live["vectors_file_sha256"] + assert freeze["vectors_canonical_sha256"] == sha256_json(payload) == live["vectors_canonical_sha256"] + assert freeze["sealed_detector_scorecard_path"] == SEALED_DETECTOR_SCORECARD_PATH + assert freeze["sealed_detector_scorecard_hash"] == SEALED_DETECTOR_SCORECARD_HASH + assert freeze["sealed_detector_scorecard_file_sha256"] == live["sealed_detector_scorecard_file_sha256"] + assert payload["algorithm_version"] == ROBUSTNESS_ALGORITHM_VERSION + assert "fuckmark-robustness-bench-v1" in PROTOCOL_PATH.read_text(encoding="utf-8") + assert "does **not** rerun" in PROTOCOL_PATH.read_text(encoding="utf-8") + + +def test_sealed_scorecard_constants_match_gate_v2() -> None: + assert SEALED_DETECTOR_SCORECARD_PATH == CYCLE8_GATE_V2_CONFIRMATION_SCORECARD_PATH + assert SEALED_DETECTOR_SCORECARD_HASH == CYCLE8_GATE_V2_CONFIRMATION_SCORECARD_HASH + scorecard = json.loads((ROOT / SEALED_DETECTOR_SCORECARD_PATH).read_text(encoding="utf-8")) + assert scorecard["scorecard_hash"] == SEALED_DETECTOR_SCORECARD_HASH + assert scorecard["identity_watermarked_detected"] == 188 + assert scorecard["mix_watermarked_detected_by_required_sanitizer"]["raw"] == 0 + assert scorecard["visible_pass_rate"] == "192/192" + + +def test_vectors_replay_all_cells_and_match_live_payload() -> None: + payload = load_vectors() + cells = iter_cells() + assert len(payload["cells"]) == 180 + assert len(cells) == 180 + assert payload["attacks"] == list(ATTACK_IDS) + assert [item["id"] for item in payload["fixtures"]] == list(fixture_ids()) + expected = {item["id"]: item["expect"] for item in payload["cells"]} + for cell in cells: + key = f"{cell.fixture_id}/{cell.attack_id}" + assert expected[key]["restores_source"] is cell.restores_source + assert expected[key]["mix_projection_equals_source"] is cell.mix_projection_equals_source + assert expected[key]["projection_equals_source"] is cell.projection_equals_source + assert expected[key]["carrier_detected"] is cell.carrier_detected + assert expected[key]["residual_categories"] == list(cell.residual_categories) + assert expected[key]["mix_sha256"] == cell.mix_sha256 + assert expected[key]["output_sha256"] == cell.output_sha256 + assert build_vectors_payload() == payload + + +def test_digits_restore_and_mixed_fixtures_never_restore() -> None: + report = run_robustness_bench() + summary = report["summary"] + assert summary["cells"] == 180 + assert summary["restores_source"] == 18 + assert summary["mix_projection_equals_source"] == 180 + assert summary["mismatches"] == 0 + assert report["sealed_detector_ok"] is True + digits = [cell for cell in iter_cells(fixtures=("digits",))] + assert len(digits) == 18 + assert all(cell.restores_source for cell in digits) + assert all(not cell.carrier_detected for cell in digits) + mixed = [cell for cell in iter_cells(fixtures=MIXED_FIXTURES)] + assert mixed + assert all(not cell.restores_source for cell in mixed) + assert all(cell.mix_projection_equals_source for cell in mixed) + + +def test_ascii_prose_mn_me_us_cf_does_not_restore_or_keep_carriers() -> None: + cell = measure_cell("ascii_prose", "mn_me_us_cf") + assert cell.restores_source is False + assert cell.carrier_detected is False + assert cell.projection_equals_source is False + assert cell.residual_categories == () + identity = measure_cell("ascii_prose", "identity") + assert identity.carrier_detected is True + assert identity.restores_source is False + assert identity.projection_equals_source is True + + +def test_attacks_match_cycle8_reference_sanitizers() -> None: + from fuckmark.cycle8.benchmark import ( + strip_default_ignorable as ref_di, + strip_enclosing_marks as ref_me, + strip_nonspacing_marks as ref_mn, + strip_other_controls as ref_cc, + ) + from fuckmark.cycle8.control_carrier import apply_required_sanitizer_bundle as ref_bundle + from fuckmark.cycle8.threat_model_audit import lm_watermarking_unicode_sanitizer + + mixed = measure_cell("ascii_prose", "identity").mixed + assert strip_nonspacing_marks(mixed) == ref_mn(mixed) + assert strip_default_ignorable(mixed) == ref_di(mixed) + assert strip_enclosing_marks(mixed) == ref_me(mixed) + assert strip_other_controls(mixed) == ref_cc(mixed) + assert unicode_sanitizer(mixed) == lm_watermarking_unicode_sanitizer(mixed) + assert apply_required_sanitizer_bundle(mixed) == ref_bundle(mixed) + assert apply_attack("unicode_sanitizer", mixed) == lm_watermarking_unicode_sanitizer(mixed) + assert apply_attack("required_bundle", mixed) == ref_bundle(mixed) + + +def test_unknown_fixture_and_attack_raise() -> None: + with pytest.raises(ValueError, match="unknown robustness fixture"): + fixture_source("not_a_fixture") + with pytest.raises(ValueError, match="unknown robustness attack"): + apply_attack("not_an_attack", "x") + with pytest.raises(TypeError, match="text must be a string"): + apply_attack("identity", 1) + with pytest.raises(ValueError, match="unknown robustness fixtures"): + iter_cells(fixtures=("nope",)) + with pytest.raises(ValueError, match="unknown robustness attacks"): + iter_cells(attacks=("nope",)) + + +def test_cli_json_and_main_dispatch() -> None: + code, out, err = _run(["--json", "--fixture", "digits", "--attack", "identity"]) + assert code == ROBUSTNESS_EXIT_OK + assert err == "" + payload = json.loads(out) + assert payload["algorithm_version"] == ROBUSTNESS_ALGORITHM_VERSION + assert payload["summary"]["cells"] == 1 + assert payload["summary"]["restores_source"] == 1 + assert payload["summary"]["mismatches"] == 0 + assert payload["sealed_detector_ok"] is True + assert payload["cells"][0]["id"] == "digits/identity" + dispatched_out, dispatched_err = StringIO(), StringIO() + dispatched = main( + StringIO(""), + dispatched_out, + error_stream=dispatched_err, + argv=("robustness", "--json", "--fixture", "digits"), + ) + assert dispatched == ROBUSTNESS_EXIT_OK + report = json.loads(dispatched_out.getvalue()) + assert report["summary"]["cells"] == 18 + assert report["summary"]["restores_source"] == 18 + + +def test_cli_human_report_and_quiet() -> None: + code, out, err = _run(["--fixture", "digits", "--attack", "nfc"]) + assert code == ROBUSTNESS_EXIT_OK + assert out == "" + assert "1 cells" in err + assert "1 restore the source" in err + assert "0 mismatches" in err + assert "Detectors were not rerun" in err + quiet_code, quiet_out, quiet_err = _run(["-q", "--fixture", "digits", "--attack", "nfc"]) + assert quiet_code == ROBUSTNESS_EXIT_OK + assert quiet_out == "" + assert quiet_err == "" + + +def test_cli_usage_errors() -> None: + code, _out, err = _run(["--fixture", "not_a_fixture"]) + assert code == ROBUSTNESS_EXIT_USAGE + assert "unknown robustness fixtures" in err + attack_code, _out2, attack_err = _run(["--attack", "not_an_attack"]) + assert attack_code == ROBUSTNESS_EXIT_USAGE + assert "unknown robustness attacks" in attack_err + + +def test_cli_mismatch_is_exit_one(monkeypatch) -> None: + monkeypatch.setattr("fuckmark.robustness.load_vectors", lambda: {"cells": []}) + code, out, err = _run(["--json", "--fixture", "digits", "--attack", "identity"]) + assert code == ROBUSTNESS_EXIT_MISMATCH + payload = json.loads(out) + assert payload["summary"]["mismatches"] == 1 + assert "digits/identity" in err or payload["mismatches"][0]["id"] == "digits/identity" + + +def test_cli_help_mentions_robustness_bench(capsys) -> None: + with pytest.raises(SystemExit) as result: + main(argv=("--help",)) + assert result.value.code == 0 + rendered = capsys.readouterr().out + assert "fuckmark robustness" in rendered + assert "sanitizer-restore" in rendered + assert "no detector rerun" in rendered From ea6aa953f72f62e72287927646d1c56841649e7f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 03:50:27 +0000 Subject: [PATCH 2/3] Harden robustness bench freeze checks and wheel packaging Load protocol, vectors, freeze, and the sealed scorecard from packaged module data so `fuckmark robustness` works from a wheel. Compare those file hashes to the freeze record before exit 0, including scorecard bytes when the embedded hash is left unchanged. Co-authored-by: Byte271 --- CHANGELOG.md | 2 +- docs/robustness.md | 5 +- fuckmark/robustness.py | 100 +- ...le8-gate-v2-confirmation-scorecard-v1.json | 1 + .../fuckmark-robustness-bench-v1.freeze.json | 12 + .../fuckmark-robustness-bench-v1.protocol.md | 87 + .../fuckmark-robustness-bench-v1.vectors.json | 3363 +++++++++++++++++ pyproject.toml | 2 +- .../fuckmark-robustness-bench-v1.freeze.json | 2 +- .../fuckmark-robustness-bench-v1.protocol.md | 11 +- tests/test_robustness.py | 74 +- tools/verify_release_install.py | 27 + 12 files changed, 3665 insertions(+), 21 deletions(-) create mode 100644 fuckmark/robustness_data/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json create mode 100644 fuckmark/robustness_data/fuckmark-robustness-bench-v1.freeze.json create mode 100644 fuckmark/robustness_data/fuckmark-robustness-bench-v1.protocol.md create mode 100644 fuckmark/robustness_data/fuckmark-robustness-bench-v1.vectors.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d57f9bb0..5a678220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ - WASM build of `fuckmark-hidden-scan-v1` (`crates/fuckmark-scan`, committed `fuckmark_scan.wasm`). The scan page and the Chromium popup load it when `fetch` works so classification stays on-device; `scan.js` remains the `file://` fallback. Lone UTF-16 surrogates route through the JS engine (UTF-8 cannot carry them). Empty category arrays stay empty selections. The extension CSP allows `wasm-unsafe-eval` so Chromium can instantiate the module. Page reveal and paste-safe keep the synchronous JS port. Rebuild with `crates/fuckmark-scan/build-wasm.sh`. - Language bindings for the same Rust scan ABI: C header (`crates/fuckmark-scan/include/fuckmark_scan.h`), optional Python ctypes loader (`fuckmark.native_scan`), and a Node package under `bindings/node` (WASM + JS fallback). Host library build: `crates/fuckmark-scan/build-native.sh`. Lone UTF-16 surrogates cannot cross the UTF-8 ABI, so Python `scan_text` / `clean_text` delegate those strings to the Python reference scanner, and the WASM wrapper normalizes the JS fallback result to the same `index` / `truncated` / `highest_severity` / `source_length` schema. The C example is linked with `-rpath,$ORIGIN` and written next to `libfuckmark_scan.so`. Overview: `bindings/README.md`. - OS clipboard watcher (`fuckmark-clipboard-watch-v1`). `fuckmark clipboard scan` / `clean` / `watch` poll the system clipboard (pbpaste, wl-paste, xclip, xsel, PowerShell Get-Clipboard) for the same security category set as `fuckmark lint`. `--clean` rewrites the clipboard in place after a digest re-read so a newer copy is not overwritten. Snapshots hash with UTF-8 `surrogatepass` so lone surrogates do not crash the watcher. Linux read uses `wl-paste` without `--no-newline`. Emoji ZWJ sequences (both neighbors emoji) and info-level variation selectors are kept, matching browser paste-safe. `--json`, `-q`, `--select`, `--once`, `--exit-on-find`, `--interval`, `--max-seconds`. Exit `0` clean, `1` findings, `2` usage, `3` clipboard unavailable. Reference: `docs/clipboard.md`. Python: `evaluate_clipboard_text`, `watch_clipboard`, `clean_clipboard_text` (injectable reader/writer for tests). -- Public sanitizer-restore robustness bench (`fuckmark-robustness-bench-v1`). `fuckmark robustness` replays 10 short fixtures times 18 Unicode sanitizer attacks (180 cells) against the live five-layer mix and frozen vectors. It does not rerun GPT-2 or SynthID; the Gate v2 confirmation scorecard stays the sealed detector track (identity 188/192, mix 0/192 after required sanitizers). Mixed letter/emoji fixtures do not restore; `digits` has no eligible site so mix is a no-op and every attack restores. `--json`, `-q`, `--fixture`, `--attack`. Exit `0` match, `1` mismatch, `2` usage. Spec: `specs/fuckmark-robustness-bench-v1.{protocol.md,vectors.json,freeze.json}`. Reference: `docs/robustness.md`. +- Public sanitizer-restore robustness bench (`fuckmark-robustness-bench-v1`). `fuckmark robustness` replays 10 short fixtures times 18 Unicode sanitizer attacks (180 cells) against the live five-layer mix and frozen vectors. It does not rerun GPT-2 or SynthID; the Gate v2 confirmation scorecard stays the sealed detector track (identity 188/192, mix 0/192 after required sanitizers). Mixed letter/emoji fixtures do not restore; `digits` has no eligible site so mix is a no-op and every attack restores. `--json`, `-q`, `--fixture`, `--attack`. Exit `0` match, `1` mismatch, `2` usage. The CLI loads packaged copies under `fuckmark/robustness_data/` (byte-identical to `specs/`) and compares protocol, vectors, and scorecard-file SHA-256 to the freeze record before reporting success. Spec: `specs/fuckmark-robustness-bench-v1.{protocol.md,vectors.json,freeze.json}`. Reference: `docs/robustness.md`. - `fuckmark normalize` (`fuckmark-normalize-v1`): NFC, optional identifier lookalike fold (UTS #39-inspired subset, not a full confusable map), then strip of the security category set, plus a JSON receipt (`input_sha256` / `output_sha256` / `steps` / `report_hash`). `--clean` remains the blunt strip with no receipt. HTTP: `POST /api/normalize`. Python: `normalize_text`, `skeleton_fold`. Reference: `docs/normalize.md`. - VS Code / Cursor: `fuckmark.cleanOnSave` (default off) strips hidden Unicode on save. - Frozen confirmation files, hashes, mix mechanisms, and the published wheel are unchanged. Package version stays 0.4.1. diff --git a/docs/robustness.md b/docs/robustness.md index 9128d657..8b111331 100644 --- a/docs/robustness.md +++ b/docs/robustness.md @@ -12,6 +12,7 @@ Protocol: [`specs/fuckmark-robustness-bench-v1.protocol.md`](../specs/fuckmark-r Vectors (sources as codepoint arrays only): [`specs/fuckmark-robustness-bench-v1.vectors.json`](../specs/fuckmark-robustness-bench-v1.vectors.json). Freeze hashes: [`specs/fuckmark-robustness-bench-v1.freeze.json`](../specs/fuckmark-robustness-bench-v1.freeze.json). +Installed wheels load byte-identical copies from `fuckmark/robustness_data/`. Before exit 0 the CLI recomputes those hashes, including the scorecard *file* SHA-256, so editing the scorecard while leaving its embedded `scorecard_hash` field alone still fails. ## Command line @@ -68,8 +69,8 @@ Sealed detector track (hashed, not rerun): Gate v2 confirmation identity | Status | Meaning | | ---: | ---: | -| 0 | Selected cells match the frozen vectors and the sealed scorecard hash still matches. | -| 1 | Mix or sanitizer drift, or the sealed scorecard hash no longer matches. | +| 0 | Selected cells match the frozen vectors, freeze hashes match the loaded artifacts, and the sealed scorecard hash still matches. | +| 1 | Mix, sanitizer, or freeze-hash drift, or a packaged artifact is missing. | | 2 | Usage (unknown `--fixture` / `--attack`, or argparse). | ## Python diff --git a/fuckmark/robustness.py b/fuckmark/robustness.py index 03a42ffd..4aa95652 100644 --- a/fuckmark/robustness.py +++ b/fuckmark/robustness.py @@ -22,12 +22,46 @@ ROBUSTNESS_EXIT_OK = 0 ROBUSTNESS_EXIT_MISMATCH = 1 ROBUSTNESS_EXIT_USAGE = 2 -SPEC_DIR = Path(__file__).resolve().parents[1] / "specs" -PROTOCOL_PATH = SPEC_DIR / "fuckmark-robustness-bench-v1.protocol.md" -VECTORS_PATH = SPEC_DIR / "fuckmark-robustness-bench-v1.vectors.json" -FREEZE_PATH = SPEC_DIR / "fuckmark-robustness-bench-v1.freeze.json" +PACKAGE_DATA_DIR = Path(__file__).resolve().parent / "robustness_data" SEALED_DETECTOR_SCORECARD_PATH = "specs/cycle8/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json" SEALED_DETECTOR_SCORECARD_HASH = "3df98598fa1f9fb3951029f105b43dfd5f3e83a9ec69fd5c160b31686b0ad6c9" +FREEZE_BINDING_KEYS = ( + "algorithm_version", + "protocol_sha256", + "vectors_file_sha256", + "vectors_canonical_sha256", + "sealed_detector_scorecard_path", + "sealed_detector_scorecard_hash", + "sealed_detector_scorecard_file_sha256", +) + + +def _artifact(name: str, repo_relative: str) -> Path: + packaged = PACKAGE_DATA_DIR / name + if packaged.is_file(): + return packaged + repo = Path(__file__).resolve().parents[1] / repo_relative + if repo.is_file(): + return repo + return packaged + + +PROTOCOL_PATH = _artifact( + "fuckmark-robustness-bench-v1.protocol.md", + "specs/fuckmark-robustness-bench-v1.protocol.md", +) +VECTORS_PATH = _artifact( + "fuckmark-robustness-bench-v1.vectors.json", + "specs/fuckmark-robustness-bench-v1.vectors.json", +) +FREEZE_PATH = _artifact( + "fuckmark-robustness-bench-v1.freeze.json", + "specs/fuckmark-robustness-bench-v1.freeze.json", +) +SCORECARD_FILE_PATH = _artifact( + "fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json", + SEALED_DETECTOR_SCORECARD_PATH, +) _UNICODE_SANITIZER_PATTERN = re.compile( "[\u00a0\u1680\u180e\u2000-\u200b\u200c\u200d\u200e\u200f\u2060\u2063\u202f\u205f\u3000" "\ufeff\uffa0\ufff9\ufffa\ufffb\ufe00\ufe01\ufe02\ufe03\ufe04\ufe05\ufe06\ufe07\ufe08\ufe09" @@ -234,7 +268,9 @@ def cell_dict(cell: RobustnessCell) -> dict[str, object]: def sealed_detector_track() -> dict[str, object]: - path = Path(__file__).resolve().parents[1] / SEALED_DETECTOR_SCORECARD_PATH + path = SCORECARD_FILE_PATH + if not path.is_file(): + raise FileNotFoundError(f"FuckMark robustness artifact is not installed: {path.name}") payload = json.loads(path.read_text(encoding="utf-8")) return { "id": "cycle8-gate-v2-confirmation-scorecard-v1", @@ -293,9 +329,17 @@ def build_vectors_payload() -> dict[str, object]: def load_vectors() -> dict[str, object]: + if not VECTORS_PATH.is_file(): + raise FileNotFoundError(f"FuckMark robustness artifact is not installed: {VECTORS_PATH.name}") return json.loads(VECTORS_PATH.read_text(encoding="utf-8")) +def load_freeze() -> dict[str, object]: + if not FREEZE_PATH.is_file(): + raise FileNotFoundError(f"FuckMark robustness artifact is not installed: {FREEZE_PATH.name}") + return json.loads(FREEZE_PATH.read_text(encoding="utf-8")) + + def compare_to_vectors(cells: Sequence[RobustnessCell], vectors: dict[str, object]) -> list[dict[str, object]]: expected = {item["id"]: item["expect"] for item in vectors["cells"]} mismatches: list[dict[str, object]] = [] @@ -308,6 +352,23 @@ def compare_to_vectors(cells: Sequence[RobustnessCell], vectors: dict[str, objec return mismatches +def compare_freeze(freeze: dict[str, object], live: dict[str, object]) -> list[dict[str, object]]: + mismatches: list[dict[str, object]] = [] + for key in FREEZE_BINDING_KEYS: + if freeze.get(key) != live.get(key): + mismatches.append({"id": f"freeze/{key}", "expected": freeze.get(key), "actual": live.get(key)}) + return mismatches + expected = {item["id"]: item["expect"] for item in vectors["cells"]} + mismatches: list[dict[str, object]] = [] + for cell in cells: + key = f"{cell.fixture_id}/{cell.attack_id}" + want = expected.get(key) + got = cell_expect(cell) + if want != got: + mismatches.append({"id": key, "expected": want, "actual": got}) + return mismatches + + def summary_dict(cells: Sequence[RobustnessCell], mismatches: Sequence[dict[str, object]]) -> dict[str, object]: restores = sum(1 for cell in cells if cell.restores_source) mix_ok = sum(1 for cell in cells if cell.mix_projection_equals_source) @@ -326,23 +387,34 @@ def run_robustness_bench( fixtures: Sequence[str] | None = None, attacks: Sequence[str] | None = None, ) -> dict[str, object]: + freeze = load_freeze() + live_freeze = freeze_bindings() + freeze_mismatches = compare_freeze(freeze, live_freeze) cells = iter_cells(fixtures=fixtures, attacks=attacks) vectors = load_vectors() - mismatches = compare_to_vectors(cells, vectors) + cell_mismatches = compare_to_vectors(cells, vectors) + mismatches = freeze_mismatches + cell_mismatches sealed = sealed_detector_track() - sealed_ok = sealed["scorecard_hash"] == sealed["expected_scorecard_hash"] + sealed_ok = ( + sealed["scorecard_hash"] == freeze.get("sealed_detector_scorecard_hash") == SEALED_DETECTOR_SCORECARD_HASH + and sealed["file_sha256"] == freeze.get("sealed_detector_scorecard_file_sha256") + and not freeze_mismatches + ) return { "algorithm_version": ROBUSTNESS_ALGORITHM_VERSION, "mix_mechanism_id": LETTER_MIX_MECHANISM_ID, "summary": summary_dict(cells, mismatches), "sealed_detector_track": sealed, "sealed_detector_ok": sealed_ok, + "freeze_ok": not freeze_mismatches, "mismatches": mismatches, "cells": [cell_dict(cell) for cell in cells], } def freeze_bindings() -> dict[str, object]: + if not PROTOCOL_PATH.is_file(): + raise FileNotFoundError(f"FuckMark robustness artifact is not installed: {PROTOCOL_PATH.name}") vectors = load_vectors() return { "algorithm_version": ROBUSTNESS_ALGORITHM_VERSION, @@ -351,9 +423,7 @@ def freeze_bindings() -> dict[str, object]: "vectors_canonical_sha256": sha256_json(vectors), "sealed_detector_scorecard_path": SEALED_DETECTOR_SCORECARD_PATH, "sealed_detector_scorecard_hash": SEALED_DETECTOR_SCORECARD_HASH, - "sealed_detector_scorecard_file_sha256": sha256_file( - Path(__file__).resolve().parents[1] / SEALED_DETECTOR_SCORECARD_PATH - ), + "sealed_detector_scorecard_file_sha256": sha256_file(SCORECARD_FILE_PATH), } @@ -399,6 +469,14 @@ def run_robustness_argv(argv: list[str], output: TextIO, errors: TextIO) -> int: return int(code) if isinstance(code, int) else ROBUSTNESS_EXIT_USAGE try: report = run_robustness_bench(fixtures=arguments.fixtures, attacks=arguments.attacks) + except json.JSONDecodeError as error: + errors.write(f"FuckMark: {error}\n") + errors.flush() + return ROBUSTNESS_EXIT_MISMATCH + except FileNotFoundError as error: + errors.write(f"FuckMark: {error}\n") + errors.flush() + return ROBUSTNESS_EXIT_MISMATCH except ValueError as error: errors.write(f"FuckMark: {error}\n") errors.flush() @@ -429,5 +507,5 @@ def run_robustness_argv(argv: list[str], output: TextIO, errors: TextIO) -> int: if mismatches: for item in mismatches[:12]: _emit(errors, f" mismatch {item['id']}") - failed = bool(mismatches) or not report["sealed_detector_ok"] + failed = bool(mismatches) or not report["sealed_detector_ok"] or not report["freeze_ok"] return ROBUSTNESS_EXIT_MISMATCH if failed else ROBUSTNESS_EXIT_OK diff --git a/fuckmark/robustness_data/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json b/fuckmark/robustness_data/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json new file mode 100644 index 00000000..6e870dbe --- /dev/null +++ b/fuckmark/robustness_data/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json @@ -0,0 +1 @@ +{"algorithm_version":"cycle8-gate-v2-confirmation-scorecard-v1","carrier_free_unicode_drop_from_identity":6,"carrier_free_unicode_watermarked_detected":182,"confirmation":true,"confirmation_grade":true,"corpora":[{"artifact_hash":"34528f2fdbd52d9f6e288c4487bf7f37d453144e73e0b16e34b284bf0d0412a5","carrier_free_unicode_watermarked_detected":61,"identity_pristine_watermarked_detected":64,"mix_max_score":0.5191244339951236,"mix_raw_unwatermarked_detected":0,"mix_raw_watermarked_detected":0,"mix_unicode_sanitizer_watermarked_detected":0,"path":"evidence/cycle8-gate-v2-confirmation-1200000-n64-2026-08-27/detector-compare.json","seed_base":1200000,"topic":"gate v2 confirmation primary","visible_pass_count":128},{"artifact_hash":"59288b71fbd1166b31e97e6e0ba72c484e050ffb0a456a949d45e91d52c3d234","carrier_free_unicode_watermarked_detected":60,"identity_pristine_watermarked_detected":61,"mix_max_score":0.5267390214198725,"mix_raw_unwatermarked_detected":0,"mix_raw_watermarked_detected":0,"mix_unicode_sanitizer_watermarked_detected":0,"path":"evidence/cycle8-gate-v2-confirmation-1210000-n64-2026-08-27/detector-compare.json","seed_base":1210000,"topic":"gate v2 confirmation replication","visible_pass_count":128},{"artifact_hash":"e1ec65072f1d41f7d62966e0d24b46bed6b800408dd60cd4697f31570e16e924","carrier_free_unicode_watermarked_detected":61,"identity_pristine_watermarked_detected":63,"mix_max_score":0.5164185408087847,"mix_raw_unwatermarked_detected":0,"mix_raw_watermarked_detected":0,"mix_unicode_sanitizer_watermarked_detected":0,"path":"evidence/cycle8-gate-v2-confirmation-1220000-n64-2026-08-27/detector-compare.json","seed_base":1220000,"topic":"gate v2 confirmation holdout","visible_pass_count":128}],"do_not_generate_950000":true,"evidence_label":"VERIFIED","fully_satisfied":true,"gate_version":"cycle8-publishability-gate-v2","identity_unwatermarked_detected":1,"identity_watermarked_detected":188,"mix_max_score":0.5267390214198725,"mix_unwatermarked_detected":0,"mix_watermarked_detected_by_diagnostic_sanitizer":{"required_bundle":188},"mix_watermarked_detected_by_required_sanitizer":{"cf_strip":0,"lm_watermarking_unicode_sanitizer":0,"nfc":0,"nfkc":0,"nfkc_cf_strip":0,"raw":0,"ws_collapse":0,"ws_collapse_nfkc_cf_strip":0},"notes":"One-shot Gate v2 confirmation of detector-after-sanitizer publishability. Do not rerun looking for zero. Do not retune on these corpora. required_bundle is diagnostic. Public CLI remains empty until a separate product-authorization step.","pass_conditions":{"carrier_free_unicode_floor":true,"carrier_free_unicode_max_drop":true,"identity_watermarked_detected":true,"mix_required_sanitizers_undetected":true,"mix_unwatermarked_undetected":true,"visible_invariance":true},"product_authorized":false,"rerun_looking_for_zero":false,"run_once":true,"scorecard_hash":"3df98598fa1f9fb3951029f105b43dfd5f3e83a9ec69fd5c160b31686b0ad6c9","status":"confirmed","threshold":0.5570987654320988,"visible_pass_rate":"192/192"} diff --git a/fuckmark/robustness_data/fuckmark-robustness-bench-v1.freeze.json b/fuckmark/robustness_data/fuckmark-robustness-bench-v1.freeze.json new file mode 100644 index 00000000..5e1a3714 --- /dev/null +++ b/fuckmark/robustness_data/fuckmark-robustness-bench-v1.freeze.json @@ -0,0 +1,12 @@ +{ + "algorithm_version": "fuckmark-robustness-bench-v1", + "freeze_record_version": "fuckmark-robustness-bench-v1-freeze-v1", + "frozen_at_utc": "2026-09-02T03:30:00+00:00", + "protocol_id": "fuckmark-robustness-bench-v1", + "protocol_sha256": "1d318bb3f7cdd826348eae5a26f93e12ee0710e5c607eb35d359a2f646dbbfd2", + "vectors_file_sha256": "c84fdb3f4ab0a0582b9bfc6254af1f7957d88682d01e0abb40922cf17643aa3c", + "vectors_canonical_sha256": "ceb7a7e0ac5608740ddf4271994315b28c4b052d20e68dba4e0a45c2e8ed8ba1", + "sealed_detector_scorecard_path": "specs/cycle8/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json", + "sealed_detector_scorecard_hash": "3df98598fa1f9fb3951029f105b43dfd5f3e83a9ec69fd5c160b31686b0ad6c9", + "sealed_detector_scorecard_file_sha256": "818fe948cd126c83fe8508847b581ebe5778073555752974fbb7e99ebfa8e6fa" +} diff --git a/fuckmark/robustness_data/fuckmark-robustness-bench-v1.protocol.md b/fuckmark/robustness_data/fuckmark-robustness-bench-v1.protocol.md new file mode 100644 index 00000000..930b469c --- /dev/null +++ b/fuckmark/robustness_data/fuckmark-robustness-bench-v1.protocol.md @@ -0,0 +1,87 @@ +# fuckmark-robustness-bench-v1 + +Public sanitizer-restore robustness bench for the live five-layer mix. +Hash-binding freeze record: `specs/fuckmark-robustness-bench-v1.freeze.json`. +Conformance vectors: `specs/fuckmark-robustness-bench-v1.vectors.json` +(source strings as codepoint arrays only). + +This bench answers: after the public mix, do standard Unicode sanitizers restore +the original source? It does **not** rerun GPT-2, SynthID, or any neural +detector. Detector numbers stay on the sealed Gate v2 scorecard. + +## 1. Identity + +- Algorithm version: `fuckmark-robustness-bench-v1` +- Mix mechanism: `u034f-ufe00-cc-me-cf-ia-letter-alt-v1` +- Python reference: `fuckmark.robustness` +- CLI: `fuckmark robustness` +- Contact: `Fhelp@q1z.org` + +A later v2 may add detector adapters from `fuckmark.adapters` as an opt-in +track. This v1 freeze is local, deterministic, and model-free. + +## 2. Cell + +For each fixture S and attack A: + +1. Mix: `M = apply_letter_alternating_mix(S)`. +2. Attack: `T = A(M)`. +3. Record: + - `restores_source`: `T == S` + - `mix_projection_equals_source`: visible projection of `M` equals `S` + - `projection_equals_source`: visible projection of `T` equals `S` + - `carrier_detected`: closed-set FuckMark insertion scan of `T` + - `residual_categories`: `fuckmark-hidden-scan-v1` categories still present in `T` + - `mix_sha256` / `output_sha256`: SHA-256 of UTF-8 `M` and `T` + +Visible projection is `project_visible_v1` (approved carriers removed; Me, Cc, +and Cf residuals remain). + +## 3. Attacks + +Applied to the mixed string, in this order in the catalog: + +1. `identity` — no sanitizer +2. `mn_strip` — drop general category Mn +3. `default_ignorable_strip` — drop default-ignorable codepoints +4. `nfc` / `nfkc` / `nfkd` +5. `cf_strip` — drop general category Cf +6. `me_strip` — drop general category Me +7. `cc_strip` — drop general category Cc +8. `unicode_sanitizer` — frozen copy of lm-watermarking `UnicodeSanitizer` + (NFC, BMP regex to space, collapse spaces, drop Cc) +9. `mn_then_us` / `di_then_us` / `us_then_mn` — orderings of Mn/DI strip and + UnicodeSanitizer +10. `required_bundle` — NFC, NFKC, Mn-strip, default-ignorable strip, Cf-strip +11. `required_bundle_then_us` +12. `mn_me_us` — Mn-strip, Me-strip, UnicodeSanitizer +13. `mn_me_us_cf` / `di_me_us_cf` — that path plus frozen Cf-strip + +UnicodeSanitizer turns interlinear annotation controls into spaces, so Cf-strip +after it cannot rebuild the original spacing. + +## 4. Fixtures + +Public fixtures are short. They are not the GPT-2 Gate v2 corpus. `digits` has +no eligible letter or emoji site, so mix is a no-op and every attack restores. +Mixed letter/emoji fixtures must not restore under the attacks above. + +## 5. Sealed detector track + +The Gate v2 confirmation scorecard remains the historical GPT-2 / SynthID +record (identity 188/192 detected; mix 0/192 after required sanitizers; +visible 192/192). This bench hashes that file and the inner `scorecard_hash`. +It does not regenerate those corpora. + +The CLI loads packaged copies under `fuckmark/robustness_data/` that must +match these `specs/` files byte-for-byte. Before reporting success it +recomputes protocol, vectors-file, vectors-canonical, and scorecard-file +SHA-256 and compares them to this freeze record. Changing scorecard bytes +while leaving the embedded `scorecard_hash` field alone is still a mismatch. + +## 6. Exit status + +`fuckmark robustness` exits 0 when every selected cell matches the frozen +vectors, the freeze hashes still match the loaded artifacts, and the sealed +scorecard hash still matches. Exit 1 is a mismatch (mix, sanitizer, or +freeze-hash drift). Exit 2 is usage. diff --git a/fuckmark/robustness_data/fuckmark-robustness-bench-v1.vectors.json b/fuckmark/robustness_data/fuckmark-robustness-bench-v1.vectors.json new file mode 100644 index 00000000..299d8ee9 --- /dev/null +++ b/fuckmark/robustness_data/fuckmark-robustness-bench-v1.vectors.json @@ -0,0 +1,3363 @@ +{ + "algorithm_version": "fuckmark-robustness-bench-v1", + "mix_mechanism_id": "u034f-ufe00-cc-me-cf-ia-letter-alt-v1", + "attacks": [ + "identity", + "mn_strip", + "default_ignorable_strip", + "nfc", + "nfkc", + "nfkd", + "cf_strip", + "me_strip", + "cc_strip", + "unicode_sanitizer", + "mn_then_us", + "di_then_us", + "us_then_mn", + "required_bundle", + "required_bundle_then_us", + "mn_me_us", + "mn_me_us_cf", + "di_me_us_cf" + ], + "fixtures": [ + { + "id": "ascii_prose", + "source": [ + 73, + 32, + 100, + 111, + 32, + 110, + 111, + 116, + 32, + 97, + 103, + 114, + 101, + 101, + 46 + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "ascii_clause", + "source": [ + 87, + 101, + 32, + 99, + 97, + 110, + 110, + 111, + 116, + 32, + 99, + 111, + 110, + 116, + 105, + 110, + 117, + 101, + 46 + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "nfd_latin", + "source": [ + 101, + 769 + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "emoji", + "source": [ + 128512 + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "greek", + "source": [ + 945, + 946 + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "cyrillic", + "source": [ + 1072, + 1073 + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "han", + "source": [ + 20013 + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "hangul", + "source": [ + 44032 + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "url_mixed", + "source": [ + 83, + 101, + 101, + 32, + 104, + 116, + 116, + 112, + 115, + 58, + 47, + 47, + 101, + 120, + 97, + 109, + 112, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 120, + 32, + 73, + 32, + 100, + 111, + 32, + 110, + 111, + 116, + 32, + 97, + 103, + 114, + 101, + 101, + 46 + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "mix_projection_equals_source": true, + "mixed": true + }, + { + "id": "digits", + "source": [ + 49, + 50, + 51, + 46 + ], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "mix_projection_equals_source": true, + "mixed": false + } + ], + "cells": [ + { + "id": "ascii_prose/identity", + "fixture": "ascii_prose", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d" + } + }, + { + "id": "ascii_prose/mn_strip", + "fixture": "ascii_prose", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "de800a87e319075d777c57ade25aa784cf7dd3136633a7c315d66acb87dfb78c" + } + }, + { + "id": "ascii_prose/default_ignorable_strip", + "fixture": "ascii_prose", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "de800a87e319075d777c57ade25aa784cf7dd3136633a7c315d66acb87dfb78c" + } + }, + { + "id": "ascii_prose/nfc", + "fixture": "ascii_prose", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d" + } + }, + { + "id": "ascii_prose/nfkc", + "fixture": "ascii_prose", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d" + } + }, + { + "id": "ascii_prose/nfkd", + "fixture": "ascii_prose", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d" + } + }, + { + "id": "ascii_prose/cf_strip", + "fixture": "ascii_prose", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "293f2ed34f680ff94635a6c0bf1f0fbabd39d5096dc7984c944808733a9856a1" + } + }, + { + "id": "ascii_prose/me_strip", + "fixture": "ascii_prose", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "75d683d5bc4fd3bcf1bd62e1999c12eed2b52f825747824bbf772ec5ee0ef758" + } + }, + { + "id": "ascii_prose/cc_strip", + "fixture": "ascii_prose", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "ea501f223eec60f3526b8e8b728cbbd57eb6368ac2e519e7425f52ab2e31ebe3" + } + }, + { + "id": "ascii_prose/unicode_sanitizer", + "fixture": "ascii_prose", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "7bb52edfb678cc9e2ee18f5425f6d6ee7f496a085b954eba4e27f2121812f4a1" + } + }, + { + "id": "ascii_prose/mn_then_us", + "fixture": "ascii_prose", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "a6c9d5f6deb1fac818d3154f2f1dae9bef79bb6b77e280bcdc3acb52b6ec8430" + } + }, + { + "id": "ascii_prose/di_then_us", + "fixture": "ascii_prose", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "a6c9d5f6deb1fac818d3154f2f1dae9bef79bb6b77e280bcdc3acb52b6ec8430" + } + }, + { + "id": "ascii_prose/us_then_mn", + "fixture": "ascii_prose", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "d1e2a542701f9e9a2f5a46742e9a1de8fd1c187576e8e190b79967b735b3957a" + } + }, + { + "id": "ascii_prose/required_bundle", + "fixture": "ascii_prose", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "8c7bc121f9be9af25515387405045d3cd6f72c4464e7f2a93f70f962e94309f7" + } + }, + { + "id": "ascii_prose/required_bundle_then_us", + "fixture": "ascii_prose", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "4e5ba236ac48180e149528d8b21a8c9689ff77afdb379da5cddaeccd7bc92fa4" + } + }, + { + "id": "ascii_prose/mn_me_us", + "fixture": "ascii_prose", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "c3385fd0a4b986619c38d8eaf47451efa8955b724b8cae425dbd06d0ca0ef172" + } + }, + { + "id": "ascii_prose/mn_me_us_cf", + "fixture": "ascii_prose", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "1fb6f4419886c7ee5aca8d44a2c9aaadf7295ad523978c2c88270f1465263a45" + } + }, + { + "id": "ascii_prose/di_me_us_cf", + "fixture": "ascii_prose", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "301634bdcd8bb1d0be4e0eafa1017386a58c6d5ed1514de8b2f9e4ae0c91ca3d", + "output_sha256": "1fb6f4419886c7ee5aca8d44a2c9aaadf7295ad523978c2c88270f1465263a45" + } + }, + { + "id": "ascii_clause/identity", + "fixture": "ascii_clause", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be" + } + }, + { + "id": "ascii_clause/mn_strip", + "fixture": "ascii_clause", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "371d49e40a3ac79e705f800954f775b31b4753f51012b31cf2b43c49b3e57417" + } + }, + { + "id": "ascii_clause/default_ignorable_strip", + "fixture": "ascii_clause", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "371d49e40a3ac79e705f800954f775b31b4753f51012b31cf2b43c49b3e57417" + } + }, + { + "id": "ascii_clause/nfc", + "fixture": "ascii_clause", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be" + } + }, + { + "id": "ascii_clause/nfkc", + "fixture": "ascii_clause", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be" + } + }, + { + "id": "ascii_clause/nfkd", + "fixture": "ascii_clause", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be" + } + }, + { + "id": "ascii_clause/cf_strip", + "fixture": "ascii_clause", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "b23bdc51893b3ae1f9165ab54f20d0ae4b1ee2ff905f2e069df0923e65f3e868" + } + }, + { + "id": "ascii_clause/me_strip", + "fixture": "ascii_clause", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "5c825ca966354c3a9256f7824c8a2e165cd5d072322347937096305a86450206" + } + }, + { + "id": "ascii_clause/cc_strip", + "fixture": "ascii_clause", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "38545d9c10b505ac8546a7c9c36f209770e546fe920ff8409029060c018a9305" + } + }, + { + "id": "ascii_clause/unicode_sanitizer", + "fixture": "ascii_clause", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "15accdaee1ece4ccdb49e9781f72c4703fdb562702ee1b216b810340cf5abc3f" + } + }, + { + "id": "ascii_clause/mn_then_us", + "fixture": "ascii_clause", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "5a0271e7b209eb9d4a3211758a730c0458d9593f3fd80535bd57338de45fd552" + } + }, + { + "id": "ascii_clause/di_then_us", + "fixture": "ascii_clause", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "5a0271e7b209eb9d4a3211758a730c0458d9593f3fd80535bd57338de45fd552" + } + }, + { + "id": "ascii_clause/us_then_mn", + "fixture": "ascii_clause", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "d4021102da682894624efc8f1d5e2616aed69e57115219e44b43ec5941466734" + } + }, + { + "id": "ascii_clause/required_bundle", + "fixture": "ascii_clause", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "d84fdcc0e23eeb2fffe125624c831fc6d711f739d341265858809dc0080c4508" + } + }, + { + "id": "ascii_clause/required_bundle_then_us", + "fixture": "ascii_clause", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "2787d0fb72c685a51c8704b6040e139dc47e7e570b63548393877b3f9c6583a2" + } + }, + { + "id": "ascii_clause/mn_me_us", + "fixture": "ascii_clause", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "3d9de8f43f39da89baa9059c58f51bbc33a64311139418e49c2a82ef83fdfffa" + } + }, + { + "id": "ascii_clause/mn_me_us_cf", + "fixture": "ascii_clause", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "dc98014ade366930c16b0df96c315d530199cc67012191f911af14d81a525849" + } + }, + { + "id": "ascii_clause/di_me_us_cf", + "fixture": "ascii_clause", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "c89595a873077370b474e6203d425512584ba7258d9b9a6cf6af8624a7a125be", + "output_sha256": "dc98014ade366930c16b0df96c315d530199cc67012191f911af14d81a525849" + } + }, + { + "id": "nfd_latin/identity", + "fixture": "nfd_latin", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded" + } + }, + { + "id": "nfd_latin/mn_strip", + "fixture": "nfd_latin", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "7b9b226c1bcb13f7f2f6cca28f26329d7e6045e0d8947acfe5551d5529eba57b" + } + }, + { + "id": "nfd_latin/default_ignorable_strip", + "fixture": "nfd_latin", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "dd80e8f315fd2b338b94e183453b102a55ff46e9092093db9fbe15c223907db0" + } + }, + { + "id": "nfd_latin/nfc", + "fixture": "nfd_latin", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "b4671680b248de718c2ccc7c63812057e947f4e8e15f8abba5b62b21ba03edf9" + } + }, + { + "id": "nfd_latin/nfkc", + "fixture": "nfd_latin", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "b4671680b248de718c2ccc7c63812057e947f4e8e15f8abba5b62b21ba03edf9" + } + }, + { + "id": "nfd_latin/nfkd", + "fixture": "nfd_latin", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded" + } + }, + { + "id": "nfd_latin/cf_strip", + "fixture": "nfd_latin", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "b55d3b119c9e1c061a78fdc174b9ace3bda1107d76c616236e27aa7b2425be97" + } + }, + { + "id": "nfd_latin/me_strip", + "fixture": "nfd_latin", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "deprecated", + "format", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "8d5b963f6782a65c7ad7003d4735a69b93e3212123ee4b4223f500fdaf39b89c" + } + }, + { + "id": "nfd_latin/cc_strip", + "fixture": "nfd_latin", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "64794315449e462eeebc11e1fd349e2401d67051ef780ff01652961d6bbcac75" + } + }, + { + "id": "nfd_latin/unicode_sanitizer", + "fixture": "nfd_latin", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "80d58e24d10fe955dac8c097cad84c819afd4f4423238de8f3680d508322296c" + } + }, + { + "id": "nfd_latin/mn_then_us", + "fixture": "nfd_latin", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "9a132ef10232336d2bd8ede21b5a9fbd286f95a33ed0c861ed04d1b1506246b6" + } + }, + { + "id": "nfd_latin/di_then_us", + "fixture": "nfd_latin", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "3b5e0713a3bdb94f14b227829743d85f17d46b1a93d36b04e3fd93ec99d1ffce" + } + }, + { + "id": "nfd_latin/us_then_mn", + "fixture": "nfd_latin", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "3b5e0713a3bdb94f14b227829743d85f17d46b1a93d36b04e3fd93ec99d1ffce" + } + }, + { + "id": "nfd_latin/required_bundle", + "fixture": "nfd_latin", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "6fc4cb93945152f0abb0ed322cf7f48b89f296ae87f6f98dedc6e45fd172ef01" + } + }, + { + "id": "nfd_latin/required_bundle_then_us", + "fixture": "nfd_latin", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "072af84bd61b5f8385796ac016234b01a2987801793d7dff44626b53385aec18" + } + }, + { + "id": "nfd_latin/mn_me_us", + "fixture": "nfd_latin", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "83f540d3d88ecd32a1f72c4ffd53586f16cf327c49d6d3b0c346c3cc904c4425" + } + }, + { + "id": "nfd_latin/mn_me_us_cf", + "fixture": "nfd_latin", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "f3401e2083d1f1d4a098c1091a3a9592d0c197a98d0f352d5f97be8c5ddf63d1" + } + }, + { + "id": "nfd_latin/di_me_us_cf", + "fixture": "nfd_latin", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "ffe473354f5d13e61d58c616dd60a5132d930a4cfc1f9c89264c8f1442cf5ded", + "output_sha256": "7a8d067d39df262ae963939a2e6777b7aadcdcd1202fc68dc9ad426d36d89a27" + } + }, + { + "id": "emoji/identity", + "fixture": "emoji", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb" + } + }, + { + "id": "emoji/mn_strip", + "fixture": "emoji", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "48af8e8b3f001394d9206ecc62bcf52c14876fc180e162d201362a7bb47287dc" + } + }, + { + "id": "emoji/default_ignorable_strip", + "fixture": "emoji", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "48af8e8b3f001394d9206ecc62bcf52c14876fc180e162d201362a7bb47287dc" + } + }, + { + "id": "emoji/nfc", + "fixture": "emoji", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb" + } + }, + { + "id": "emoji/nfkc", + "fixture": "emoji", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb" + } + }, + { + "id": "emoji/nfkd", + "fixture": "emoji", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb" + } + }, + { + "id": "emoji/cf_strip", + "fixture": "emoji", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "7a1d3e4147bbf4c5fdfccaa888d4a70b20f5503cddcc4f1c66c620ba145b57dd" + } + }, + { + "id": "emoji/me_strip", + "fixture": "emoji", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "deprecated", + "format", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "4d8e828688cbf1b2bb00c65ba0e961508472d7fd231d366b7d66a731defc3dbd" + } + }, + { + "id": "emoji/cc_strip", + "fixture": "emoji", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "ab2ca1e0360f7f7bf340b36116229f4d11aacc1724d4e4216a18c1d9c4d0224c" + } + }, + { + "id": "emoji/unicode_sanitizer", + "fixture": "emoji", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "d954eff1a9729d9ac878145690ab720c395f8a779a7503e94e2f652f9adefd19" + } + }, + { + "id": "emoji/mn_then_us", + "fixture": "emoji", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "b9f3659b0ee88a007a3f56ede750c4a56da7f6e41fd3f00e3ce0a232cf422c2d" + } + }, + { + "id": "emoji/di_then_us", + "fixture": "emoji", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "b9f3659b0ee88a007a3f56ede750c4a56da7f6e41fd3f00e3ce0a232cf422c2d" + } + }, + { + "id": "emoji/us_then_mn", + "fixture": "emoji", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "b9f3659b0ee88a007a3f56ede750c4a56da7f6e41fd3f00e3ce0a232cf422c2d" + } + }, + { + "id": "emoji/required_bundle", + "fixture": "emoji", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "89381612c80309456d58e616fe47d0ec5f6fb8c4caaa74a2c1a56f3303672f12" + } + }, + { + "id": "emoji/required_bundle_then_us", + "fixture": "emoji", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "34171f913e14bc1e28a49548c3ac79a138bd57a4621692abb3c047b3de71667d" + } + }, + { + "id": "emoji/mn_me_us", + "fixture": "emoji", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "e262d8fef01b7bca769465669213701652a317f4ecf57f738d2bd1fe79e93439" + } + }, + { + "id": "emoji/mn_me_us_cf", + "fixture": "emoji", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "db1759a9ace89ffd17820559db65f66c17028658cbaa2d3125e0c4d77cfada51" + } + }, + { + "id": "emoji/di_me_us_cf", + "fixture": "emoji", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "cd3192d495d47a100ed65030dd33e213e7f528ef6e6322ad7172a837034c7cbb", + "output_sha256": "db1759a9ace89ffd17820559db65f66c17028658cbaa2d3125e0c4d77cfada51" + } + }, + { + "id": "greek/identity", + "fixture": "greek", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c" + } + }, + { + "id": "greek/mn_strip", + "fixture": "greek", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "bca9bc765d450a1423e424a7492b8c6e6f508a8af7989b0b61f4509fb029dbd2" + } + }, + { + "id": "greek/default_ignorable_strip", + "fixture": "greek", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "bca9bc765d450a1423e424a7492b8c6e6f508a8af7989b0b61f4509fb029dbd2" + } + }, + { + "id": "greek/nfc", + "fixture": "greek", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c" + } + }, + { + "id": "greek/nfkc", + "fixture": "greek", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c" + } + }, + { + "id": "greek/nfkd", + "fixture": "greek", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c" + } + }, + { + "id": "greek/cf_strip", + "fixture": "greek", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "6aac9e373252935a4b0baceeb0b866d7370d771137bf87b0c6f91d7bcedc769c" + } + }, + { + "id": "greek/me_strip", + "fixture": "greek", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "55c7913fe2a506126741bf445630af732571c7d84d84731e951d2b18556fe2f5" + } + }, + { + "id": "greek/cc_strip", + "fixture": "greek", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "f32ed8bad164a8c33a7c3b35e082fa24ae8152277f9bbd843276fbbef20dd5a0" + } + }, + { + "id": "greek/unicode_sanitizer", + "fixture": "greek", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "8ad14ba81c6a26c23ebec84f5455b83dfeec204779b4e7962f232b997edd1fdc" + } + }, + { + "id": "greek/mn_then_us", + "fixture": "greek", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "2cc227b489a2b195b1978e74670a7ccfd00aed4c6ffd38720f3efd84858999fb" + } + }, + { + "id": "greek/di_then_us", + "fixture": "greek", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "2cc227b489a2b195b1978e74670a7ccfd00aed4c6ffd38720f3efd84858999fb" + } + }, + { + "id": "greek/us_then_mn", + "fixture": "greek", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "cdcda385f5e8f2b72a88734cd4567f5c3af92a152ef7c2183ca840570e7ef98d" + } + }, + { + "id": "greek/required_bundle", + "fixture": "greek", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "8fc04360f470f69fcd4eb1300c9ecbaa2a2e07b7a99973abb5d76975560b91bd" + } + }, + { + "id": "greek/required_bundle_then_us", + "fixture": "greek", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "e0b89e94bf89ddf228234020ae983e915f0564b6ebf11824a290c9555db24359" + } + }, + { + "id": "greek/mn_me_us", + "fixture": "greek", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "4eb80d82ce3fdb0d8592ea8dabcebb842080b09ca16a132f1c2427f8d2c8625f" + } + }, + { + "id": "greek/mn_me_us_cf", + "fixture": "greek", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "fa9944962ebd5fc5e1ec7459ceedde46eec5e5dbf4e0f734218dd07cc33ff45a" + } + }, + { + "id": "greek/di_me_us_cf", + "fixture": "greek", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "9a6208d38d7bbc44480fce049d20956efc3bd233c29cdc600d29bf0f84d8e65c", + "output_sha256": "fa9944962ebd5fc5e1ec7459ceedde46eec5e5dbf4e0f734218dd07cc33ff45a" + } + }, + { + "id": "cyrillic/identity", + "fixture": "cyrillic", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d" + } + }, + { + "id": "cyrillic/mn_strip", + "fixture": "cyrillic", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "47240465a2170ea848a42b5a4e1523f23b2c67e4baef978b27a8be0f90e32352" + } + }, + { + "id": "cyrillic/default_ignorable_strip", + "fixture": "cyrillic", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "47240465a2170ea848a42b5a4e1523f23b2c67e4baef978b27a8be0f90e32352" + } + }, + { + "id": "cyrillic/nfc", + "fixture": "cyrillic", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d" + } + }, + { + "id": "cyrillic/nfkc", + "fixture": "cyrillic", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d" + } + }, + { + "id": "cyrillic/nfkd", + "fixture": "cyrillic", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d" + } + }, + { + "id": "cyrillic/cf_strip", + "fixture": "cyrillic", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "7573d27a7e446269185bbc62c0b066a5c7423bef8b04c910cab02d1e10d4b8d0" + } + }, + { + "id": "cyrillic/me_strip", + "fixture": "cyrillic", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "a5cabf7f514e1869d71ade5d25f41509267ca07e7312325fd7650c29272d9e9a" + } + }, + { + "id": "cyrillic/cc_strip", + "fixture": "cyrillic", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "6dd2b56a60748ab51729ad5c1cae599caf65f0de7e34fb77c96bd304c4f61f66" + } + }, + { + "id": "cyrillic/unicode_sanitizer", + "fixture": "cyrillic", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "0d662b1590b8e86653c4eaf2cfd7c00da1e8b18262473c19c163162ff09e70ba" + } + }, + { + "id": "cyrillic/mn_then_us", + "fixture": "cyrillic", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "530127fbc4472a1b6360293df5e112088646df4cd0cbf496d0c1e4c6a98947f3" + } + }, + { + "id": "cyrillic/di_then_us", + "fixture": "cyrillic", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "530127fbc4472a1b6360293df5e112088646df4cd0cbf496d0c1e4c6a98947f3" + } + }, + { + "id": "cyrillic/us_then_mn", + "fixture": "cyrillic", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "3315cc4e425d92eb595c8865330929d25d0c4d46b5e6dd750c2c6746762a8e95" + } + }, + { + "id": "cyrillic/required_bundle", + "fixture": "cyrillic", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "e1e44049062afbf5c16999f422fc706beff319247693c9dc1dc76da4d933a506" + } + }, + { + "id": "cyrillic/required_bundle_then_us", + "fixture": "cyrillic", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "b58f7ec5af563bf91eca3b56920e3d3b7c919ccbb4f5e8a228af2bb495f1d316" + } + }, + { + "id": "cyrillic/mn_me_us", + "fixture": "cyrillic", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "70e1e2ffc6b4518941744a87cbc1e184f42c7a1d77fb1cf7bfab7a18f71e8ca3" + } + }, + { + "id": "cyrillic/mn_me_us_cf", + "fixture": "cyrillic", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "1474ce7c15d02096e17484aa5981c662989982e51262e0075c021e636600ecf4" + } + }, + { + "id": "cyrillic/di_me_us_cf", + "fixture": "cyrillic", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "4ade3e648c50cd953f98b2bb352a0f4231512fa563211254e28d44e29ce49f0d", + "output_sha256": "1474ce7c15d02096e17484aa5981c662989982e51262e0075c021e636600ecf4" + } + }, + { + "id": "han/identity", + "fixture": "han", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9" + } + }, + { + "id": "han/mn_strip", + "fixture": "han", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "3e1b8cfef2619b3e061b35d3a0c6139c02b27d990addcd40b7b4d0f5f6756792" + } + }, + { + "id": "han/default_ignorable_strip", + "fixture": "han", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "3e1b8cfef2619b3e061b35d3a0c6139c02b27d990addcd40b7b4d0f5f6756792" + } + }, + { + "id": "han/nfc", + "fixture": "han", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9" + } + }, + { + "id": "han/nfkc", + "fixture": "han", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9" + } + }, + { + "id": "han/nfkd", + "fixture": "han", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9" + } + }, + { + "id": "han/cf_strip", + "fixture": "han", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "44abc3e256af1175b1a2ef740658e23b69f83c60d7051e44ad49ea4b86b2ec9f" + } + }, + { + "id": "han/me_strip", + "fixture": "han", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "deprecated", + "format", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "045637d021cb61e2f4c9bf961be9d9a4a523fac6cce29de01d3e174d65c16bed" + } + }, + { + "id": "han/cc_strip", + "fixture": "han", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "a55a7bec3899619e5ee683f4c6153c8038b4bf6910bb6f4bec07fc2aacf4e468" + } + }, + { + "id": "han/unicode_sanitizer", + "fixture": "han", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "52330acff1fe76fed1a913d6a9587eef7911f88169dcdb62b4a18a00cbc1230d" + } + }, + { + "id": "han/mn_then_us", + "fixture": "han", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "9f9796db4c35bb524cebb46d5f72dba4ec3f62091c284e76092bf06d22292bcd" + } + }, + { + "id": "han/di_then_us", + "fixture": "han", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "9f9796db4c35bb524cebb46d5f72dba4ec3f62091c284e76092bf06d22292bcd" + } + }, + { + "id": "han/us_then_mn", + "fixture": "han", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "9f9796db4c35bb524cebb46d5f72dba4ec3f62091c284e76092bf06d22292bcd" + } + }, + { + "id": "han/required_bundle", + "fixture": "han", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "e801ce4fa3e2ea8a080aa7d482a162fa944e91529072b650b8cf274a31ee5264" + } + }, + { + "id": "han/required_bundle_then_us", + "fixture": "han", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "7f91f015be843006ed4d6da3ae3b48892f281dd6c43a18d6ce72d3b2304f4a46" + } + }, + { + "id": "han/mn_me_us", + "fixture": "han", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "439b6f273b0dd83a1f344eb1f054988ad86adf6f6e9bb8f2b00670f3c3778d73" + } + }, + { + "id": "han/mn_me_us_cf", + "fixture": "han", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "2f932a5b0329537dc6ea38ae01a160a223a47256864662c55ae2c72360554d4b" + } + }, + { + "id": "han/di_me_us_cf", + "fixture": "han", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "aaed52e79f8827756eff6d97c1fa0c16d8d52fd592a15f462dce65a2602be9b9", + "output_sha256": "2f932a5b0329537dc6ea38ae01a160a223a47256864662c55ae2c72360554d4b" + } + }, + { + "id": "hangul/identity", + "fixture": "hangul", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664" + } + }, + { + "id": "hangul/mn_strip", + "fixture": "hangul", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "edc2bb560e4971c0cb3a6e35b3d0e7c701e5f1bdd30f4501b41f8ef8b65d2925" + } + }, + { + "id": "hangul/default_ignorable_strip", + "fixture": "hangul", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "edc2bb560e4971c0cb3a6e35b3d0e7c701e5f1bdd30f4501b41f8ef8b65d2925" + } + }, + { + "id": "hangul/nfc", + "fixture": "hangul", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664" + } + }, + { + "id": "hangul/nfkc", + "fixture": "hangul", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664" + } + }, + { + "id": "hangul/nfkd", + "fixture": "hangul", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "c4d19198ade11637f2d6974c530613e3d01584b6ce3c7d1e49914d1769d18b00" + } + }, + { + "id": "hangul/cf_strip", + "fixture": "hangul", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "43359e805d74c04c7a67daa07f5bf03c9b61bcf2e7fa665bb0ca8efd3bb146d9" + } + }, + { + "id": "hangul/me_strip", + "fixture": "hangul", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "deprecated", + "format", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "9ea5664a5a152bb0def1cfe9fb6c5eef832357e4dea52cf3fc5510d187dbd127" + } + }, + { + "id": "hangul/cc_strip", + "fixture": "hangul", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "9763f9284245147d406a93c3a2d943473d59cf34e9fe4ea5120980f146b628ff" + } + }, + { + "id": "hangul/unicode_sanitizer", + "fixture": "hangul", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "b616729a4d762db9e6a1252bdbf179da5ccaa51cb28a98ae3f9f52cb3fd4f0c0" + } + }, + { + "id": "hangul/mn_then_us", + "fixture": "hangul", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "e648c1a588c3598c88b34b6636274646852e770702343b29fba327e4824901cd" + } + }, + { + "id": "hangul/di_then_us", + "fixture": "hangul", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "e648c1a588c3598c88b34b6636274646852e770702343b29fba327e4824901cd" + } + }, + { + "id": "hangul/us_then_mn", + "fixture": "hangul", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "e648c1a588c3598c88b34b6636274646852e770702343b29fba327e4824901cd" + } + }, + { + "id": "hangul/required_bundle", + "fixture": "hangul", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "e2224e298113b8a82a3d98e36ecb2f8f8239cb5d0be3ff089f1e8123a9d0e4dc" + } + }, + { + "id": "hangul/required_bundle_then_us", + "fixture": "hangul", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "523a21039028d365e14c2af243b7c2f65a0cbe2ae191440bd27ed79b85dff8e9" + } + }, + { + "id": "hangul/mn_me_us", + "fixture": "hangul", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "583b37586c4ca3428f5c17516c4b475eadfd3e61f90df918367bd51ff52d3987" + } + }, + { + "id": "hangul/mn_me_us_cf", + "fixture": "hangul", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "c5421efe9a5b8374f020097c81c3bf425c43ee52ad15a9b5b8a042a4f7bee9ed" + } + }, + { + "id": "hangul/di_me_us_cf", + "fixture": "hangul", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "dbc306e43c532df41347c717afb52d25c634a7d9cea38fef2a087ba7e7868664", + "output_sha256": "c5421efe9a5b8374f020097c81c3bf425c43ee52ad15a9b5b8a042a4f7bee9ed" + } + }, + { + "id": "url_mixed/identity", + "fixture": "url_mixed", + "attack": "identity", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67" + } + }, + { + "id": "url_mixed/mn_strip", + "fixture": "url_mixed", + "attack": "mn_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "8818678c99cc9407ea16572307966cd7e77e9d86b33938806b556c9c55e919b3" + } + }, + { + "id": "url_mixed/default_ignorable_strip", + "fixture": "url_mixed", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "8818678c99cc9407ea16572307966cd7e77e9d86b33938806b556c9c55e919b3" + } + }, + { + "id": "url_mixed/nfc", + "fixture": "url_mixed", + "attack": "nfc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67" + } + }, + { + "id": "url_mixed/nfkc", + "fixture": "url_mixed", + "attack": "nfkc", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67" + } + }, + { + "id": "url_mixed/nfkd", + "fixture": "url_mixed", + "attack": "nfkd", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67" + } + }, + { + "id": "url_mixed/cf_strip", + "fixture": "url_mixed", + "attack": "cf_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "2f515d68cac2ede2814597c9db1a1524b717ee333960506de956f217281f9dfd" + } + }, + { + "id": "url_mixed/me_strip", + "fixture": "url_mixed", + "attack": "me_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "deprecated", + "format", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "d3606380dcf2b614bd12c51be7ab2203c02ed7f92fcbc95f51aeac3580747c55" + } + }, + { + "id": "url_mixed/cc_strip", + "fixture": "url_mixed", + "attack": "cc_strip", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "variation_selector", + "enclosing_mark", + "deprecated", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "41670f567a3564b84857c4fa5dfd3833a4e8744a362e59d60a990d219385eec3" + } + }, + { + "id": "url_mixed/unicode_sanitizer", + "fixture": "url_mixed", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "zero_width", + "enclosing_mark", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "7a09e8170f3fdd540f04666a69f7a7c08577caa56a8898496f5cd040fe2b599c" + } + }, + { + "id": "url_mixed/mn_then_us", + "fixture": "url_mixed", + "attack": "mn_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "61acd38e2b1db49bc43553f6ff723b2d2bd78c38bc7e37b3d4e6565c9e92b458" + } + }, + { + "id": "url_mixed/di_then_us", + "fixture": "url_mixed", + "attack": "di_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "61acd38e2b1db49bc43553f6ff723b2d2bd78c38bc7e37b3d4e6565c9e92b458" + } + }, + { + "id": "url_mixed/us_then_mn", + "fixture": "url_mixed", + "attack": "us_then_mn", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "a1fb089533a4ee4448c23b3e959a6c91156b33c5cbc979a0fd669434eab6f985" + } + }, + { + "id": "url_mixed/required_bundle", + "fixture": "url_mixed", + "attack": "required_bundle", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark", + "control" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "808f70cfcac9e1ca14e66e9c62353135738e936cf52555938b0b671a4ea232c4" + } + }, + { + "id": "url_mixed/required_bundle_then_us", + "fixture": "url_mixed", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": true, + "residual_categories": [ + "enclosing_mark" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "461ad0cbe74d538a00d8e019f86794f3a6722ca6374a1795117076eaf3b14081" + } + }, + { + "id": "url_mixed/mn_me_us", + "fixture": "url_mixed", + "attack": "mn_me_us", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": true, + "residual_categories": [ + "format" + ], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "fa0fba7d062e379969908643c0e68b50f550b99dfe319729d7eca3b27e9c8238" + } + }, + { + "id": "url_mixed/mn_me_us_cf", + "fixture": "url_mixed", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "38c2d97d995f358011fa4779d88f2cca5d9398e2facf61c81b1ff8f7bdfb4262" + } + }, + { + "id": "url_mixed/di_me_us_cf", + "fixture": "url_mixed", + "attack": "di_me_us_cf", + "expect": { + "restores_source": false, + "mix_projection_equals_source": true, + "projection_equals_source": false, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "343722d77853218794890770e1116e198f07640d454cc1196830f325160a1b67", + "output_sha256": "38c2d97d995f358011fa4779d88f2cca5d9398e2facf61c81b1ff8f7bdfb4262" + } + }, + { + "id": "digits/identity", + "fixture": "digits", + "attack": "identity", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/mn_strip", + "fixture": "digits", + "attack": "mn_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/default_ignorable_strip", + "fixture": "digits", + "attack": "default_ignorable_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/nfc", + "fixture": "digits", + "attack": "nfc", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/nfkc", + "fixture": "digits", + "attack": "nfkc", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/nfkd", + "fixture": "digits", + "attack": "nfkd", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/cf_strip", + "fixture": "digits", + "attack": "cf_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/me_strip", + "fixture": "digits", + "attack": "me_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/cc_strip", + "fixture": "digits", + "attack": "cc_strip", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/unicode_sanitizer", + "fixture": "digits", + "attack": "unicode_sanitizer", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/mn_then_us", + "fixture": "digits", + "attack": "mn_then_us", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/di_then_us", + "fixture": "digits", + "attack": "di_then_us", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/us_then_mn", + "fixture": "digits", + "attack": "us_then_mn", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/required_bundle", + "fixture": "digits", + "attack": "required_bundle", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/required_bundle_then_us", + "fixture": "digits", + "attack": "required_bundle_then_us", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/mn_me_us", + "fixture": "digits", + "attack": "mn_me_us", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/mn_me_us_cf", + "fixture": "digits", + "attack": "mn_me_us_cf", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + }, + { + "id": "digits/di_me_us_cf", + "fixture": "digits", + "attack": "di_me_us_cf", + "expect": { + "restores_source": true, + "mix_projection_equals_source": true, + "projection_equals_source": true, + "carrier_detected": false, + "residual_categories": [], + "mix_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec", + "output_sha256": "a5813ad799f804b5cc8d127f9c7e4e09cf96ce2135b9f77b379881a0ab2d1aec" + } + } + ] +} diff --git a/pyproject.toml b/pyproject.toml index e74d25d9..31c556a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,4 +50,4 @@ where = ["."] include = ["fuckmark*"] [tool.setuptools.package-data] -fuckmark = ["webui/*.html", "webui/*.js", "webui/*.wasm"] +fuckmark = ["webui/*.html", "webui/*.js", "webui/*.wasm", "robustness_data/*"] diff --git a/specs/fuckmark-robustness-bench-v1.freeze.json b/specs/fuckmark-robustness-bench-v1.freeze.json index 2c0f2eda..5e1a3714 100644 --- a/specs/fuckmark-robustness-bench-v1.freeze.json +++ b/specs/fuckmark-robustness-bench-v1.freeze.json @@ -3,7 +3,7 @@ "freeze_record_version": "fuckmark-robustness-bench-v1-freeze-v1", "frozen_at_utc": "2026-09-02T03:30:00+00:00", "protocol_id": "fuckmark-robustness-bench-v1", - "protocol_sha256": "9808da06fce59d952c98464dcdd0f0df45968b363b57d00307c01ed794813cbe", + "protocol_sha256": "1d318bb3f7cdd826348eae5a26f93e12ee0710e5c607eb35d359a2f646dbbfd2", "vectors_file_sha256": "c84fdb3f4ab0a0582b9bfc6254af1f7957d88682d01e0abb40922cf17643aa3c", "vectors_canonical_sha256": "ceb7a7e0ac5608740ddf4271994315b28c4b052d20e68dba4e0a45c2e8ed8ba1", "sealed_detector_scorecard_path": "specs/cycle8/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json", diff --git a/specs/fuckmark-robustness-bench-v1.protocol.md b/specs/fuckmark-robustness-bench-v1.protocol.md index f5427790..930b469c 100644 --- a/specs/fuckmark-robustness-bench-v1.protocol.md +++ b/specs/fuckmark-robustness-bench-v1.protocol.md @@ -73,8 +73,15 @@ record (identity 188/192 detected; mix 0/192 after required sanitizers; visible 192/192). This bench hashes that file and the inner `scorecard_hash`. It does not regenerate those corpora. +The CLI loads packaged copies under `fuckmark/robustness_data/` that must +match these `specs/` files byte-for-byte. Before reporting success it +recomputes protocol, vectors-file, vectors-canonical, and scorecard-file +SHA-256 and compares them to this freeze record. Changing scorecard bytes +while leaving the embedded `scorecard_hash` field alone is still a mismatch. + ## 6. Exit status `fuckmark robustness` exits 0 when every selected cell matches the frozen -vectors and the sealed scorecard hash still matches. Exit 1 is a mismatch -(mix or sanitizer drift). Exit 2 is usage. +vectors, the freeze hashes still match the loaded artifacts, and the sealed +scorecard hash still matches. Exit 1 is a mismatch (mix, sanitizer, or +freeze-hash drift). Exit 2 is usage. diff --git a/tests/test_robustness.py b/tests/test_robustness.py index 8d2a5621..d398c0df 100644 --- a/tests/test_robustness.py +++ b/tests/test_robustness.py @@ -13,11 +13,13 @@ from fuckmark.robustness import ( ATTACK_IDS, FREEZE_PATH, + PACKAGE_DATA_DIR, PROTOCOL_PATH, ROBUSTNESS_ALGORITHM_VERSION, ROBUSTNESS_EXIT_MISMATCH, ROBUSTNESS_EXIT_OK, ROBUSTNESS_EXIT_USAGE, + SCORECARD_FILE_PATH, SEALED_DETECTOR_SCORECARD_HASH, SEALED_DETECTOR_SCORECARD_PATH, VECTORS_PATH, @@ -28,6 +30,7 @@ fixture_ids, fixture_source, iter_cells, + load_freeze, load_vectors, measure_cell, run_robustness_argv, @@ -65,6 +68,21 @@ def test_robustness_freeze_binds_protocol_vectors_and_scorecard() -> None: assert payload["algorithm_version"] == ROBUSTNESS_ALGORITHM_VERSION assert "fuckmark-robustness-bench-v1" in PROTOCOL_PATH.read_text(encoding="utf-8") assert "does **not** rerun" in PROTOCOL_PATH.read_text(encoding="utf-8") + assert live == freeze_bindings() + + +def test_packaged_robustness_artifacts_match_specs() -> None: + pairs = ( + (ROOT / "specs" / "fuckmark-robustness-bench-v1.protocol.md", PROTOCOL_PATH), + (ROOT / "specs" / "fuckmark-robustness-bench-v1.vectors.json", VECTORS_PATH), + (ROOT / "specs" / "fuckmark-robustness-bench-v1.freeze.json", FREEZE_PATH), + (ROOT / SEALED_DETECTOR_SCORECARD_PATH, SCORECARD_FILE_PATH), + ) + for public, packaged in pairs: + assert packaged.is_file(), packaged + assert packaged.parent == PACKAGE_DATA_DIR + assert packaged.read_bytes() == public.read_bytes(), packaged.name + assert PROTOCOL_PATH.resolve() != (ROOT / "specs" / "fuckmark-robustness-bench-v1.protocol.md").resolve() def test_sealed_scorecard_constants_match_gate_v2() -> None: @@ -105,6 +123,7 @@ def test_digits_restore_and_mixed_fixtures_never_restore() -> None: assert summary["mix_projection_equals_source"] == 180 assert summary["mismatches"] == 0 assert report["sealed_detector_ok"] is True + assert report["freeze_ok"] is True digits = [cell for cell in iter_cells(fixtures=("digits",))] assert len(digits) == 18 assert all(cell.restores_source for cell in digits) @@ -171,6 +190,7 @@ def test_cli_json_and_main_dispatch() -> None: assert payload["summary"]["restores_source"] == 1 assert payload["summary"]["mismatches"] == 0 assert payload["sealed_detector_ok"] is True + assert payload["freeze_ok"] is True assert payload["cells"][0]["id"] == "digits/identity" dispatched_out, dispatched_err = StringIO(), StringIO() dispatched = main( @@ -209,12 +229,60 @@ def test_cli_usage_errors() -> None: def test_cli_mismatch_is_exit_one(monkeypatch) -> None: - monkeypatch.setattr("fuckmark.robustness.load_vectors", lambda: {"cells": []}) - code, out, err = _run(["--json", "--fixture", "digits", "--attack", "identity"]) + monkeypatch.setattr( + "fuckmark.robustness.compare_to_vectors", + lambda _cells, _vectors: [ + {"id": "digits/identity", "expected": {"restores_source": False}, "actual": {"restores_source": True}} + ], + ) + code, out, _err = _run(["--json", "--fixture", "digits", "--attack", "identity"]) assert code == ROBUSTNESS_EXIT_MISMATCH payload = json.loads(out) assert payload["summary"]["mismatches"] == 1 - assert "digits/identity" in err or payload["mismatches"][0]["id"] == "digits/identity" + assert payload["freeze_ok"] is True + assert payload["mismatches"][0]["id"] == "digits/identity" + + +def test_cli_freeze_hash_mismatch_is_exit_one(monkeypatch) -> None: + freeze = load_freeze() + + def drifted() -> dict: + payload = dict(freeze) + payload["vectors_file_sha256"] = "00" * 32 + return payload + + monkeypatch.setattr("fuckmark.robustness.load_freeze", drifted) + code, out, _err = _run(["--json", "--fixture", "digits", "--attack", "identity"]) + assert code == ROBUSTNESS_EXIT_MISMATCH + payload = json.loads(out) + assert payload["freeze_ok"] is False + assert payload["sealed_detector_ok"] is False + assert payload["cells"][0]["expect"]["restores_source"] is True + assert any(item["id"] == "freeze/vectors_file_sha256" for item in payload["mismatches"]) + + +def test_cli_scorecard_file_hash_mismatch_is_exit_one(monkeypatch) -> None: + freeze = load_freeze() + + def drifted() -> dict: + payload = dict(freeze) + payload["sealed_detector_scorecard_file_sha256"] = "11" * 32 + return payload + + monkeypatch.setattr("fuckmark.robustness.load_freeze", drifted) + code, out, _err = _run(["--json", "--fixture", "digits", "--attack", "identity"]) + assert code == ROBUSTNESS_EXIT_MISMATCH + payload = json.loads(out) + assert payload["freeze_ok"] is False + assert payload["sealed_detector_ok"] is False + assert any(item["id"] == "freeze/sealed_detector_scorecard_file_sha256" for item in payload["mismatches"]) + + +def test_cli_missing_artifact_is_exit_one(monkeypatch, tmp_path: Path) -> None: + monkeypatch.setattr("fuckmark.robustness.FREEZE_PATH", tmp_path / "missing-freeze.json") + code, _out, err = _run(["--json", "--fixture", "digits", "--attack", "identity"]) + assert code == ROBUSTNESS_EXIT_MISMATCH + assert "not installed" in err def test_cli_help_mentions_robustness_bench(capsys) -> None: diff --git a/tools/verify_release_install.py b/tools/verify_release_install.py index 60df9166..f7d33203 100644 --- a/tools/verify_release_install.py +++ b/tools/verify_release_install.py @@ -1,6 +1,7 @@ from __future__ import annotations import hashlib +import json import os import subprocess import sys @@ -154,6 +155,32 @@ def _verify_artifact(artifact: Path) -> None: help_text = _run([str(command), "--help"], capture_output=True, env=environment).stdout if "fuckmark" not in help_text.casefold() or "--visible" not in help_text or ":done" not in help_text: raise RuntimeError(f"installed CLI --help failed for {command.name}: {help_text!r}") + if "fuckmark robustness" not in help_text: + raise RuntimeError(f"installed CLI --help missing robustness for {command.name}: {help_text!r}") + try: + bench = _run( + [str(command), "robustness", "--json", "--fixture", "digits", "--attack", "identity"], + capture_output=True, + env=environment, + ) + except subprocess.CalledProcessError as error: + raise RuntimeError( + f"installed CLI robustness failed for {command.name}: stdout={error.stdout!r} stderr={error.stderr!r}" + ) from error + try: + bench_payload = json.loads(bench.stdout) + except json.JSONDecodeError as error: + raise RuntimeError( + f"installed CLI robustness JSON failed for {command.name}: stdout={bench.stdout!r}" + ) from error + if bench_payload.get("freeze_ok") is not True or bench_payload.get("sealed_detector_ok") is not True: + raise RuntimeError( + f"installed CLI robustness integrity failed for {command.name}: {bench_payload!r}" + ) + if bench_payload.get("summary", {}).get("restores_source") != 1: + raise RuntimeError( + f"installed CLI robustness digits cell failed for {command.name}: {bench_payload!r}" + ) def main() -> int: From cee921d307dbe300e5a3eb940c3e581cc2140bf9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 03:54:50 +0000 Subject: [PATCH 3/3] Fold CRLF before hashing robustness freeze artifacts Windows Package E2E failed because Git checkout CRLF changed protocol, vectors, and scorecard file SHA-256. Hash UTF-8 after LF folding so the freeze matches, and pin those artifacts to LF in .gitattributes. Co-authored-by: Byte271 --- .gitattributes | 5 +++++ CHANGELOG.md | 2 +- docs/robustness.md | 6 +++++- fuckmark/hashing.py | 7 +++++++ fuckmark/robustness.py | 19 +++++-------------- tests/test_hashing.py | 16 ++++++++++------ tests/test_robustness.py | 19 +++++++++++++++---- 7 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..49e750a9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +fuckmark/robustness_data/** text eol=lf +specs/fuckmark-robustness-bench-v1.protocol.md text eol=lf +specs/fuckmark-robustness-bench-v1.vectors.json text eol=lf +specs/fuckmark-robustness-bench-v1.freeze.json text eol=lf +specs/cycle8/fuckmark-cycle8-gate-v2-confirmation-scorecard-v1.json text eol=lf diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a678220..b9157c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ - WASM build of `fuckmark-hidden-scan-v1` (`crates/fuckmark-scan`, committed `fuckmark_scan.wasm`). The scan page and the Chromium popup load it when `fetch` works so classification stays on-device; `scan.js` remains the `file://` fallback. Lone UTF-16 surrogates route through the JS engine (UTF-8 cannot carry them). Empty category arrays stay empty selections. The extension CSP allows `wasm-unsafe-eval` so Chromium can instantiate the module. Page reveal and paste-safe keep the synchronous JS port. Rebuild with `crates/fuckmark-scan/build-wasm.sh`. - Language bindings for the same Rust scan ABI: C header (`crates/fuckmark-scan/include/fuckmark_scan.h`), optional Python ctypes loader (`fuckmark.native_scan`), and a Node package under `bindings/node` (WASM + JS fallback). Host library build: `crates/fuckmark-scan/build-native.sh`. Lone UTF-16 surrogates cannot cross the UTF-8 ABI, so Python `scan_text` / `clean_text` delegate those strings to the Python reference scanner, and the WASM wrapper normalizes the JS fallback result to the same `index` / `truncated` / `highest_severity` / `source_length` schema. The C example is linked with `-rpath,$ORIGIN` and written next to `libfuckmark_scan.so`. Overview: `bindings/README.md`. - OS clipboard watcher (`fuckmark-clipboard-watch-v1`). `fuckmark clipboard scan` / `clean` / `watch` poll the system clipboard (pbpaste, wl-paste, xclip, xsel, PowerShell Get-Clipboard) for the same security category set as `fuckmark lint`. `--clean` rewrites the clipboard in place after a digest re-read so a newer copy is not overwritten. Snapshots hash with UTF-8 `surrogatepass` so lone surrogates do not crash the watcher. Linux read uses `wl-paste` without `--no-newline`. Emoji ZWJ sequences (both neighbors emoji) and info-level variation selectors are kept, matching browser paste-safe. `--json`, `-q`, `--select`, `--once`, `--exit-on-find`, `--interval`, `--max-seconds`. Exit `0` clean, `1` findings, `2` usage, `3` clipboard unavailable. Reference: `docs/clipboard.md`. Python: `evaluate_clipboard_text`, `watch_clipboard`, `clean_clipboard_text` (injectable reader/writer for tests). -- Public sanitizer-restore robustness bench (`fuckmark-robustness-bench-v1`). `fuckmark robustness` replays 10 short fixtures times 18 Unicode sanitizer attacks (180 cells) against the live five-layer mix and frozen vectors. It does not rerun GPT-2 or SynthID; the Gate v2 confirmation scorecard stays the sealed detector track (identity 188/192, mix 0/192 after required sanitizers). Mixed letter/emoji fixtures do not restore; `digits` has no eligible site so mix is a no-op and every attack restores. `--json`, `-q`, `--fixture`, `--attack`. Exit `0` match, `1` mismatch, `2` usage. The CLI loads packaged copies under `fuckmark/robustness_data/` (byte-identical to `specs/`) and compares protocol, vectors, and scorecard-file SHA-256 to the freeze record before reporting success. Spec: `specs/fuckmark-robustness-bench-v1.{protocol.md,vectors.json,freeze.json}`. Reference: `docs/robustness.md`. +- Public sanitizer-restore robustness bench (`fuckmark-robustness-bench-v1`). `fuckmark robustness` replays 10 short fixtures times 18 Unicode sanitizer attacks (180 cells) against the live five-layer mix and frozen vectors. It does not rerun GPT-2 or SynthID; the Gate v2 confirmation scorecard stays the sealed detector track (identity 188/192, mix 0/192 after required sanitizers). Mixed letter/emoji fixtures do not restore; `digits` has no eligible site so mix is a no-op and every attack restores. `--json`, `-q`, `--fixture`, `--attack`. Exit `0` match, `1` mismatch, `2` usage. The CLI loads packaged copies under `fuckmark/robustness_data/` (byte-identical to `specs/` after LF newline folding) and compares protocol, vectors, and scorecard-file SHA-256 to the freeze record before reporting success. File hashes fold CRLF to LF so Windows checkouts match. Spec: `specs/fuckmark-robustness-bench-v1.{protocol.md,vectors.json,freeze.json}`. Reference: `docs/robustness.md`. - `fuckmark normalize` (`fuckmark-normalize-v1`): NFC, optional identifier lookalike fold (UTS #39-inspired subset, not a full confusable map), then strip of the security category set, plus a JSON receipt (`input_sha256` / `output_sha256` / `steps` / `report_hash`). `--clean` remains the blunt strip with no receipt. HTTP: `POST /api/normalize`. Python: `normalize_text`, `skeleton_fold`. Reference: `docs/normalize.md`. - VS Code / Cursor: `fuckmark.cleanOnSave` (default off) strips hidden Unicode on save. - Frozen confirmation files, hashes, mix mechanisms, and the published wheel are unchanged. Package version stays 0.4.1. diff --git a/docs/robustness.md b/docs/robustness.md index 8b111331..fd9086f5 100644 --- a/docs/robustness.md +++ b/docs/robustness.md @@ -12,7 +12,11 @@ Protocol: [`specs/fuckmark-robustness-bench-v1.protocol.md`](../specs/fuckmark-r Vectors (sources as codepoint arrays only): [`specs/fuckmark-robustness-bench-v1.vectors.json`](../specs/fuckmark-robustness-bench-v1.vectors.json). Freeze hashes: [`specs/fuckmark-robustness-bench-v1.freeze.json`](../specs/fuckmark-robustness-bench-v1.freeze.json). -Installed wheels load byte-identical copies from `fuckmark/robustness_data/`. Before exit 0 the CLI recomputes those hashes, including the scorecard *file* SHA-256, so editing the scorecard while leaving its embedded `scorecard_hash` field alone still fails. +Installed wheels load copies from `fuckmark/robustness_data/` that must match +`specs/` after UTF-8 LF newline folding. Before exit 0 the CLI recomputes those +hashes, including the scorecard *file* SHA-256, so editing the scorecard while +leaving its embedded `scorecard_hash` field alone still fails. CRLF checkouts +(Windows `core.autocrlf`) are folded to LF before hashing. ## Command line diff --git a/fuckmark/hashing.py b/fuckmark/hashing.py index 86a43a0b..0c5b1b79 100644 --- a/fuckmark/hashing.py +++ b/fuckmark/hashing.py @@ -36,6 +36,13 @@ def sha256_file(path: str | Path, chunk_size: int = 1024 * 1024) -> str: return digest.hexdigest() +def sha256_lf_file(path: str | Path) -> str: + if not isinstance(path, (str, Path)): + raise TypeError("path must be a string or Path") + data = Path(path).read_bytes().replace(b"\r\n", b"\n").replace(b"\r", b"\n") + return sha256_bytes(data) + + def sha256_json(value: Any) -> str: return sha256_bytes(canonical_json_bytes(value)) diff --git a/fuckmark/robustness.py b/fuckmark/robustness.py index 4aa95652..b7a42aca 100644 --- a/fuckmark/robustness.py +++ b/fuckmark/robustness.py @@ -11,7 +11,7 @@ from .cycle8.letter_mix import LETTER_MIX_MECHANISM_ID, apply_letter_alternating_mix from .cycle8.unicode_meta import is_default_ignorable_v1 -from .hashing import sha256_file, sha256_json, sha256_text +from .hashing import sha256_json, sha256_lf_file, sha256_text from .product.detect import detect_fuckmark_insertions from .product.scan import scan_hidden_characters from .product.visible_projection import project_visible_v1 @@ -275,7 +275,7 @@ def sealed_detector_track() -> dict[str, object]: return { "id": "cycle8-gate-v2-confirmation-scorecard-v1", "path": SEALED_DETECTOR_SCORECARD_PATH, - "file_sha256": sha256_file(path), + "file_sha256": sha256_lf_file(path), "scorecard_hash": payload["scorecard_hash"], "expected_scorecard_hash": SEALED_DETECTOR_SCORECARD_HASH, "identity_watermarked_detected": payload["identity_watermarked_detected"], @@ -358,15 +358,6 @@ def compare_freeze(freeze: dict[str, object], live: dict[str, object]) -> list[d if freeze.get(key) != live.get(key): mismatches.append({"id": f"freeze/{key}", "expected": freeze.get(key), "actual": live.get(key)}) return mismatches - expected = {item["id"]: item["expect"] for item in vectors["cells"]} - mismatches: list[dict[str, object]] = [] - for cell in cells: - key = f"{cell.fixture_id}/{cell.attack_id}" - want = expected.get(key) - got = cell_expect(cell) - if want != got: - mismatches.append({"id": key, "expected": want, "actual": got}) - return mismatches def summary_dict(cells: Sequence[RobustnessCell], mismatches: Sequence[dict[str, object]]) -> dict[str, object]: @@ -418,12 +409,12 @@ def freeze_bindings() -> dict[str, object]: vectors = load_vectors() return { "algorithm_version": ROBUSTNESS_ALGORITHM_VERSION, - "protocol_sha256": sha256_file(PROTOCOL_PATH), - "vectors_file_sha256": sha256_file(VECTORS_PATH), + "protocol_sha256": sha256_lf_file(PROTOCOL_PATH), + "vectors_file_sha256": sha256_lf_file(VECTORS_PATH), "vectors_canonical_sha256": sha256_json(vectors), "sealed_detector_scorecard_path": SEALED_DETECTOR_SCORECARD_PATH, "sealed_detector_scorecard_hash": SEALED_DETECTOR_SCORECARD_HASH, - "sealed_detector_scorecard_file_sha256": sha256_file(SCORECARD_FILE_PATH), + "sealed_detector_scorecard_file_sha256": sha256_lf_file(SCORECARD_FILE_PATH), } diff --git a/tests/test_hashing.py b/tests/test_hashing.py index ca75af04..6dea619e 100644 --- a/tests/test_hashing.py +++ b/tests/test_hashing.py @@ -1,6 +1,6 @@ from pathlib import Path -from fuckmark.hashing import sha256_bytes, sha256_file, sha256_text +from fuckmark.hashing import sha256_bytes, sha256_file, sha256_lf_file, sha256_text def test_byte_and_text_hash_match_for_utf8() -> None: @@ -8,11 +8,13 @@ def test_byte_and_text_hash_match_for_utf8() -> None: assert sha256_text(text) == sha256_bytes(text.encode("utf-8")) -def test_file_hash_matches_content_hash(tmp_path: Path) -> None: - path = tmp_path / "fixture.bin" - payload = b"abc\x00def" - path.write_bytes(payload) - assert sha256_file(path) == sha256_bytes(payload) +def test_lf_file_hash_folds_crlf(tmp_path: Path) -> None: + lf = tmp_path / "lf.txt" + crlf = tmp_path / "crlf.txt" + lf.write_bytes(b"a\nb\n") + crlf.write_bytes(b"a\r\nb\r\n") + assert sha256_lf_file(lf) == sha256_lf_file(crlf) == sha256_file(lf) + assert sha256_file(crlf) != sha256_file(lf) def test_file_hash_rejects_non_positive_chunk_size(tmp_path: Path) -> None: @@ -47,6 +49,8 @@ def test_hashing_rejects_wrong_input_types() -> None: sha256_bytes("abc") with pytest.raises(TypeError): sha256_text(b"abc") + with pytest.raises(TypeError): + sha256_lf_file(123) def test_file_hash_rejects_non_integer_chunk_size(tmp_path: Path) -> None: diff --git a/tests/test_robustness.py b/tests/test_robustness.py index d398c0df..43d56883 100644 --- a/tests/test_robustness.py +++ b/tests/test_robustness.py @@ -9,7 +9,7 @@ CYCLE8_GATE_V2_CONFIRMATION_SCORECARD_HASH, CYCLE8_GATE_V2_CONFIRMATION_SCORECARD_PATH, ) -from fuckmark.hashing import sha256_file, sha256_json +from fuckmark.hashing import sha256_json, sha256_lf_file from fuckmark.robustness import ( ATTACK_IDS, FREEZE_PATH, @@ -59,8 +59,8 @@ def test_robustness_freeze_binds_protocol_vectors_and_scorecard() -> None: live = freeze_bindings() assert freeze["algorithm_version"] == ROBUSTNESS_ALGORITHM_VERSION assert freeze["protocol_id"] == "fuckmark-robustness-bench-v1" - assert freeze["protocol_sha256"] == sha256_file(PROTOCOL_PATH) == live["protocol_sha256"] - assert freeze["vectors_file_sha256"] == sha256_file(VECTORS_PATH) == live["vectors_file_sha256"] + assert freeze["protocol_sha256"] == sha256_lf_file(PROTOCOL_PATH) == live["protocol_sha256"] + assert freeze["vectors_file_sha256"] == sha256_lf_file(VECTORS_PATH) == live["vectors_file_sha256"] assert freeze["vectors_canonical_sha256"] == sha256_json(payload) == live["vectors_canonical_sha256"] assert freeze["sealed_detector_scorecard_path"] == SEALED_DETECTOR_SCORECARD_PATH assert freeze["sealed_detector_scorecard_hash"] == SEALED_DETECTOR_SCORECARD_HASH @@ -81,10 +81,21 @@ def test_packaged_robustness_artifacts_match_specs() -> None: for public, packaged in pairs: assert packaged.is_file(), packaged assert packaged.parent == PACKAGE_DATA_DIR - assert packaged.read_bytes() == public.read_bytes(), packaged.name + assert sha256_lf_file(packaged) == sha256_lf_file(public), packaged.name assert PROTOCOL_PATH.resolve() != (ROOT / "specs" / "fuckmark-robustness-bench-v1.protocol.md").resolve() +def test_freeze_file_hashes_fold_crlf(tmp_path: Path) -> None: + from fuckmark.hashing import sha256_file + + freeze = load_freeze() + lf = PROTOCOL_PATH.read_bytes().replace(b"\r\n", b"\n").replace(b"\r", b"\n") + crlf_path = tmp_path / "protocol.md" + crlf_path.write_bytes(lf.replace(b"\n", b"\r\n")) + assert sha256_lf_file(crlf_path) == freeze["protocol_sha256"] + assert sha256_file(crlf_path) != freeze["protocol_sha256"] + + def test_sealed_scorecard_constants_match_gate_v2() -> None: assert SEALED_DETECTOR_SCORECARD_PATH == CYCLE8_GATE_V2_CONFIRMATION_SCORECARD_PATH assert SEALED_DETECTOR_SCORECARD_HASH == CYCLE8_GATE_V2_CONFIRMATION_SCORECARD_HASH