field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

commit c5aaaff421b879b7cbfabed086dd2e0c96e83afa
parent 8cae06e6e2eb7a92851d9403539ab6cbfc0613d3
Author: triesap <tyson@radroots.org>
Date:   Wed,  9 Sep 2026 17:08:01 +0000

today: bound cursor input before allocation

- Reject oversized v1 tokens before scanning, hex decoding or hashing.
- Derive the byte cap from validated context bounds and check encoded lengths.
- Cover maximum, malformed, corrupted and invalid-scope boundary cases.
- Rebuild exact native artifacts while preserving accepted bytes and binding APIs.

Diffstat:
MTeraFFI/provenance.json | 54+++++++++++++++++++++++++++---------------------------
MTeraFFI/source.lock | 4++--
MTeraFFI/source/aarch64-apple-darwin.json | 20+++++++++++++-------
MTeraFFI/source/aarch64-apple-ios-sim.json | 20+++++++++++++-------
MTeraFFI/source/aarch64-apple-ios.json | 20+++++++++++++-------
Mcore/crates/tera_core/src/runtime/product_surface/cursor.rs | 19++++++++++++++-----
Acore/crates/tera_core/src/runtime/product_surface/cursor_boundary_tests.rs | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcore/crates/tera_core/src/runtime/product_surface/local_network_id.rs | 4+++-
Mrelease/provenance.json | 4++--
9 files changed, 213 insertions(+), 58 deletions(-)

