"""SBOM parser and CLI integration tests.

Covers:
  - SPDX 2.x JSON parsing (packages + files, checksum extraction)
  - CycloneDX JSON parsing (components + metadata component)
  - Format auto-detection
  - Edge cases: empty SBOMs, missing hashes, oversized files, invalid JSON
  - Hash deduplication
  - CLI commands: sbom attest, sbom verify, sbom summary
"""

from __future__ import annotations

import json
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
from click.testing import CliRunner

from certisigma_census.cli import main
from certisigma_census.sbom import (
    MAX_SBOM_FILE_BYTES,
    SBOMComponent,
    SBOMDocument,
    SBOMParseError,
    _extract_sha256,
    _is_valid_sha256,
    _parse_cyclonedx,
    _parse_spdx,
    deduplicate_hashes,
    detect_format,
    parse_sbom,
)


HASH_A = "a" * 64
HASH_B = "b" * 64
HASH_C = "c" * 64
HASH_UPPER = "A" * 64


def _spdx_doc(
    packages: list[dict] | None = None,
    files: list[dict] | None = None,
) -> dict:
    """Build a minimal SPDX 2.3 JSON document."""
    return {
        "spdxVersion": "SPDX-2.3",
        "name": "test-sbom",
        "SPDXID": "SPDXRef-DOCUMENT",
        "creationInfo": {
            "creators": ["Tool: test"],
            "created": "2026-01-01T00:00:00Z",
        },
        "packages": packages or [],
        "files": files or [],
    }


def _cyclonedx_doc(
    components: list[dict] | None = None,
    metadata_component: dict | None = None,
) -> dict:
    """Build a minimal CycloneDX 1.5 JSON document."""
    doc: dict = {
        "bomFormat": "CycloneDX",
        "specVersion": "1.5",
        "metadata": {
            "timestamp": "2026-01-01T00:00:00Z",
            "component": metadata_component or {"name": "test-app", "type": "application"},
        },
        "components": components or [],
    }
    return doc


def _spdx_pkg(name: str, version: str, sha256: str | None = None) -> dict:
    pkg: dict = {
        "name": name,
        "SPDXID": f"SPDXRef-Package-{name}",
        "versionInfo": version,
        "downloadLocation": "https://example.com",
        "checksums": [],
    }
    if sha256:
        pkg["checksums"].append({"algorithm": "SHA256", "checksumValue": sha256})
    return pkg


def _spdx_file(name: str, sha256: str | None = None) -> dict:
    entry: dict = {
        "fileName": name,
        "SPDXID": f"SPDXRef-File-{name.replace('/', '-')}",
        "checksums": [],
    }
    if sha256:
        entry["checksums"].append({"algorithm": "SHA256", "checksumValue": sha256})
    return entry


def _cdx_component(
    name: str,
    version: str = "1.0",
    sha256: str | None = None,
    purl: str | None = None,
    component_type: str = "library",
) -> dict:
    comp: dict = {"name": name, "version": version, "type": component_type}
    if sha256:
        comp["hashes"] = [{"alg": "SHA-256", "content": sha256}]
    if purl:
        comp["purl"] = purl
    return comp


class TestValidation:
    def test_valid_sha256(self):
        assert _is_valid_sha256(HASH_A)
        assert _is_valid_sha256(HASH_UPPER)
        assert _is_valid_sha256("ab" * 32)

    def test_invalid_sha256_too_short(self):
        assert not _is_valid_sha256("a" * 63)

    def test_invalid_sha256_too_long(self):
        assert not _is_valid_sha256("a" * 65)

    def test_invalid_sha256_non_hex(self):
        assert not _is_valid_sha256("g" * 64)

    def test_invalid_sha256_empty(self):
        assert not _is_valid_sha256("")


