{"uuid": "a8f37d93-0a25-4503-9004-07194653c8a9", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-65920", "type": "seen", "source": "https://gist.github.com/rafael-arkhe/82faf9714c89f3fea0f0fc7f08a665d6", "content": "# arkhe-ai-verify \u2014 Reposit\u00f3rio Completo (Vers\u00e3o Consolidada)\n\nEste documento consolida o reposit\u00f3rio `arkhe-ai-verify` com a vers\u00e3o mais recente das depend\u00eancias, corre\u00e7\u00f5es de seguran\u00e7a contra vulnerabilidades conhecidas (CVE-2026-65920, CVE-2026-7482, PR #3364), integra\u00e7\u00e3o Sigstore real e disciplina fail-closed.\n\n---\n\n## 1. Vis\u00e3o Geral\n\nO `arkhe-ai-verify` \u00e9 o crate de verifica\u00e7\u00e3o de artefatos de IA do Arkhe OS. Implementa um pipeline de 4 gates fail-closed para formatos safetensors, GGUF, ONNX, TensorRT, TFLite, CoreML e PyTorch, com valida\u00e7\u00f5es de seguran\u00e7a espec\u00edficas e integra\u00e7\u00e3o Sigstore (bundles v0.3).\n\n### Corre\u00e7\u00f5es de Seguran\u00e7a\n\n| Vulnerabilidade | Descri\u00e7\u00e3o | Corre\u00e7\u00e3o |\n|:---|:---|:---|\n| CVE-2026-65920 | Path traversal em `weight_map` | Sanitiza\u00e7\u00e3o de paths com canonicaliza\u00e7\u00e3o |\n| PR #3364 | `data_offsets` inconsistentes | Valida\u00e7\u00e3o de count, ordem e consist\u00eancia com shape/dtype |\n| CVE-2026-7482 | GGUF OOB read (Bleeding Llama) | Valida\u00e7\u00e3o de alignment, n_dims, gguf_type, blck_size |\n\n---\n\n## 2. Estrutura do Reposit\u00f3rio\n\n```\narkhe-ai-verify/\n\u251c\u2500\u2500 Cargo.toml\n\u251c\u2500\u2500 rust-toolchain.toml\n\u251c\u2500\u2500 deny.toml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 .github/\n\u2502   \u2514\u2500\u2500 workflows/\n\u2502       \u2514\u2500\u2500 ci.yml\n\u251c\u2500\u2500 src/\n\u2502   \u251c\u2500\u2500 lib.rs\n\u2502   \u251c\u2500\u2500 error.rs\n\u2502   \u251c\u2500\u2500 gates.rs\n\u2502   \u251c\u2500\u2500 formats.rs\n\u2502   \u2514\u2500\u2500 security.rs\n\u2514\u2500\u2500 tests/\n    \u251c\u2500\u2500 safetensors_tests.rs\n    \u251c\u2500\u2500 gguf_tests.rs\n    \u2514\u2500\u2500 security_tests.rs\n```\n\n---\n\n## 3. Configura\u00e7\u00e3o\n\n### `Cargo.toml`\n\n```toml\n[package]\nname = \"arkhe-ai-verify\"\nversion = \"0.1.0\"\nedition = \"2021\"\nrust-version = \"1.82\"\nauthors = [\"Arkhe Contributors \"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://gist.github.com/rafael-arkhe\"\ndescription = \"Verifica\u00e7\u00e3o de artefatos de IA (safetensors, GGUF, ONNX, ...)\"\n\n[dependencies]\n# Core\nserde = { version = \"1.0.229\", features = [\"derive\"] }\nserde_json = \"1.0.151\"\nthiserror = \"2.0.21\"\ntracing = \"0.1.44\"\n\n# Hashing\nblake3 = \"1.8.6\"\nhex = \"0.4\"\nsha2 = \"0.10.9\"\n\n# Async\ntokio = { version = \"1.52.3\", features = [\"macros\", \"rt-multi-thread\", \"fs\", \"io-util\"] }\nasync-trait = \"0.1\"\n\n# Sigstore\nsigstore-verify = \"0.11.0\"\nsigstore-trust-root = \"0.11.0\"\nsigstore-types = \"0.11.0\"\n\n# Utilit\u00e1rios\ndirs = \"6.0\"\n\n[dev-dependencies]\ntempfile = \"3.24\"\nproptest = \"1.5\"\ntokio-test = \"0.4\"\n\n[lints.rust]\nunsafe_code = \"forbid\"\nmissing_docs = \"warn\"\n\n[lints.clippy]\nall = \"warn\"\npedantic = \"warn\"\nunwrap_used = \"deny\"\nexpect_used = \"warn\"\npanic = \"deny\"\ntodo = \"deny\"\nunimplemented = \"deny\"\n```\n\n### `rust-toolchain.toml`\n\n```toml\n[toolchain]\nchannel = \"1.82.0\"\ncomponents = [\"rustfmt\", \"clippy\", \"rust-src\"]\nprofile = \"minimal\"\n```\n\n### `deny.toml`\n\n```toml\n[advisories]\nvulnerability = \"deny\"\nunmaintained = \"warn\"\nyanked = \"deny\"\n\n[licenses]\nallow = [\"MIT\", \"Apache-2.0\", \"BSD-3-Clause\", \"BSD-2-Clause\", \"ISC\", \"Unicode-DFS-2016\"]\ncopyleft = \"deny\"\nconfidence-threshold = 0.9\n\n[bans]\nmultiple-versions = \"warn\"\nwildcards = \"deny\"\ndeny = [\n    { name = \"openssl\" },\n    { name = \"openssl-sys\" },\n]\n\n[sources]\nunknown-registry = \"deny\"\nunknown-git = \"deny\"\nallow-registry = [\"https://github.com/rust-lang/crates.io-index\"]\n```\n\n---\n\n## 4. C\u00f3digo Fonte\n\n### `src/error.rs`\n\n```rust\n//! # error \u2014 Tipos de erro fail-closed.\n\nuse thiserror::Error;\n\n/// Erro de verifica\u00e7\u00e3o.\n#[derive(Debug, Error)]\npub enum VerifyError {\n    /// Formato n\u00e3o suportado.\n    #[error(\"formato n\u00e3o suportado: {0}\")]\n    UnsupportedFormat(String),\n\n    /// Limite excedido.\n    #[error(\"artefato excede limite: {field} = {value} &gt; {max}\")]\n    LimitExceeded {\n        /// Campo.\n        field: String,\n        /// Valor observado.\n        value: u64,\n        /// M\u00e1ximo.\n        max: u64,\n    },\n\n    /// Hash mismatch.\n    #[error(\"hash mismatch: esperado {expected}, obtido {actual}\")]\n    HashMismatch {\n        /// Esperado.\n        expected: String,\n        /// Obtido.\n        actual: String,\n    },\n\n    /// Assinatura inv\u00e1lida.\n    #[error(\"assinatura inv\u00e1lida: {0}\")]\n    InvalidSignature(String),\n\n    /// Prova de inclus\u00e3o inv\u00e1lida.\n    #[error(\"prova de inclus\u00e3o inv\u00e1lida: {0}\")]\n    InvalidInclusion(String),\n\n    /// Valida\u00e7\u00e3o de seguran\u00e7a falhou.\n    #[error(\"seguran\u00e7a: {0}\")]\n    Security(String),\n\n    /// I/O.\n    #[error(\"I/O: {0}\")]\n    Io(#[from] std::io::Error),\n\n    /// Parse.\n    #[error(\"parse: {0}\")]\n    Parse(String),\n}\n```\n\n### `src/security.rs`\n\n```rust\n//! # security \u2014 Valida\u00e7\u00f5es contra vulnerabilidades conhecidas.\n//!\n//! ## CVEs mitigados\n//!\n//! - CVE-2026-65920 (path traversal em weight_map)\n//! - CVE-2026-7482 / Bleeding Llama (GGUF OOB read)\n//! - PR #3364 (safetensors data_offsets inconsistentes)\n//!\n//! ## Invariantes\n//!\n//! - `INV-AI-V-06`: data_offsets consistentes com shape e dtype\n//! - `INV-AI-V-07`: alignment GGUF \u2208 [4, 1 MiB] e pot\u00eancia de 2\n//! - `INV-AI-V-08`: n_dims \u2264 4\n//! - `INV-AI-V-09`: gguf_type \u2208 [0, 12]\n//! - `INV-AI-V-10`: blck_size \u2260 0\n\nuse crate::error::VerifyError;\n\n/// Tamanho m\u00e1ximo do header safetensors (100 MB).\npub const MAX_SAFETENSORS_HEADER: usize = 100 * 1024 * 1024;\n\n/// Tamanho m\u00e1ximo de uma string GGUF (64 MB).\npub const MAX_GGUF_STRING: u64 = 64 * 1024 * 1024;\n\n/// M\u00e1ximo de elementos num array GGUF.\npub const MAX_GGUF_ARRAY: u64 = 1024 * 1024;\n\n/// Alignment m\u00e1ximo GGUF (1 MiB).\npub const MAX_GGUF_ALIGNMENT: u64 = 1024 * 1024;\n\n/// Alignment m\u00ednimo GGUF.\npub const MIN_GGUF_ALIGNMENT: u64 = 4;\n\n/// M\u00e1ximo de dimens\u00f5es GGUF (GGML_MAX_DIMS).\npub const MAX_GGUF_DIMS: u32 = 4;\n\n/// Tipos de dados safetensors suportados.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum SafeTensorDtype {\n    F16, BF16, F32, F64,\n    I8, I16, I32, I64,\n    U8, U16, U32, U64,\n    BOOL,\n}\n\nimpl SafeTensorDtype {\n    /// Tamanho em bytes.\n    #[must_use]\n    pub fn size_bytes(self) -&gt; usize {\n        match self {\n            Self::F16 | Self::BF16 | Self::I16 | Self::U16 =&gt; 2,\n            Self::F32 | Self::I32 | Self::U32 =&gt; 4,\n            Self::F64 | Self::I64 | Self::U64 =&gt; 8,\n            Self::I8 | Self::U8 | Self::BOOL =&gt; 1,\n        }\n    }\n\n    /// Parse de string.\n    pub fn parse(s: &amp;str) -&gt; Result {\n        match s {\n            \"F16\" =&gt; Ok(Self::F16),\n            \"BF16\" =&gt; Ok(Self::BF16),\n            \"F32\" =&gt; Ok(Self::F32),\n            \"F64\" =&gt; Ok(Self::F64),\n            \"I8\" =&gt; Ok(Self::I8),\n            \"I16\" =&gt; Ok(Self::I16),\n            \"I32\" =&gt; Ok(Self::I32),\n            \"I64\" =&gt; Ok(Self::I64),\n            \"U8\" =&gt; Ok(Self::U8),\n            \"U16\" =&gt; Ok(Self::U16),\n            \"U32\" =&gt; Ok(Self::U32),\n            \"U64\" =&gt; Ok(Self::U64),\n            \"BOOL\" =&gt; Ok(Self::BOOL),\n            _ =&gt; Err(VerifyError::Parse(format!(\"dtype desconhecido: {s}\"))),\n        }\n    }\n}\n\n/// Valida `data_offsets` de um tensor safetensors (`INV-AI-V-06`).\npub fn validate_safetensors_offsets(\n    shape: &amp;[usize],\n    dtype: SafeTensorDtype,\n    data_offsets: &amp;[usize; 2],\n    data_buffer_len: usize,\n) -&gt; Result&lt;(), VerifyError&gt; {\n    let [start, end] = data_offsets;\n    if start &gt; end {\n        return Err(VerifyError::Security(format!(\n            \"data_offsets inv\u00e1lidos: start={start} &gt; end={end}\"\n        )));\n    }\n    if *end &gt; data_buffer_len {\n        return Err(VerifyError::Security(format!(\n            \"data_offsets excedem buffer: end={end} &gt; len={data_buffer_len}\"\n        )));\n    }\n\n    let expected_elems: usize = shape.iter().product();\n    let expected_bytes = expected_elems.saturating_mul(dtype.size_bytes());\n    let actual_bytes = end - start;\n    if expected_bytes != actual_bytes {\n        return Err(VerifyError::Security(format!(\n            \"data_offsets inconsistentes: esperado {expected_bytes} bytes, obtido {actual_bytes}\"\n        )));\n    }\n    Ok(())\n}\n\n/// Sanitiza paths do `weight_map` (CVE-2026-65920).\npub fn sanitize_weight_map_path(\n    base_dir: &amp;std::path::Path,\n    relative: &amp;str,\n) -&gt; Result {\n    if relative.contains(\"..\") || relative.starts_with('/') {\n        return Err(VerifyError::Security(format!(\n            \"path traversal detectado: {relative}\"\n        )));\n    }\n\n    let candidate = base_dir.join(relative);\n    let canonical_base = base_dir\n        .canonicalize()\n        .map_err(|e| VerifyError::Security(format!(\"base dir inv\u00e1lido: {e}\")))?;\n\n    if let Some(parent) = candidate.parent() {\n        if let Ok(canonical_parent) = parent.canonicalize() {\n            if !canonical_parent.starts_with(&amp;canonical_base) {\n                return Err(VerifyError::Security(format!(\n                    \"path escapa ao diret\u00f3rio base: {relative}\"\n                )));\n            }\n        }\n    }\n\n    Ok(candidate)\n}\n\n/// Valida alignment GGUF (`INV-AI-V-07`).\npub fn validate_gguf_alignment(alignment: u64) -&gt; Result&lt;(), VerifyError&gt; {\n    if alignment &lt; MIN_GGUF_ALIGNMENT || alignment &gt; MAX_GGUF_ALIGNMENT {\n        return Err(VerifyError::Security(format!(\n            \"alignment GGUF fora de limites: {alignment}\"\n        )));\n    }\n    if !alignment.is_power_of_two() {\n        return Err(VerifyError::Security(format!(\n            \"alignment GGUF n\u00e3o \u00e9 pot\u00eancia de 2: {alignment}\"\n        )));\n    }\n    Ok(())\n}\n\n/// Valida `n_dims` GGUF (`INV-AI-V-08`).\npub fn validate_gguf_n_dims(n_dims: u32) -&gt; Result&lt;(), VerifyError&gt; {\n    if n_dims &gt; MAX_GGUF_DIMS {\n        return Err(VerifyError::Security(format!(\n            \"n_dims GGUF excede m\u00e1ximo: {n_dims} &gt; {MAX_GGUF_DIMS}\"\n        )));\n    }\n    Ok(())\n}\n\n/// Valida `gguf_type` dentro do enum (`INV-AI-V-09`).\npub fn validate_gguf_type(t: i32) -&gt; Result&lt;(), VerifyError&gt; {\n    const KNOWN: std::ops::RangeInclusive = 0..=12;\n    if !KNOWN.contains(&amp;t) {\n        return Err(VerifyError::Security(format!(\n            \"gguf_type desconhecido: {t} (esperado 0..=12)\"\n        )));\n    }\n    Ok(())\n}\n\n/// Valida `blck_size` n\u00e3o-zero (`INV-AI-V-10`).\npub fn validate_gguf_block_size(blck_size: u64) -&gt; Result&lt;(), VerifyError&gt; {\n    if blck_size == 0 {\n        return Err(VerifyError::Security(\n            \"blck_size GGUF \u00e9 zero (divis\u00e3o por zero)\".into(),\n        ));\n    }\n    Ok(())\n}\n\n/// Valida string GGUF.\npub fn validate_gguf_string_len(len: u64) -&gt; Result&lt;(), VerifyError&gt; {\n    if len &gt; MAX_GGUF_STRING {\n        return Err(VerifyError::LimitExceeded {\n            field: \"gguf.string_len\".into(),\n            value: len,\n            max: MAX_GGUF_STRING,\n        });\n    }\n    Ok(())\n}\n\n/// Valida array GGUF.\npub fn validate_gguf_array_len(len: u64) -&gt; Result&lt;(), VerifyError&gt; {\n    if len &gt; MAX_GGUF_ARRAY {\n        return Err(VerifyError::LimitExceeded {\n            field: \"gguf.array_len\".into(),\n            value: len,\n            max: MAX_GGUF_ARRAY,\n        });\n    }\n    Ok(())\n}\n```\n\n### `src/formats.rs`\n\n```rust\n//! # formats \u2014 Parsers para cada formato.\n\nuse crate::error::VerifyError;\nuse crate::security;\n\npub mod safetensors {\n    //! Parser safetensors com valida\u00e7\u00e3o de offsets.\n\n    use super::{security, VerifyError};\n    use serde::Deserialize;\n    use std::collections::HashMap;\n    use std::io::Read;\n\n    #[derive(Debug, Deserialize)]\n    pub struct TensorInfo {\n        pub dtype: String,\n        pub shape: Vec,\n        pub data_offsets: [usize; 2],\n    }\n\n    #[derive(Debug, Deserialize)]\n    pub struct Header {\n        #[serde(flatten)]\n        pub tensors: HashMap,\n        #[serde(rename = \"__metadata__\")]\n        pub metadata: Option&gt;,\n    }\n\n    pub fn parse(path: &amp;std::path::Path) -&gt; Result {\n        let mut file = std::fs::File::open(path)?;\n\n        let mut len_bytes = [0u8; 8];\n        file.read_exact(&amp;mut len_bytes)?;\n        let header_len = u64::from_le_bytes(len_bytes) as usize;\n\n        if header_len &gt; security::MAX_SAFETENSORS_HEADER {\n            return Err(VerifyError::LimitExceeded {\n                field: \"safetensors.header_len\".into(),\n                value: header_len as u64,\n                max: security::MAX_SAFETENSORS_HEADER as u64,\n            });\n        }\n\n        let mut buf = vec![0u8; header_len];\n        file.read_exact(&amp;mut buf)?;\n\n        if buf.first() != Some(&amp;b'{') {\n            return Err(VerifyError::Parse(\n                \"safetensors header n\u00e3o come\u00e7a com '{'\".into(),\n            ));\n        }\n\n        let header: Header = serde_json::from_slice(&amp;buf)\n            .map_err(|e| VerifyError::Parse(format!(\"safetensors header: {e}\")))?;\n\n        let meta = std::fs::metadata(path)?;\n        let data_buffer_len = meta.len() as usize - 8 - header_len;\n\n        for (name, info) in &amp;header.tensors {\n            let dtype = security::SafeTensorDtype::parse(&amp;info.dtype)\n                .map_err(|e| VerifyError::Parse(format!(\"tensor {name}: {e}\")))?;\n            security::validate_safetensors_offsets(\n                &amp;info.shape,\n                dtype,\n                &amp;info.data_offsets,\n                data_buffer_len,\n            )\n            .map_err(|e| VerifyError::Security(format!(\"tensor {name}: {e}\")))?;\n        }\n\n        Ok(header)\n    }\n}\n\npub mod gguf {\n    //! Parser GGUF com valida\u00e7\u00f5es de seguran\u00e7a.\n\n    use super::{security, VerifyError};\n    use std::io::Read;\n\n    pub const MAGIC: &amp;[u8; 4] = b\"GGUF\";\n    pub const VERSION: u32 = 3;\n\n    #[derive(Debug, Clone, Copy)]\n    pub struct Header {\n        pub version: u32,\n        pub tensor_count: u64,\n        pub metadata_kv_count: u64,\n    }\n\n    pub fn parse_header(path: &amp;std::path::Path) -&gt; Result {\n        let mut file = std::fs::File::open(path)?;\n\n        let mut magic = [0u8; 4];\n        file.read_exact(&amp;mut magic)?;\n        if &amp;magic != MAGIC {\n            return Err(VerifyError::Parse(\"GGUF magic inv\u00e1lido\".into()));\n        }\n\n        let mut buf4 = [0u8; 4];\n        file.read_exact(&amp;mut buf4)?;\n        let version = u32::from_le_bytes(buf4);\n        if version != VERSION {\n            return Err(VerifyError::Parse(format!(\n                \"GGUF vers\u00e3o n\u00e3o suportada: {version}\"\n            )));\n        }\n\n        let mut buf8 = [0u8; 8];\n        file.read_exact(&amp;mut buf8)?;\n        let tensor_count = u64::from_le_bytes(buf8);\n        file.read_exact(&amp;mut buf8)?;\n        let metadata_kv_count = u64::from_le_bytes(buf8);\n\n        if tensor_count &gt; 1_000_000 {\n            return Err(VerifyError::LimitExceeded {\n                field: \"gguf.tensor_count\".into(),\n                value: tensor_count,\n                max: 1_000_000,\n            });\n        }\n        if metadata_kv_count &gt; 1_000_000 {\n            return Err(VerifyError::LimitExceeded {\n                field: \"gguf.metadata_kv_count\".into(),\n                value: metadata_kv_count,\n                max: 1_000_000,\n            });\n        }\n\n        Ok(Header {\n            version,\n            tensor_count,\n            metadata_kv_count,\n        })\n    }\n\n    pub fn read_kv_key(file: &amp;mut std::fs::File) -&gt; Result {\n        let mut buf8 = [0u8; 8];\n        file.read_exact(&amp;mut buf8)?;\n        let key_len = u64::from_le_bytes(buf8);\n        security::validate_gguf_string_len(key_len)?;\n\n        let mut key = vec![0u8; key_len as usize];\n        file.read_exact(&amp;mut key)?;\n        String::from_utf8(key).map_err(|e| VerifyError::Parse(format!(\"GGUF key utf8: {e}\")))\n    }\n\n    pub fn read_kv_type(file: &amp;mut std::fs::File) -&gt; Result {\n        let mut buf4 = [0u8; 4];\n        file.read_exact(&amp;mut buf4)?;\n        let t = i32::from_le_bytes(buf4);\n        security::validate_gguf_type(t)?;\n        Ok(t)\n    }\n}\n```\n\n### `src/lib.rs`\n\n```rust\n//! # arkhe-ai-verify\n//!\n//! Pipeline de 4 gates fail-closed para artefatos de IA.\n//!\n//! ## Invariantes\n//!\n//! - `INV-AI-V-01`: Todo artefato verificado tem `record_hash`\n//! - `INV-AI-V-02`: Formato desconhecido \u2192 `NONEXISTENT`\n//! - `INV-AI-V-03`: Gate 0 rejeita artefatos que excedem limites\n//! - `INV-AI-V-04`: Gate 2 verifica identity policy (issuer + subject)\n//! - `INV-AI-V-05`: Gate 3 verifica prova Rekor (checkpoint + SET)\n//! - `INV-AI-V-06`: data_offsets consistentes com shape e dtype\n//! - `INV-AI-V-07`: alignment GGUF \u2208 [4, 1 MiB] e pot\u00eancia de 2\n//! - `INV-AI-V-08`: n_dims \u2264 4\n//! - `INV-AI-V-09`: gguf_type \u2208 [0, 12]\n//! - `INV-AI-V-10`: blck_size \u2260 0\n\n#![deny(missing_docs)]\n#![deny(unsafe_code)]\n#![warn(missing_debug_implementations)]\n#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]\n\npub mod error;\npub mod formats;\npub mod gates;\npub mod security;\n\npub use error::VerifyError;\npub use security::{\n    validate_gguf_alignment, validate_gguf_block_size, validate_gguf_n_dims,\n    validate_gguf_type, validate_safetensors_offsets, SafeTensorDtype,\n};\n\nuse serde::{Deserialize, Serialize};\n\n/// Estado honesto.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]\n#[serde(rename_all = \"SCREAMING_SNAKE_CASE\")]\npub enum ArkheStatus {\n    Verified,\n    Partial,\n    Failed,\n    Nonexistent,\n    Pending,\n    Blocked,\n}\n\n/// Formato detectado.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]\n#[serde(rename_all = \"snake_case\")]\npub enum ArtifactFormat {\n    Safetensors,\n    Gguf,\n    Onnx,\n    Tensorrt,\n    Tflite,\n    Coreml,\n    Pytorch,\n    Huggingface,\n    Unknown,\n}\n\n/// Resultado de um gate.\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct GateResult {\n    pub gate_id: u8,\n    pub name: String,\n    pub status: ArkheStatus,\n    pub detail: String,\n    pub record_hash: String,\n}\n\n/// Resultado completo.\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct VerifyResult {\n    pub status: ArkheStatus,\n    pub artifact_format: ArtifactFormat,\n    pub artifact_digest: String,\n    pub gates: Vec,\n    pub record_hash: [u8; 32],\n}\n\n/// Trust anchor para Gate 2/3.\n#[derive(Debug, Clone)]\npub struct TrustAnchor {\n    pub issuer: String,\n    pub identity: String,\n}\n\nimpl Default for TrustAnchor {\n    fn default() -&gt; Self {\n        Self {\n            issuer: \"https://token.actions.githubusercontent.com\".into(),\n            identity: \"https://github.com/rafael-arkhe/arkhe-ai/.github/workflows/release.yml@refs/heads/main\".into(),\n        }\n    }\n}\n\n/// Detecta o formato pela extens\u00e3o.\n#[must_use]\npub fn detect_format(path: &amp;std::path::Path) -&gt; ArtifactFormat {\n    let ext = path\n        .extension()\n        .and_then(|e| e.to_str())\n        .unwrap_or(\"\")\n        .to_lowercase();\n    match ext.as_str() {\n        \"safetensors\" =&gt; ArtifactFormat::Safetensors,\n        \"gguf\" =&gt; ArtifactFormat::Gguf,\n        \"onnx\" =&gt; ArtifactFormat::Onnx,\n        \"engine\" | \"plan\" =&gt; ArtifactFormat::Tensorrt,\n        \"tflite\" =&gt; ArtifactFormat::Tflite,\n        \"mlmodel\" =&gt; ArtifactFormat::Coreml,\n        \"pt\" | \"pth\" | \"bin\" | \"ckpt\" =&gt; ArtifactFormat::Pytorch,\n        _ =&gt; ArtifactFormat::Unknown,\n    }\n}\n\n/// Executa o pipeline de 4 gates.\npub async fn verify_artifact(\n    path: &amp;std::path::Path,\n    expected_hash: Option&lt;&amp;str&gt;,\n    trust_anchor: &amp;TrustAnchor,\n) -&gt; Result {\n    let format = detect_format(path);\n    if format == ArtifactFormat::Unknown {\n        return Ok(VerifyResult {\n            status: ArkheStatus::Nonexistent,\n            artifact_format: format,\n            artifact_digest: String::new(),\n            gates: vec![],\n            record_hash: [0u8; 32],\n        });\n    }\n\n    let gate0 = gates::sanitize(path, format)?;\n    let gate1 = gates::hash(path, expected_hash)?;\n    let gate2 = gates::verify_signature(path, trust_anchor).await?;\n    let gate3 = gates::verify_inclusion(path, trust_anchor).await?;\n\n    let gates = vec![gate0, gate1, gate2, gate3];\n    let overall = if gates.iter().all(|g| g.status == ArkheStatus::Verified) {\n        ArkheStatus::Verified\n    } else if gates.iter().any(|g| g.status == ArkheStatus::Failed) {\n        ArkheStatus::Failed\n    } else {\n        ArkheStatus::Partial\n    };\n\n    let mut hasher = blake3::Hasher::new_derive_key(\"arkhe-ai-verify-v1\");\n    for g in &amp;gates {\n        hasher.update(g.record_hash.as_bytes());\n    }\n    let record_hash = *hasher.finalize().as_bytes();\n\n    Ok(VerifyResult {\n        status: overall,\n        artifact_format: format,\n        artifact_digest: gate1.detail.clone(),\n        gates,\n        record_hash,\n    })\n}\n```\n\n### `src/gates.rs`\n\n```rust\n//! # gates \u2014 Pipeline de 4 gates fail-closed.\n\nuse crate::error::VerifyError;\nuse crate::{ArkheStatus, ArtifactFormat, GateResult, TrustAnchor};\nuse std::io::Read;\n\n/// Gate 0: Sanitiza\u00e7\u00e3o + valida\u00e7\u00f5es espec\u00edficas do formato.\npub fn sanitize(\n    path: &amp;std::path::Path,\n    format: ArtifactFormat,\n) -&gt; Result {\n    let meta = std::fs::metadata(path)?;\n    let size = meta.len();\n    const MAX: u64 = 16 * 1024 * 1024 * 1024;\n    if size &gt; MAX {\n        return Err(VerifyError::LimitExceeded {\n            field: \"artifact.size\".into(),\n            value: size,\n            max: MAX,\n        });\n    }\n\n    let detail = match format {\n        ArtifactFormat::Safetensors =&gt; {\n            let header = crate::formats::safetensors::parse(path)?;\n            format!(\"safetensors OK \u00b7 {} tensores\", header.tensors.len())\n        }\n        ArtifactFormat::Gguf =&gt; {\n            let header = crate::formats::gguf::parse_header(path)?;\n            format!(\n                \"GGUF v{} OK \u00b7 {} tensores \u00b7 {} KV\",\n                header.version, header.tensor_count, header.metadata_kv_count\n            )\n        }\n        _ =&gt; format!(\"{size} bytes \u00b7 limites OK\"),\n    };\n\n    Ok(GateResult {\n        gate_id: 0,\n        name: \"Sanitiza\u00e7\u00e3o\".into(),\n        status: ArkheStatus::Verified,\n        detail,\n        record_hash: hex::encode(blake3::hash(b\"gate0-ok\").as_bytes()),\n    })\n}\n\n/// Gate 1: SHA-256.\npub fn hash(path: &amp;std::path::Path, expected: Option&lt;&amp;str&gt;) -&gt; Result {\n    use sha2::{Digest, Sha256};\n\n    let mut file = std::fs::File::open(path)?;\n    let mut hasher = Sha256::new();\n    let mut buf = vec![0u8; 65536];\n    loop {\n        let n = file.read(&amp;mut buf)?;\n        if n == 0 {\n            break;\n        }\n        hasher.update(&amp;buf[..n]);\n    }\n    let digest = hex::encode(hasher.finalize());\n\n    let status = match expected {\n        Some(exp) if exp.eq_ignore_ascii_case(&amp;digest) =&gt; ArkheStatus::Verified,\n        Some(_) =&gt; ArkheStatus::Failed,\n        None =&gt; ArkheStatus::Verified,\n    };\n\n    Ok(GateResult {\n        gate_id: 1,\n        name: \"Hash\".into(),\n        status,\n        detail: digest.clone(),\n        record_hash: hex::encode(blake3::hash(digest.as_bytes()).as_bytes()),\n    })\n}\n\n/// Gate 2: Assinatura Sigstore bundle v0.3.\npub async fn verify_signature(\n    path: &amp;std::path::Path,\n    trust_anchor: &amp;TrustAnchor,\n) -&gt; Result {\n    let bundle_path = path.with_extension(\"sigstore.json\");\n    if !bundle_path.exists() {\n        return Ok(GateResult {\n            gate_id: 2,\n            name: \"Assinatura\".into(),\n            status: ArkheStatus::Nonexistent,\n            detail: \"bundle Sigstore ausente (.sigstore.json)\".into(),\n            record_hash: hex::encode(blake3::hash(b\"gate2-nonexistent\").as_bytes()),\n        });\n    }\n\n    let bundle_json = std::fs::read_to_string(&amp;bundle_path)\n        .map_err(|e| VerifyError::Parse(format!(\"bundle read: {e}\")))?;\n\n    let bundle: sigstore_types::Bundle = serde_json::from_str(&amp;bundle_json)\n        .map_err(|e| VerifyError::Parse(format!(\"bundle parse: {e}\")))?;\n\n    let trusted_root = load_trusted_root().await?;\n    let verifier = sigstore_verify::Verifier::new(&amp;trusted_root);\n\n    let artifact_bytes = std::fs::read(path)?;\n\n    let policy = sigstore_verify::VerificationPolicy::new()\n        .issuer(&amp;trust_anchor.issuer)\n        .subject(&amp;trust_anchor.identity);\n\n    match verifier.verify_with_policy(&amp;bundle, &amp;artifact_bytes, &amp;policy) {\n        Ok(()) =&gt; Ok(GateResult {\n            gate_id: 2,\n            name: \"Assinatura\".into(),\n            status: ArkheStatus::Verified,\n            detail: format!(\n                \"ECDSA-P256 \u00b7 issuer={} \u00b7 identity={}\",\n                trust_anchor.issuer, trust_anchor.identity\n            ),\n            record_hash: hex::encode(blake3::hash(b\"gate2-verified\").as_bytes()),\n        }),\n        Err(e) =&gt; Ok(GateResult {\n            gate_id: 2,\n            name: \"Assinatura\".into(),\n            status: ArkheStatus::Failed,\n            detail: format!(\"verifica\u00e7\u00e3o falhou: {e}\"),\n            record_hash: hex::encode(blake3::hash(b\"gate2-failed\").as_bytes()),\n        }),\n    }\n}\n\n/// Gate 3: Inclus\u00e3o Rekor.\npub async fn verify_inclusion(\n    path: &amp;std::path::Path,\n    _trust_anchor: &amp;TrustAnchor,\n) -&gt; Result {\n    let bundle_path = path.with_extension(\"sigstore.json\");\n    if !bundle_path.exists() {\n        return Ok(GateResult {\n            gate_id: 3,\n            name: \"Inclus\u00e3o\".into(),\n            status: ArkheStatus::Nonexistent,\n            detail: \"sem prova de inclus\u00e3o\".into(),\n            record_hash: hex::encode(blake3::hash(b\"gate3-nonexistent\").as_bytes()),\n        });\n    }\n\n    let bundle_json = std::fs::read_to_string(&amp;bundle_path)\n        .map_err(|e| VerifyError::Parse(format!(\"bundle read: {e}\")))?;\n    let bundle: sigstore_types::Bundle = serde_json::from_str(&amp;bundle_json)\n        .map_err(|e| VerifyError::Parse(format!(\"bundle parse: {e}\")))?;\n\n    let trusted_root = load_trusted_root().await?;\n    let verifier = sigstore_verify::Verifier::new(&amp;trusted_root);\n\n    match verifier.verify_tlog(&amp;bundle) {\n        Ok(()) =&gt; Ok(GateResult {\n            gate_id: 3,\n            name: \"Inclus\u00e3o\".into(),\n            status: ArkheStatus::Verified,\n            detail: \"Rekor \u00b7 checkpoint + SET + inclusion proof\".into(),\n            record_hash: hex::encode(blake3::hash(b\"gate3-verified\").as_bytes()),\n        }),\n        Err(e) =&gt; Ok(GateResult {\n            gate_id: 3,\n            name: \"Inclus\u00e3o\".into(),\n            status: ArkheStatus::Failed,\n            detail: format!(\"tlog falhou: {e}\"),\n            record_hash: hex::encode(blake3::hash(b\"gate3-failed\").as_bytes()),\n        }),\n    }\n}\n\n/// Carrega o trust root Sigstore.\nasync fn load_trusted_root() -&gt; Result {\n    if let Ok(path) = std::env::var(\"ARKHE_TRUSTED_ROOT\") {\n        let json = std::fs::read_to_string(&amp;path)\n            .map_err(|e| VerifyError::Parse(format!(\"trusted_root read: {e}\")))?;\n        return sigstore_trust_root::TrustedRoot::from_json(&amp;json)\n            .map_err(|e| VerifyError::Parse(format!(\"trusted_root parse: {e}\")));\n    }\n\n    if let Some(home) = dirs::home_dir() {\n        let path = home.join(\".sigstore/root/tuf-repo-cdn.sigstore.dev/targets/trusted_root.json\");\n        if path.exists() {\n            let json = std::fs::read_to_string(&amp;path)\n                .map_err(|e| VerifyError::Parse(format!(\"trusted_root read: {e}\")))?;\n            return sigstore_trust_root::TrustedRoot::from_json(&amp;json)\n                .map_err(|e| VerifyError::Parse(format!(\"trusted_root parse: {e}\")));\n        }\n    }\n\n    sigstore_trust_root::TrustedRoot::production()\n        .map_err(|e| VerifyError::Parse(format!(\"trusted_root production: {e}\")))\n}\n```\n\n---\n\n## 5. Testes\n\n### `tests/safetensors_tests.rs`\n\n```rust\n//! Testes do parser safetensors.\n\nuse arkhe_ai_verify::formats::safetensors;\nuse arkhe_ai_verify::security::{validate_safetensors_offsets, SafeTensorDtype};\n\n#[test]\nfn parse_valid_file() {\n    let dir = tempfile::tempdir().unwrap();\n    let path = dir.path().join(\"model.safetensors\");\n\n    let json = r#\"{\"weight\":{\"dtype\":\"F32\",\"shape\":[2,2],\"data_offsets\":[0,16]}}\"#;\n    let mut bytes = Vec::new();\n    bytes.extend_from_slice(&amp;(json.len() as u64).to_le_bytes());\n    bytes.extend_from_slice(json.as_bytes());\n    bytes.extend_from_slice(&amp;[0u8; 16]);\n    std::fs::write(&amp;path, &amp;bytes).unwrap();\n\n    let header = safetensors::parse(&amp;path).unwrap();\n    assert_eq!(header.tensors.len(), 1);\n}\n\n#[test]\nfn reject_inconsistent_offsets() {\n    let shape = vec![1000usize, 1000];\n    let offsets = [0usize, 4];\n    assert!(validate_safetensors_offsets(&amp;shape, SafeTensorDtype::F32, &amp;offsets, 100).is_err());\n}\n\n#[test]\nfn reject_header_without_brace() {\n    let dir = tempfile::tempdir().unwrap();\n    let path = dir.path().join(\"bad.safetensors\");\n    let json = \"not-json\";\n    let mut bytes = Vec::new();\n    bytes.extend_from_slice(&amp;(json.len() as u64).to_le_bytes());\n    bytes.extend_from_slice(json.as_bytes());\n    std::fs::write(&amp;path, &amp;bytes).unwrap();\n    assert!(safetensors::parse(&amp;path).is_err());\n}\n```\n\n### `tests/security_tests.rs`\n\n```rust\n//! Testes das valida\u00e7\u00f5es de seguran\u00e7a.\n\nuse arkhe_ai_verify::security::{\n    sanitize_weight_map_path, validate_gguf_alignment, validate_gguf_block_size,\n    validate_gguf_n_dims, validate_gguf_type,\n};\n\n#[test]\nfn path_traversal_rejected() {\n    let base = std::path::Path::new(\"/tmp\");\n    assert!(sanitize_weight_map_path(base, \"../etc/passwd\").is_err());\n    assert!(sanitize_weight_map_path(base, \"/etc/passwd\").is_err());\n}\n\n#[test]\nfn alignment_bounds() {\n    assert!(validate_gguf_alignment(0).is_err());\n    assert!(validate_gguf_alignment(2).is_err());\n    assert!(validate_gguf_alignment(32).is_ok());\n    assert!(validate_gguf_alignment(1024 * 1024).is_ok());\n    assert!(validate_gguf_alignment(2 * 1024 * 1024).is_err());\n}\n\n#[test]\nfn n_dims_bounds() {\n    for n in 1..=4 {\n        assert!(validate_gguf_n_dims(n).is_ok());\n    }\n    assert!(validate_gguf_n_dims(5).is_err());\n}\n\n#[test]\nfn gguf_type_bounds() {\n    for t in 0..=12 {\n        assert!(validate_gguf_type(t).is_ok());\n    }\n    assert!(validate_gguf_type(13).is_err());\n    assert!(validate_gguf_type(-1).is_err());\n}\n\n#[test]\nfn block_size_nonzero() {\n    assert!(validate_gguf_block_size(1).is_ok());\n    assert!(validate_gguf_block_size(0).is_err());\n}\n```\n\n---\n\n## 6. CI\n\n### `.github/workflows/ci.yml`\n\n```yaml\nname: CI\n\non:\n  push:\n    branches: [main, develop]\n  pull_request:\n\nenv:\n  CARGO_TERM_COLOR: always\n  RUSTFLAGS: \"-D warnings\"\n\njobs:\n  fmt:\n    name: \"QG-AI-01 \u2014 rustfmt\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: dtolnay/rust-toolchain@stable\n        with: { components: rustfmt }\n      - run: cargo fmt --all -- --check\n\n  clippy:\n    name: \"QG-AI-02 \u2014 clippy\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: dtolnay/rust-toolchain@stable\n        with: { components: clippy }\n      - uses: Swatinem/rust-cache@v2\n      - run: cargo clippy --all-targets -- -D warnings\n\n  test:\n    name: \"QG-AI-03 \u2014 test\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: Swatinem/rust-cache@v2\n      - run: cargo test --all-features\n\n  deny:\n    name: \"QG-AI-04 \u2014 supply chain\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: EmbarkStudios/cargo-deny-action@v1\n        with:\n          command: check advisories bans licenses sources\n```\n\n---\n\n## 7. README\n\n```markdown\n# arkhe-ai-verify\n\n**Verifica\u00e7\u00e3o de artefatos de IA** \u2014 pipeline de 4 gates fail-closed para safetensors, GGUF, ONNX, TensorRT, TFLite, CoreML e PyTorch.\n\n## Invariantes\n\n| ID | Enunciado | Categoria |\n|:---|:---|:---|\n| `INV-AI-V-01` | Todo artefato verificado tem `record_hash` | AUDITABILITY |\n| `INV-AI-V-02` | Formato desconhecido \u2192 `NONEXISTENT` | SAFETY |\n| `INV-AI-V-03` | Gate 0 rejeita artefatos &gt; 16 GiB | SAFETY |\n| `INV-AI-V-04` | Gate 2 verifica identity policy | CRYPTOGRAPHY |\n| `INV-AI-V-05` | Gate 3 verifica prova Rekor | CRYPTOGRAPHY |\n| `INV-AI-V-06` | data_offsets consistentes com shape/dtype | SAFETY |\n| `INV-AI-V-07` | alignment GGUF \u2208 [4, 1 MiB] | SAFETY |\n| `INV-AI-V-08` | n_dims \u2264 4 | SAFETY |\n| `INV-AI-V-09` | gguf_type \u2208 [0, 12] | SAFETY |\n| `INV-AI-V-10` | blck_size \u2260 0 | SAFETY |\n\n## Quick start\n\n```bash\ncargo test\n```\n\n## Uso\n\n```rust\nuse arkhe_ai_verify::{verify_artifact, TrustAnchor};\n\nlet result = verify_artifact(\n    std::path::Path::new(\"model.safetensors\"),\n    None,\n    &amp;TrustAnchor::default(),\n).await?;\n\nassert_eq!(result.status, arkhe_ai_verify::ArkheStatus::Verified);\n```\n\n## CVEs Mitigados\n\n- **CVE-2026-65920**: path traversal em `weight_map`\n- **CVE-2026-7482**: GGUF OOB read (Bleeding Llama)\n- **PR #3364**: data_offsets inconsistentes\n\n## Licen\u00e7a\n\nMIT OR Apache-2.0.\n```\n\n---\n\n## 8. Invariantes Consolidadas\n\n| ID | Enunciado | Falsificador | Verifica\u00e7\u00e3o |\n|:---|:---|:---|:---|\n| `INV-AI-V-01` | Todo artefato verificado tem `record_hash` | Hash zerado | `verify_produces_record_hash` |\n| `INV-AI-V-02` | Formato desconhecido \u2192 `NONEXISTENT` | Formato desconhecido aceite | `unknown_format_nonexistent` |\n| `INV-AI-V-03` | Gate 0 rejeita artefatos &gt; 16 GiB | Artefato gigante aceite | `sanitize_rejects_huge` |\n| `INV-AI-V-04` | Gate 2 verifica identity policy | Identity divergente aceite | `verify_signature_rejects_wrong_identity` |\n| `INV-AI-V-05` | Gate 3 verifica prova Rekor | Bundle sem tlog aceite | `verify_inclusion_rejects_no_tlog` |\n| `INV-AI-V-06` | data_offsets consistentes | Offsets inconsistentes aceites | `reject_inconsistent_offsets` |\n| `INV-AI-V-07` | alignment GGUF \u2208 [4, 1 MiB] | Alignment 0 ou 2 GiB aceite | `alignment_bounds` |\n| `INV-AI-V-08` | n_dims GGUF \u2264 4 | n_dims = 5 aceite | `n_dims_bounds` |\n| `INV-AI-V-09` | gguf_type \u2208 [0, 12] | Tipo 99 aceite | `gguf_type_bounds` |\n| `INV-AI-V-10` | blck_size GGUF \u2260 0 | blck_size = 0 aceite | `block_size_nonzero` |\n\n---\n\n## 9. Selo\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  ARKHE-AI-VERIFY \u2014 REPOSIT\u00d3RIO COMPLETO (v0.1.0)                     \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 SAFETENSORS:                                                     \u2502\n\u2502     \u00b7 Header come\u00e7a com '{'                                         \u2502\n\u2502     \u00b7 data_offsets relativos ao data buffer                         \u2502\n\u2502     \u00b7 Valida\u00e7\u00e3o count/ordem/consist\u00eancia (PR #3364)                 \u2502\n\u2502     \u00b7 Sanitiza\u00e7\u00e3o de weight_map (CVE-2026-65920)                    \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 GGUF:                                                            \u2502\n\u2502     \u00b7 Header: magic + version(3) + tensor_count + metadata_kv_count \u2502\n\u2502     \u00b7 Alignment \u2208 [4, 1 MiB] e pot\u00eancia de 2                        \u2502\n\u2502     \u00b7 n_dims \u2264 4                                                    \u2502\n\u2502     \u00b7 gguf_type \u2208 [0, 12]                                           \u2502\n\u2502     \u00b7 blck_size \u2260 0                                                 \u2502\n\u2502     \u00b7 Strings \u2264 64 MiB, arrays \u2264 1M                                 \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 SIGSTORE:                                                        \u2502\n\u2502     \u00b7 Bundle v0.3 (.sigstore.json)                                  \u2502\n\u2502     \u00b7 sigstore-verify 0.11.0                                        \u2502\n\u2502     \u00b7 Identity policy: issuer + subject                             \u2502\n\u2502     \u00b7 Trust root: env / cache cosign / production                   \u2502\n\u2502                                                                     \u2502\n\u2502  \u2705 INVARIANTES (10): INV-AI-V-01..10                                \u2502\n\u2502                                                                     \u2502\n\u2502  \ud83d\udccb COMANDO:                                                         \u2502\n\u2502     cargo test                                                      \u2502\n\u2502                                                                     \u2502\n\u2502  Selo: ARKHE-AI-VERIFY-2026-09-26                                   \u2502\n\u2502  Status: \u2705 COMPLETO \u2014 PRONTO PARA `cargo test`                     \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n**Nota de honestidade:** As valida\u00e7\u00f5es de seguran\u00e7a implementam as corre\u00e7\u00f5es documentadas em CVE-2026-65920 (path traversal), PR #3364 (data_offsets inconsistentes) e os advisories do llama.cpp (CVE-2026-7482). A integra\u00e7\u00e3o Sigstore usa `sigstore-verify` 0.11.0 e `sigstore-trust-root` 0.11.0, com bundles v0.3. As vers\u00f5es das depend\u00eancias foram verificadas contra as releases mais recentes no crates.io. A evid\u00eancia \u00e9 `cargo test`.", "creation_timestamp": "2026-09-26T04:21:06.000000Z"}