diff --git a/TeraFFI/provenance.json b/TeraFFI/provenance.json @@ -22,9 +22,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 71247176, + "bytes": 71240640, "path": "TeraFFI.xcframework/ios-arm64-simulator/libtera_ffi.a", - "sha256": "9f83b0d9f50c3b59736ae843ffa503bd678654b571b7ff4ad6109ffd66af2e87" + "sha256": "7d2dcf5b097e28fd1d91029e6685a899a474ace53224d259ae0d07f32f1285ce" }, { "bytes": 70167, @@ -37,9 +37,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 71299416, + "bytes": 71295120, "path": "TeraFFI.xcframework/ios-arm64/libtera_ffi.a", - "sha256": "b29a77aba06a05e8901628bf83a8e02abfee0991dca60f1306e2c6a47e1f1bdb" + "sha256": "ec4f869934ae02c7eacfe83c2597054843d1d6cee0d26d50121306a0452557c6" }, { "bytes": 41698, @@ -77,34 +77,34 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 19860544, + "bytes": 19860560, "path": "native/aarch64-apple-darwin/libtera_ffi.dylib", - "sha256": "b2d1aefdaaa7d9419389c21ab8156ff869d9027bd0ebd5d4ad1cea8566182344" + "sha256": "8197b184f891653e082dca33004bba475d21ffd0b977f6ca38cdd363e914801f" }, { - "bytes": 71247176, + "bytes": 71240640, "path": "native/aarch64-apple-ios-sim/libtera_ffi.a", - "sha256": "9f83b0d9f50c3b59736ae843ffa503bd678654b571b7ff4ad6109ffd66af2e87" + "sha256": "7d2dcf5b097e28fd1d91029e6685a899a474ace53224d259ae0d07f32f1285ce" }, { - "bytes": 71299416, + "bytes": 71295120, "path": "native/aarch64-apple-ios/libtera_ffi.a", - "sha256": "b29a77aba06a05e8901628bf83a8e02abfee0991dca60f1306e2c6a47e1f1bdb" + "sha256": "ec4f869934ae02c7eacfe83c2597054843d1d6cee0d26d50121306a0452557c6" }, { - "bytes": 61732, + "bytes": 62025, "path": "source/aarch64-apple-darwin.json", - "sha256": "d345cf081f8e930146fee681e2850ffef638cde01f44e892fe5d29e187f6f511" + "sha256": "e21de0fdf37daa17745596396421c849045cdbd30c43e491726d23a3e03c089d" }, { - "bytes": 61576, + "bytes": 61869, "path": "source/aarch64-apple-ios-sim.json", - "sha256": "d69260103114a7ce7775892be8a66606e7107335f25b2595aae514ad52674cbe" + "sha256": "87018a29e7f5f3616d71750c9b04399a882e66656a8ca4b13299d86dab2fa84b" }, { - "bytes": 61572, + "bytes": 61865, "path": "source/aarch64-apple-ios.json", - "sha256": "b6109503a5606187dffc6754b565e7909f250197c2e67ad2466c33e6d16f18db" + "sha256": "4c973612c9ed7bc4c59dee90327e663b8cff89220f3839dd941ebf3d01433f36" } ], "language": "swift", @@ -112,7 +112,7 @@ "schema": "radroots.artifact-manifest.v2", "source": { "repository": "https://github.com/radrootslabs/tera", - "tree": "7a0e8bd776f6ab661db04d99e6a162d423c7402a" + "tree": "ec5432e7e1b5f9b17a23cee343947880ee292026" }, "source_records": { "aarch64-apple-darwin": "source/aarch64-apple-darwin.json", @@ -139,9 +139,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 71247176, + "bytes": 71240640, "path": "Tera/Frameworks/TeraFFI.xcframework/ios-arm64-simulator/libtera_ffi.a", - "sha256": "9f83b0d9f50c3b59736ae843ffa503bd678654b571b7ff4ad6109ffd66af2e87" + "sha256": "7d2dcf5b097e28fd1d91029e6685a899a474ace53224d259ae0d07f32f1285ce" }, { "bytes": 70167, @@ -154,9 +154,9 @@ "sha256": "3ed9b7ece2f86e5b5d1ddc6ecbb97deb49c46ca2ea91a930eaec1b580232345c" }, { - "bytes": 71299416, + "bytes": 71295120, "path": "Tera/Frameworks/TeraFFI.xcframework/ios-arm64/libtera_ffi.a", - "sha256": "b29a77aba06a05e8901628bf83a8e02abfee0991dca60f1306e2c6a47e1f1bdb" + "sha256": "ec4f869934ae02c7eacfe83c2597054843d1d6cee0d26d50121306a0452557c6" }, { "bytes": 492997, @@ -174,19 +174,19 @@ "sha256": "ab87eb6e3d4512acc3986120c38988f8e7559ac1781de76d9808e0bae338d1de" }, { - "bytes": 61732, + "bytes": 62025, "path": "TeraFFI/source/aarch64-apple-darwin.json", - "sha256": "d345cf081f8e930146fee681e2850ffef638cde01f44e892fe5d29e187f6f511" + "sha256": "e21de0fdf37daa17745596396421c849045cdbd30c43e491726d23a3e03c089d" }, { - "bytes": 61576, + "bytes": 61869, "path": "TeraFFI/source/aarch64-apple-ios-sim.json", - "sha256": "d69260103114a7ce7775892be8a66606e7107335f25b2595aae514ad52674cbe" + "sha256": "87018a29e7f5f3616d71750c9b04399a882e66656a8ca4b13299d86dab2fa84b" }, { - "bytes": 61572, + "bytes": 61865, "path": "TeraFFI/source/aarch64-apple-ios.json", - "sha256": "b6109503a5606187dffc6754b565e7909f250197c2e67ad2466c33e6d16f18db" + "sha256": "4c973612c9ed7bc4c59dee90327e663b8cff89220f3839dd941ebf3d01433f36" } ], "schema": "tera.installed-native-artifacts.v1" diff --git a/TeraFFI/source.lock b/TeraFFI/source.lock @@ -1,7 +1,7 @@ schema = "tera.installed-source.v1" repository = "https://github.com/radrootslabs/tera" -source_tree = "7a0e8bd776f6ab661db04d99e6a162d423c7402a" -manifest_sha256 = "99ff33807478af02891b42090d57f6bcb5a2ea56eef941451797743af89667e0" +source_tree = "ec5432e7e1b5f9b17a23cee343947880ee292026" +manifest_sha256 = "29b6ada7a8b8e83f9d992d6eb9971638adb6c3044c37b86830f59ad046dd1924" source_date_epoch = 1787871027 [foundation] diff --git a/TeraFFI/source/aarch64-apple-darwin.json b/TeraFFI/source/aarch64-apple-darwin.json @@ -655,10 +655,16 @@ "sha256": "e2bba7483e5c22413d621fdc7e18e79648b2afaab67078ffaba18f9737e2f120" }, "core/crates/tera_core/src/runtime/product_surface/cursor.rs": { - "bytes": 16205, - "git_blob": "0a389e6415da2b6b14cc5b338af9ec09361809cc", + "bytes": 16624, + "git_blob": "64c80d13fe0c0437af16debd48bd218c398a7760", "mode": "100644", - "sha256": "b8fe77aafce5b60e02a5e020c774a1396973deb48fbc108745fc4babc20318c6" + "sha256": "dbe14164757208306fb03e27063dc90cf948904b8582c29e87a60e085425598d" + }, + "core/crates/tera_core/src/runtime/product_surface/cursor_boundary_tests.rs": { + "bytes": 4749, + "git_blob": "43c6d53da203b400da8996e8cbdd4e643fa013b6", + "mode": "100644", + "sha256": "27add9cd6152a8c21ad66e0eab03632445a14039c43c3bf7848571b5da869606" }, "core/crates/tera_core/src/runtime/product_surface/identity.rs": { "bytes": 6310, @@ -667,10 +673,10 @@ "sha256": "2f15da2b1f648b417785e2a12eff8e984ab7e8d6c6b87fb9cb5552e195d8a3ec" }, "core/crates/tera_core/src/runtime/product_surface/local_network_id.rs": { - "bytes": 2073, - "git_blob": "a4cc9eea655532f541fcb7753f44d57c047925eb", + "bytes": 2155, + "git_blob": "b9ec4d35c10ce4b30d203d73f40dcdcebd74ef31", "mode": "100644", - "sha256": "05c360696b21b238fb416cf3b8b00207f7bf0d4188eb5ed2d2c609d5ab24cd2b" + "sha256": "af6606fa0c20f63756f9bf462cba1c681c201892b720c1135d71a17123b13ae7" }, "core/crates/tera_core/src/runtime/product_surface/media.rs": { "bytes": 75981, @@ -1340,6 +1346,6 @@ } }, "policy": "staged_inputs", - "tree": "7a0e8bd776f6ab661db04d99e6a162d423c7402a" + "tree": "ec5432e7e1b5f9b17a23cee343947880ee292026" } } diff --git a/TeraFFI/source/aarch64-apple-ios-sim.json b/TeraFFI/source/aarch64-apple-ios-sim.json @@ -651,10 +651,16 @@ "sha256": "e2bba7483e5c22413d621fdc7e18e79648b2afaab67078ffaba18f9737e2f120" }, "core/crates/tera_core/src/runtime/product_surface/cursor.rs": { - "bytes": 16205, - "git_blob": "0a389e6415da2b6b14cc5b338af9ec09361809cc", + "bytes": 16624, + "git_blob": "64c80d13fe0c0437af16debd48bd218c398a7760", "mode": "100644", - "sha256": "b8fe77aafce5b60e02a5e020c774a1396973deb48fbc108745fc4babc20318c6" + "sha256": "dbe14164757208306fb03e27063dc90cf948904b8582c29e87a60e085425598d" + }, + "core/crates/tera_core/src/runtime/product_surface/cursor_boundary_tests.rs": { + "bytes": 4749, + "git_blob": "43c6d53da203b400da8996e8cbdd4e643fa013b6", + "mode": "100644", + "sha256": "27add9cd6152a8c21ad66e0eab03632445a14039c43c3bf7848571b5da869606" }, "core/crates/tera_core/src/runtime/product_surface/identity.rs": { "bytes": 6310, @@ -663,10 +669,10 @@ "sha256": "2f15da2b1f648b417785e2a12eff8e984ab7e8d6c6b87fb9cb5552e195d8a3ec" }, "core/crates/tera_core/src/runtime/product_surface/local_network_id.rs": { - "bytes": 2073, - "git_blob": "a4cc9eea655532f541fcb7753f44d57c047925eb", + "bytes": 2155, + "git_blob": "b9ec4d35c10ce4b30d203d73f40dcdcebd74ef31", "mode": "100644", - "sha256": "05c360696b21b238fb416cf3b8b00207f7bf0d4188eb5ed2d2c609d5ab24cd2b" + "sha256": "af6606fa0c20f63756f9bf462cba1c681c201892b720c1135d71a17123b13ae7" }, "core/crates/tera_core/src/runtime/product_surface/media.rs": { "bytes": 75981, @@ -1336,6 +1342,6 @@ } }, "policy": "staged_inputs", - "tree": "7a0e8bd776f6ab661db04d99e6a162d423c7402a" + "tree": "ec5432e7e1b5f9b17a23cee343947880ee292026" } } diff --git a/TeraFFI/source/aarch64-apple-ios.json b/TeraFFI/source/aarch64-apple-ios.json @@ -651,10 +651,16 @@ "sha256": "e2bba7483e5c22413d621fdc7e18e79648b2afaab67078ffaba18f9737e2f120" }, "core/crates/tera_core/src/runtime/product_surface/cursor.rs": { - "bytes": 16205, - "git_blob": "0a389e6415da2b6b14cc5b338af9ec09361809cc", + "bytes": 16624, + "git_blob": "64c80d13fe0c0437af16debd48bd218c398a7760", "mode": "100644", - "sha256": "b8fe77aafce5b60e02a5e020c774a1396973deb48fbc108745fc4babc20318c6" + "sha256": "dbe14164757208306fb03e27063dc90cf948904b8582c29e87a60e085425598d" + }, + "core/crates/tera_core/src/runtime/product_surface/cursor_boundary_tests.rs": { + "bytes": 4749, + "git_blob": "43c6d53da203b400da8996e8cbdd4e643fa013b6", + "mode": "100644", + "sha256": "27add9cd6152a8c21ad66e0eab03632445a14039c43c3bf7848571b5da869606" }, "core/crates/tera_core/src/runtime/product_surface/identity.rs": { "bytes": 6310, @@ -663,10 +669,10 @@ "sha256": "2f15da2b1f648b417785e2a12eff8e984ab7e8d6c6b87fb9cb5552e195d8a3ec" }, "core/crates/tera_core/src/runtime/product_surface/local_network_id.rs": { - "bytes": 2073, - "git_blob": "a4cc9eea655532f541fcb7753f44d57c047925eb", + "bytes": 2155, + "git_blob": "b9ec4d35c10ce4b30d203d73f40dcdcebd74ef31", "mode": "100644", - "sha256": "05c360696b21b238fb416cf3b8b00207f7bf0d4188eb5ed2d2c609d5ab24cd2b" + "sha256": "af6606fa0c20f63756f9bf462cba1c681c201892b720c1135d71a17123b13ae7" }, "core/crates/tera_core/src/runtime/product_surface/media.rs": { "bytes": 75981, @@ -1336,6 +1342,6 @@ } }, "policy": "staged_inputs", - "tree": "7a0e8bd776f6ab661db04d99e6a162d423c7402a" + "tree": "ec5432e7e1b5f9b17a23cee343947880ee292026" } } diff --git a/core/crates/tera_core/src/runtime/product_surface/cursor.rs b/core/crates/tera_core/src/runtime/product_surface/cursor.rs @@ -1,6 +1,7 @@ use sha2::{Digest, Sha256}; use thiserror::Error; +use super::local_network_id::LOCAL_NETWORK_ID_MAX_BYTES; use super::{CardId, ContextRank, LocalNetworkId, TODAY_RANK_SCHEMA_VERSION, TodayRank}; use crate::runtime::product_surface::ranking::TODAY_RANK_ALGORITHM_VERSION; @@ -9,6 +10,8 @@ const CURSOR_DOMAIN: &[u8] = b"radroots.today-cursor.v1\0"; const CURSOR_SCHEMA_VERSION: u16 = 1; const FIXED_PAYLOAD_BYTES: usize = 2 + 2 + 2 + 2 + 8 + 8 + 32 + 8 + 1 + 1 + 8 + 32; const DIGEST_BYTES: usize = 32; +const MAX_CURSOR_BYTES: usize = + CURSOR_PREFIX.len() + 2 * (FIXED_PAYLOAD_BYTES + LOCAL_NETWORK_ID_MAX_BYTES + DIGEST_BYTES); #[derive(Clone, Debug, Eq, PartialEq)] pub struct CursorScope { @@ -78,15 +81,13 @@ impl TodayCursor { return Err(CursorError::InvalidPosition); } let context_bytes = scope.context_id.as_bytes(); + let context_len = + u16::try_from(context_bytes.len()).map_err(|_| CursorError::InvalidContext)?; let mut payload = Vec::with_capacity(FIXED_PAYLOAD_BYTES + context_bytes.len()); payload.extend_from_slice(&CURSOR_SCHEMA_VERSION.to_be_bytes()); payload.extend_from_slice(&TODAY_RANK_SCHEMA_VERSION.to_be_bytes()); payload.extend_from_slice(&TODAY_RANK_ALGORITHM_VERSION.to_be_bytes()); - payload.extend_from_slice( - &u16::try_from(context_bytes.len()) - .expect("validated context length fits u16") - .to_be_bytes(), - ); + payload.extend_from_slice(&context_len.to_be_bytes()); payload.extend_from_slice(context_bytes); payload.extend_from_slice(&scope.context_generation.to_be_bytes()); payload.extend_from_slice(&scope.as_of.to_be_bytes()); @@ -130,6 +131,10 @@ impl TodayCursor { } fn decode_unbound(value: &str) -> Result<(CursorScope, TodayCursorPosition), CursorError> { + // The v1 token is bounded before any content scan, hex allocation or hash. + if value.len() > MAX_CURSOR_BYTES { + return Err(CursorError::Malformed); + } let encoded = value .strip_prefix(CURSOR_PREFIX) .ok_or(CursorError::Malformed)?; @@ -254,6 +259,10 @@ impl<'a> Decoder<'a> { } #[cfg(test)] +#[path = "cursor_boundary_tests.rs"] +mod boundary_tests; + +#[cfg(test)] mod tests { use super::*; diff --git a/core/crates/tera_core/src/runtime/product_surface/cursor_boundary_tests.rs b/core/crates/tera_core/src/runtime/product_surface/cursor_boundary_tests.rs @@ -0,0 +1,126 @@ +use super::*; + +fn scope(context: &str) -> CursorScope { + CursorScope::new(context.into(), u64::MAX, u64::MAX, [0xff; 32], u64::MAX).expect("valid scope") +} + +fn position() -> TodayCursorPosition { + TodayCursorPosition { + rank: TodayRank { + schema_version: TODAY_RANK_SCHEMA_VERSION, + algorithm_version: TODAY_RANK_ALGORITHM_VERSION, + context_rank: ContextRank::LocalityMatch, + time_relevance_rank: 4, + effective_at: u64::MAX, + card_id: CardId::parse(&"f".repeat(64)).expect("card"), + }, + } +} + +fn assert_error(value: &str, error: CursorError) { + assert_eq!(TodayCursor::scope(value), Err(error)); + assert_eq!(TodayCursor::decode(value, &scope("nearby")), Err(error)); +} + +fn rehashed(mut payload: Vec<u8>) -> String { + payload.extend_from_slice(&cursor_digest(&payload)); + format!("{CURSOR_PREFIX}{}", hex::encode(payload)) +} + +#[test] +fn maximum_cursor_round_trips_ascii_and_multibyte_contexts() { + assert_eq!(FIXED_PAYLOAD_BYTES, 106); + assert_eq!(MAX_CURSOR_BYTES, 794); + for context in ["x".repeat(256), "é".repeat(128)] { + let scope = scope(&context); + let cursor = TodayCursor::encode(&scope, position()).expect("cursor"); + assert!(cursor.as_str().is_ascii()); + assert_eq!(cursor.as_str().len(), MAX_CURSOR_BYTES); + assert_eq!(TodayCursor::scope(cursor.as_str()), Ok(scope.clone())); + assert_eq!(TodayCursor::decode(cursor.as_str(), &scope), Ok(position())); + } +} + +#[test] +fn oversized_hex_is_rejected_before_integrity_decoding() { + let valid = TodayCursor::encode(&scope(&"x".repeat(256)), position()).expect("cursor"); + for suffix in ["0", "00"] { + assert_error( + &format!("{}{suffix}", valid.as_str()), + CursorError::Malformed, + ); + } + // All bytes are valid hex. Without the length gate these reach hashing and + // return Integrity, after allocating a decoded buffer proportional to input. + for bytes in [MAX_CURSOR_BYTES + 2, 8 * 1024 * 1024] { + let malicious = format!("{CURSOR_PREFIX}{}", "0".repeat(bytes - CURSOR_PREFIX.len())); + assert_eq!(malicious.len(), bytes); + assert_error(&malicious, CursorError::Malformed); + } +} + +#[test] +fn both_cursor_entry_points_reject_malformed_shapes_and_checksums() { + for malformed in [ + "", "rrtc1:", "rrtc1:0", "rrtc1:00", "rrtc1:GG", "rrtc1:é", "rrtc2:00", + ] { + assert_error(malformed, CursorError::Malformed); + } + let cursor = TodayCursor::encode(&scope("nearby"), position()).expect("cursor"); + let encoded = cursor.as_str().strip_prefix(CURSOR_PREFIX).expect("prefix"); + let mut bytes = hex::decode(encoded).expect("hex"); + *bytes.last_mut().expect("digest") ^= 1; + assert_error( + &format!("{CURSOR_PREFIX}{}", hex::encode(bytes)), + CursorError::Integrity, + ); +} + +#[test] +fn integrity_valid_payloads_reject_versions_lengths_and_trailing_bytes() { + let cursor = TodayCursor::encode(&scope("nearby"), position()).expect("cursor"); + let mut payload = + hex::decode(cursor.as_str().strip_prefix(CURSOR_PREFIX).expect("prefix")).expect("hex"); + payload.truncate(payload.len() - DIGEST_BYTES); + for offset in [1, 3, 5] { + let mut invalid = payload.clone(); + invalid[offset] = 2; + assert_error(&rehashed(invalid), CursorError::Version); + } + for length in [257_u16, u16::MAX] { + let mut invalid = payload.clone(); + invalid[6..8].copy_from_slice(&length.to_be_bytes()); + assert_error(&rehashed(invalid), CursorError::Malformed); + } + payload.push(0); + assert_error(&rehashed(payload), CursorError::Malformed); +} + +#[test] +fn scope_construction_and_deserialization_cannot_admit_invalid_contexts() { + for context in [ + "".into(), + "x".repeat(257), + "é".repeat(129), + " nearby".into(), + "x\u{7f}".into(), + ] { + assert_eq!( + CursorScope::new(context.clone(), 0, 0, [0; 32], 0), + Err(CursorError::InvalidContext) + ); + let wire = serde_json::to_string(&context).expect("wire"); + assert!(serde_json::from_str::<LocalNetworkId>(&wire).is_err()); + } + // Public scope fields still require the validated, representation-private ID. + let id = serde_json::from_str::<LocalNetworkId>("\"nearby\"").expect("validated ID"); + let scope = CursorScope { + context_id: id, + context_generation: 0, + as_of: 0, + store_generation: [0; 32], + projection_generation: 0, + }; + let cursor = TodayCursor::encode(&scope, position()).expect("cursor"); + assert_eq!(TodayCursor::decode(cursor.as_str(), &scope), Ok(position())); +} diff --git a/core/crates/tera_core/src/runtime/product_surface/local_network_id.rs b/core/crates/tera_core/src/runtime/product_surface/local_network_id.rs @@ -2,6 +2,8 @@ use serde::{Deserialize, Serialize}; use super::LocalNetworkError; +pub(super) const LOCAL_NETWORK_ID_MAX_BYTES: usize = 256; + /// Stable local context identity, independent of any presentation session. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] #[serde(try_from = "String", into = "String")] @@ -10,7 +12,7 @@ pub struct LocalNetworkId(String); impl LocalNetworkId { pub fn new(value: String) -> Result<Self, LocalNetworkError> { if value.is_empty() - || value.len() > 256 + || value.len() > LOCAL_NETWORK_ID_MAX_BYTES || value.trim() != value || value.chars().any(char::is_control) { diff --git a/release/provenance.json b/release/provenance.json @@ -2,7 +2,7 @@ "artifacts": { "app_api_sha256": "020924097c0d7efc33128cb8fd3d3b2026d95f57c44da71880e585aff80f070b", "ffi_api_sha256": "ab87eb6e3d4512acc3986120c38988f8e7559ac1781de76d9808e0bae338d1de", - "ffi_provenance_sha256": "99ff33807478af02891b42090d57f6bcb5a2ea56eef941451797743af89667e0", + "ffi_provenance_sha256": "29b6ada7a8b8e83f9d992d6eb9971638adb6c3044c37b86830f59ad046dd1924", "info_plist_sha256": "15ef08b1cdd1096cfb9eeaf5be5bf8f814807a7ca9350bbbb47860fa72ec13ef", "privacy_manifest_sha256": "a331d51864743ebe4e00dd22360b4a538b6b3ac26a6b3eb54094e60a36959a12", "sbom_sha256": "6b5ce897d5273290febc8b831663e12dea759cb0084f182ba7949e1c593fc410", @@ -22,7 +22,7 @@ "lib_revision": "ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb", "source_date_epoch": 1787871027, "swift_package_lock_sha256": "94ae067a374726cdaf6b4ca0a5e44663c57fcdc5334060c5ffef5e79cfbf04c0", - "tera_ffi_source_tree": "7a0e8bd776f6ab661db04d99e6a162d423c7402a", + "tera_ffi_source_tree": "ec5432e7e1b5f9b17a23cee343947880ee292026", "xcode_package_lock_sha256": "c7f41934ea25f7a287bdc4f3a6ecabbf09a3a0bdd0f5a3e58183f355ca814096" }, "version": "0.1.0-alpha"