class TestExtractSHA256:
    def test_extracts_sha256(self):
        checksums = [
            {"algorithm": "MD5", "checksumValue": "d41d8cd9"},
            {"algorithm": "SHA256", "checksumValue": HASH_A},
        ]
        assert _extract_sha256(checksums, "algorithm", "checksumValue") == HASH_A.lower()

    def test_extracts_sha_hyphen(self):
        checksums = [{"alg": "SHA-256", "content": HASH_B}]
        assert _extract_sha256(checksums, "alg", "content") == HASH_B.lower()

    def test_normalises_to_lowercase(self):
        checksums = [{"algorithm": "SHA256", "checksumValue": HASH_UPPER}]
        result = _extract_sha256(checksums, "algorithm", "checksumValue")
        assert result == HASH_UPPER.lower()

    def test_returns_none_when_no_sha256(self):
        checksums = [{"algorithm": "MD5", "checksumValue": "d41d8cd9"}]
        assert _extract_sha256(checksums, "algorithm", "checksumValue") is None

    def test_returns_none_for_empty_list(self):
        assert _extract_sha256([], "algorithm", "checksumValue") is None

    def test_rejects_invalid_hex_value(self):
        checksums = [{"algorithm": "SHA256", "checksumValue": "not-a-hash"}]
        assert _extract_sha256(checksums, "algorithm", "checksumValue") is None


class TestDetectFormat:
    def test_detects_spdx(self):
        data = {"spdxVersion": "SPDX-2.3", "packages": []}
        assert detect_format(data) == "spdx"

    def test_detects_cyclonedx_bom_format(self):
        data = {"bomFormat": "CycloneDX", "specVersion": "1.5"}
        assert detect_format(data) == "cyclonedx"

    def test_detects_cyclonedx_components_key(self):
        data = {"specVersion": "1.5", "components": []}
        assert detect_format(data) == "cyclonedx"

    def test_returns_none_for_unknown(self):
        assert detect_format({"foo": "bar"}) is None

    def test_returns_none_for_empty(self):
        assert detect_format({}) is None


class TestParseSPDX:
    def test_parses_packages(self):
        data = _spdx_doc(packages=[
            _spdx_pkg("libfoo", "1.0", HASH_A),
            _spdx_pkg("libbar", "2.0", HASH_B),
        ])
        doc = _parse_spdx(data)
        assert doc.format == "spdx"
        assert doc.spec_version == "SPDX-2.3"
        assert doc.name == "test-sbom"
        assert len(doc.components) == 2
        assert doc.components[0].name == "libfoo"
        assert doc.components[0].hash_hex == HASH_A.lower()
        assert doc.components[0].component_type == "package"

    def test_parses_files(self):
        data = _spdx_doc(files=[
            _spdx_file("src/main.py", HASH_A),
        ])
        doc = _parse_spdx(data)
        assert len(doc.components) == 1
        assert doc.components[0].name == "src/main.py"
        assert doc.components[0].component_type == "file"

    def test_counts_skipped(self):
        data = _spdx_doc(packages=[
            _spdx_pkg("with-hash", "1.0", HASH_A),
            _spdx_pkg("no-hash", "1.0"),
        ])
        doc = _parse_spdx(data)
        assert len(doc.components) == 1
        assert doc.components_without_hash == 1

    def test_extracts_purl(self):
        pkg = _spdx_pkg("libfoo", "1.0", HASH_A)
        pkg["externalRefs"] = [
            {"referenceType": "purl", "referenceLocator": "pkg:pypi/libfoo@1.0"},
        ]
        data = _spdx_doc(packages=[pkg])
        doc = _parse_spdx(data)
        assert doc.components[0].purl == "pkg:pypi/libfoo@1.0"

    def test_extracts_supplier(self):
        pkg = _spdx_pkg("libfoo", "1.0", HASH_A)
        pkg["supplier"] = "Organization: Acme Inc."
        data = _spdx_doc(packages=[pkg])
        doc = _parse_spdx(data)
        assert doc.components[0].supplier == "Organization: Acme Inc."

    def test_empty_spdx(self):
        data = _spdx_doc()
        doc = _parse_spdx(data)
        assert len(doc.components) == 0
        assert doc.components_without_hash == 0

    def test_creation_info(self):
        data = _spdx_doc()
        doc = _parse_spdx(data)
        assert doc.creation_info.get("created") == "2026-01-01T00:00:00Z"
        assert "Tool: test" in doc.creation_info.get("creators", [])

    def test_non_dict_package_skipped(self):
        data = _spdx_doc(packages=["not-a-dict", _spdx_pkg("ok", "1.0", HASH_A)])
        doc = _parse_spdx(data)
        assert len(doc.components) == 1

    def test_mixed_checksum_algorithms(self):
        pkg = _spdx_pkg("mixed", "1.0")
        pkg["checksums"] = [
            {"algorithm": "SHA1", "checksumValue": "a" * 40},
            {"algorithm": "SHA256", "checksumValue": HASH_C},
        ]
        data = _spdx_doc(packages=[pkg])
        doc = _parse_spdx(data)
        assert len(doc.components) == 1
        assert doc.components[0].hash_hex == HASH_C.lower()


