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:
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"