fix(relay-skills): enforce fetchable coordinate slugs

Align persisted and ACP coordinate validation with the buzz notes command so lazy-fetch instructions are always executable and delimiter-safe.

Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-25 01:53:34 -07:00
committed by Rizz
parent 8fbde2c220
commit 5ec4fbe320
2 changed files with 22 additions and 2 deletions

View File

@@ -8,6 +8,7 @@ use crate::relay::RestClient;
const SKILL_KIND: u16 = 30023;
const MAX_SKILLS: usize = 64;
const MAX_SLUG_BYTES: usize = 80;
const MAX_DESCRIPTION_BYTES: usize = 1024;
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -24,7 +25,14 @@ fn parse_coordinate(raw: &str) -> Option<Coordinate> {
}
let publisher = PublicKey::from_hex(parts.next()?).ok()?;
let slug = parts.next()?.to_string();
(!slug.is_empty()).then_some(Coordinate {
(!slug.is_empty()
&& slug.len() <= MAX_SLUG_BYTES
&& slug.chars().all(|character| {
character.is_ascii_lowercase()
|| character.is_ascii_digit()
|| matches!(character, '-' | '_' | '.')
}))
.then_some(Coordinate {
raw: raw.to_string(),
publisher,
slug,
@@ -154,6 +162,18 @@ mod tests {
use super::*;
use nostr::{EventBuilder, Keys, Tag, Timestamp};
#[test]
fn rejects_slugs_the_lazy_fetch_command_cannot_accept() {
let publisher = Keys::generate().public_key().to_hex();
assert!(parse_coordinate(&format!("30023:{publisher}:valid_slug-1.0")).is_some());
assert!(parse_coordinate(&format!("30023:{publisher}:Uppercase")).is_none());
assert!(parse_coordinate(&format!(
"30023:{publisher}:{}",
"x".repeat(MAX_SLUG_BYTES + 1)
))
.is_none());
}
#[test]
fn exact_query_filters_do_not_admit_author_slug_cross_products() {
let alpha = Keys::generate();

View File

@@ -17,7 +17,7 @@ use tauri::State;
use crate::{app_state::AppState, native_relay_client::NativeRelayClient};
pub const RELAY_SKILL_KIND: u16 = 30023;
pub const MAX_RELAY_SKILL_SLUG_BYTES: usize = 255;
pub const MAX_RELAY_SKILL_SLUG_BYTES: usize = 80;
pub const MAX_AGENT_SKILL_NAME_BYTES: usize = 64;
pub const MAX_AGENT_SKILL_DESCRIPTION_BYTES: usize = 1024;
pub const MAX_ASSIGNED_RELAY_SKILLS: usize = 64;