class TestParseCycloneDX:
    def test_parses_components(self):
        data = _cyclonedx_doc(components=[
            _cdx_component("libfoo", "1.0", HASH_A, purl="pkg:pypi/libfoo@1.0"),
            _cdx_component("libbar", "2.0", HASH_B),
        ])
        doc = _parse_cyclonedx(data)
        assert doc.format == "cyclonedx"
        assert doc.spec_version == "1.5"
        assert len(doc.components) == 2
        assert doc.components[0].purl == "pkg:pypi/libfoo@1.0"

    def test_metadata_component_with_hash(self):
        mc = {"name": "root-app", "type": "application", "hashes": [{"alg": "SHA-256", "content": HASH_C}]}
        data = _cyclonedx_doc(metadata_component=mc)
        doc = _parse_cyclonedx(data)
        assert any(c.name == "root-app" for c in doc.components)

    def test_counts_skipped(self):
        data = _cyclonedx_doc(components=[
            _cdx_component("with-hash", "1.0", HASH_A),
            _cdx_component("no-hash", "1.0"),
        ])
        doc = _parse_cyclonedx(data)
        assert len(doc.components) == 1
        assert doc.components_without_hash == 1

    def test_supplier_dict(self):
        comp = _cdx_component("lib", "1.0", HASH_A)
        comp["supplier"] = {"name": "Acme Inc."}
        data = _cyclonedx_doc(components=[comp])
        doc = _parse_cyclonedx(data)
        assert doc.components[0].supplier == "Acme Inc."

    def test_supplier_string(self):
        comp = _cdx_component("lib", "1.0", HASH_A)
        comp["supplier"] = "Acme"
        data = _cyclonedx_doc(components=[comp])
        doc = _parse_cyclonedx(data)
        assert doc.components[0].supplier == "Acme"

    def test_empty_cyclonedx(self):
        data = _cyclonedx_doc()
        doc = _parse_cyclonedx(data)
        assert len(doc.components) == 0

    def test_timestamp_in_creation_info(self):
        data = _cyclonedx_doc()
        doc = _parse_cyclonedx(data)
        assert doc.creation_info.get("timestamp") == "2026-01-01T00:00:00Z"


class TestParseSBOM:
    def test_spdx_round_trip(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))
        doc = parse_sbom(f)
        assert doc.format == "spdx"
        assert len(doc.components) == 1

    def test_cyclonedx_round_trip(self, tmp_path):
        data = _cyclonedx_doc(components=[_cdx_component("bar", "1.0", HASH_B)])
        f = tmp_path / "sbom.cdx.json"
        f.write_text(json.dumps(data))
        doc = parse_sbom(f)
        assert doc.format == "cyclonedx"
        assert len(doc.components) == 1

    def test_forced_format(self, tmp_path):
        data = _cyclonedx_doc(components=[_cdx_component("x", "1.0", HASH_A)])
        f = tmp_path / "sbom.json"
        f.write_text(json.dumps(data))
        doc = parse_sbom(f, forced_format="cyclonedx")
        assert doc.format == "cyclonedx"

    def test_unknown_format_raises(self, tmp_path):
        f = tmp_path / "sbom.json"
        f.write_text(json.dumps({"random": "data"}))
        with pytest.raises(SBOMParseError, match="Cannot auto-detect"):
            parse_sbom(f)

    def test_invalid_json_raises(self, tmp_path):
        f = tmp_path / "bad.json"
        f.write_text("not json at all")
        with pytest.raises(SBOMParseError, match="Invalid JSON"):
            parse_sbom(f)

    def test_non_object_root_raises(self, tmp_path):
        f = tmp_path / "array.json"
        f.write_text(json.dumps([1, 2, 3]))
        with pytest.raises(SBOMParseError, match="root must be a JSON object"):
            parse_sbom(f)

    def test_oversized_file_raises(self, tmp_path):
        f = tmp_path / "huge.json"
        f.write_bytes(b"x" * 1024)
        with patch("certisigma_census.sbom.MAX_SBOM_FILE_BYTES", 512):
            with pytest.raises(ValueError, match="too large"):
                parse_sbom(f)

    def test_unreadable_file_raises(self, tmp_path):
        f = tmp_path / "nope.json"
        with pytest.raises(SBOMParseError, match="Cannot read"):
            parse_sbom(f)


class TestDeduplicateHashes:
    def test_deduplication(self):
        comp1 = SBOMComponent(name="a", version="1.0", hash_hex=HASH_A.lower())
        comp2 = SBOMComponent(name="b", version="2.0", hash_hex=HASH_A.lower())
        comp3 = SBOMComponent(name="c", version="1.0", hash_hex=HASH_B.lower())
        doc = SBOMDocument(
            format="spdx", spec_version="2.3", name="test",
            components=[comp1, comp2, comp3],
        )
        hashes, hash_map = deduplicate_hashes(doc)
        assert len(hashes) == 2
        assert len(hash_map[HASH_A.lower()]) == 2
        assert len(hash_map[HASH_B.lower()]) == 1

    def test_empty_doc(self):
        doc = SBOMDocument(format="spdx", spec_version="2.3", name="empty")
        hashes, hash_map = deduplicate_hashes(doc)
        assert hashes == []
        assert hash_map == {}


def _mock_batch_attest_response(hashes, source=None):
    attestations = [
        {"id": f"att_{i}", "hash_hex": h, "timestamp": "2026-01-01T00:00:00Z", "status": "created"}
        for i, h in enumerate(hashes)
    ]
    return MagicMock(batch_id="batch_sbom", count=len(hashes),
                     created=len(hashes), existing=0, attestations=attestations)


def _mock_batch_verify_response(hashes, all_exist=True, **kwargs):
    results = [
        {"hash_hex": h, "exists": all_exist, "id": f"att_{h[:8]}", "level": "T1",
         "source": "sbom", "timestamp": "2026-01-01T00:00:00Z"}
        for h in hashes
    ]
    return MagicMock(count=len(hashes), found=len(hashes) if all_exist else 0,
                     not_found=0 if all_exist else len(hashes), results=results)


class TestSBOMAttestCLI:
    def test_attest_spdx(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        mock_client = MagicMock()
        mock_client.batch_attest.side_effect = _mock_batch_attest_response

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, ["sbom", "attest", str(f), "--json"])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["attested"] == 1
        assert out["format"] == "spdx"

    def test_attest_cyclonedx(self, tmp_path):
        data = _cyclonedx_doc(components=[_cdx_component("bar", "2.0", HASH_B)])
        f = tmp_path / "sbom.cdx.json"
        f.write_text(json.dumps(data))

        mock_client = MagicMock()
        mock_client.batch_attest.side_effect = _mock_batch_attest_response

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, ["sbom", "attest", str(f), "--json"])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["format"] == "cyclonedx"

    def test_attest_dry_run(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "attest", str(f), "--dry-run", "--json"])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["dry_run"] is True
        assert out["unique_hashes"] == 1

    def test_attest_no_hashes(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("no-hash", "1.0")])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "attest", str(f)])

        assert result.exit_code == 0

    def test_attest_with_manifest(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))
        mpath = tmp_path / "manifest.db"

        mock_client = MagicMock()
        mock_client.batch_attest.side_effect = _mock_batch_attest_response

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, [
                "sbom", "attest", str(f), "--manifest", str(mpath), "--json",
            ])

        assert result.exit_code == 0

    def test_attest_parse_error(self, tmp_path):
        f = tmp_path / "bad.json"
        f.write_text("not json")
        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "attest", str(f)])
        assert result.exit_code != 0

    def test_attest_forced_format(self, tmp_path):
        data = _cyclonedx_doc(components=[_cdx_component("x", "1.0", HASH_A)])
        f = tmp_path / "sbom.json"
        f.write_text(json.dumps(data))

        mock_client = MagicMock()
        mock_client.batch_attest.side_effect = _mock_batch_attest_response

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, [
                "sbom", "attest", str(f), "--format", "cyclonedx", "--json",
            ])

        assert result.exit_code == 0


class TestSBOMVerifyCLI:
    def test_verify_all_found(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        mock_client = MagicMock()
        mock_client.batch_verify.side_effect = lambda h, **kw: _mock_batch_verify_response(h, all_exist=True)

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, ["sbom", "verify", str(f), "--json"])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["found"] == 1
        assert out["not_found"] == 0

    def test_verify_some_missing(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        mock_client = MagicMock()
        mock_client.batch_verify.side_effect = lambda h, **kw: _mock_batch_verify_response(h, all_exist=False)

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, ["sbom", "verify", str(f), "--json"])

        assert result.exit_code == 1
        out = json.loads(result.output)
        assert out["not_found"] == 1

    def test_verify_exit_zero(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        mock_client = MagicMock()
        mock_client.batch_verify.side_effect = lambda h, **kw: _mock_batch_verify_response(h, all_exist=False)

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, ["sbom", "verify", str(f), "--exit-zero", "--json"])

        assert result.exit_code == 0

    def test_verify_no_hashes(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("no-hash", "1.0")])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "verify", str(f)])
        assert result.exit_code == 0

    def test_verify_detailed(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        mock_client = MagicMock()
        mock_client.batch_verify.side_effect = lambda h, **kw: _mock_batch_verify_response(h, all_exist=True)

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, [
                "sbom", "verify", str(f), "--detailed", "--json",
            ])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["found_details"][0].get("level") == "T1"


class TestSBOMSummaryCLI:
    def test_summary_spdx(self, tmp_path):
        data = _spdx_doc(packages=[
            _spdx_pkg("foo", "1.0", HASH_A),
            _spdx_pkg("bar", "2.0", HASH_B),
            _spdx_pkg("no-hash", "1.0"),
        ])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "summary", str(f), "--json"])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["total_components_with_hash"] == 2
        assert out["total_components_without_hash"] == 1
        assert out["unique_hashes"] == 2
        assert out["sha256_coverage_percent"] == pytest.approx(66.7, abs=0.1)

    def test_summary_text_output(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "summary", str(f)])
        assert result.exit_code == 0
        assert "SPDX" in result.output
        assert "100.0%" in result.output

    def test_summary_parse_error(self, tmp_path):
        f = tmp_path / "bad.json"
        f.write_text("nope")
        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "summary", str(f)])
        assert result.exit_code != 0

    def test_summary_cyclonedx(self, tmp_path):
        data = _cyclonedx_doc(components=[
            _cdx_component("lib", "1.0", HASH_A, component_type="library"),
            _cdx_component("fw", "2.0", HASH_B, component_type="framework"),
        ])
        f = tmp_path / "sbom.cdx.json"
        f.write_text(json.dumps(data))

        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "summary", str(f), "--json"])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert out["component_types"]["library"] == 1
        assert out["component_types"]["framework"] == 1

    def test_summary_json_has_meta(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        runner = CliRunner()
        result = runner.invoke(main, ["sbom", "summary", str(f), "--json"])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert "census_version" in out
        assert "elapsed_seconds" in out


class TestSBOMVerifyErrors:
    def test_verify_json_has_errors_field(self, tmp_path):
        data = _spdx_doc(packages=[_spdx_pkg("foo", "1.0", HASH_A)])
        f = tmp_path / "sbom.spdx.json"
        f.write_text(json.dumps(data))

        mock_client = MagicMock()
        mock_client.batch_verify.side_effect = lambda h, **kw: _mock_batch_verify_response(h, all_exist=True)

        runner = CliRunner()
        with patch("certisigma_census.cli._get_client", return_value=mock_client):
            result = runner.invoke(main, ["sbom", "verify", str(f), "--json"])

        assert result.exit_code == 0
        out = json.loads(result.output)
        assert "errors" in out
        assert out["errors"] == 0


class TestSanitizeDisplay:
    def test_strips_control_chars(self):
        from certisigma_census.cli import _sanitize_display
        assert _sanitize_display("hello\x1b[31mworld\x00") == "hello[31mworld"
        assert "\x1b" not in _sanitize_display("test\x1binjection")
        assert "\x00" not in _sanitize_display("null\x00byte")

    def test_truncates_long_strings(self):
        from certisigma_census.cli import _sanitize_display
        result = _sanitize_display("a" * 300, max_len=100)
        assert len(result) == 103
        assert result.endswith("...")

    def test_passes_clean_strings(self):
        from certisigma_census.cli import _sanitize_display
        assert _sanitize_display("clean-name-1.0") == "clean-name-1.0"

    def test_preserves_unicode(self):
        from certisigma_census.cli import _sanitize_display
        assert _sanitize_display("libfoo-日本語") == "libfoo-日本語"
