mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Close remaining local-agent relay gaps
Co-authored-by: npub102wg7q285p64ch2fjvstmf2ntn2sz3c4u5hmwatalc76mhsuauysftjtfj <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz> Signed-off-by: npub102wg7q285p64ch2fjvstmf2ntn2sz3c4u5hmwatalc76mhsuauysftjtfj <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz>
This commit is contained in:
parent
62c9f36578
commit
93ea37c236
@@ -468,7 +468,8 @@ const overrides = new Map([
|
||||
// (if let Some(provider_update) = input.provider { record.provider = provider_update; }).
|
||||
// +8: harness_override thread-through in update_managed_agent so a deliberate
|
||||
// Custom pin routes to update_time_agent_command_override (comment + call).
|
||||
["src-tauri/src/commands/agent_models.rs", 1079],
|
||||
// +4: internal relay lockdown validates effective relay before update/profile sync.
|
||||
["src-tauri/src/commands/agent_models.rs", 1083],
|
||||
// global-agent-config: get_agent_config_surface / write_agent_config_field /
|
||||
// put_agent_session_config commands + GlobalAgentConfig serde types. New file
|
||||
// in this PR; queued to split with the command module refactor.
|
||||
|
||||
@@ -801,6 +801,7 @@ pub async fn update_managed_agent(
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<UpdateManagedAgentResponse, String> {
|
||||
// Phase 1: local save (synchronous, under lock)
|
||||
let workspace_relay_url = relay_ws_url_with_override(&state);
|
||||
let (summary, sync_params, rollback) = {
|
||||
let _store_guard = state
|
||||
.managed_agents_store_lock
|
||||
@@ -848,9 +849,11 @@ pub async fn update_managed_agent(
|
||||
// read-time. A name-only edit (relay_url == None) leaves the pin intact.
|
||||
if let Some(relay_url) = input.relay_url {
|
||||
let relay_url = relay_url.trim().to_string();
|
||||
if !relay_url.is_empty() {
|
||||
crate::managed_agents::validate_local_agent_relay(&record.backend, &relay_url)?;
|
||||
}
|
||||
crate::managed_agents::validate_effective_local_agent_relay(
|
||||
&record.backend,
|
||||
&relay_url,
|
||||
&workspace_relay_url,
|
||||
)?;
|
||||
record.relay_url = relay_url;
|
||||
}
|
||||
if let Some(acp_command) = input.acp_command {
|
||||
@@ -908,6 +911,13 @@ pub async fn update_managed_agent(
|
||||
|
||||
record.updated_at = now_iso();
|
||||
|
||||
if name_changed {
|
||||
crate::managed_agents::validate_effective_local_agent_relay(
|
||||
&record.backend,
|
||||
&record.relay_url,
|
||||
&workspace_relay_url,
|
||||
)?;
|
||||
}
|
||||
save_managed_agents(&app, &records)?;
|
||||
|
||||
let record = records
|
||||
|
||||
@@ -588,6 +588,7 @@ pub async fn create_managed_agent(
|
||||
// Snapshot the workspace owner pubkey for the legacy-record auth_tag
|
||||
// fallback. Computed outside the records lock to keep lock ordering simple.
|
||||
let owner_hex = workspace_owner_hex(&state)?;
|
||||
let workspace_relay_url = relay_ws_url_with_override(&state);
|
||||
|
||||
// ── Phase 1: generate keys (sync lock) ────────────────────────────────────
|
||||
let (agent_keys, private_key_nsec, pubkey, resolved_relay_url, input) = {
|
||||
@@ -632,9 +633,11 @@ pub async fn create_managed_agent(
|
||||
.map(str::trim)
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
if !resolved_relay_url.is_empty() {
|
||||
crate::managed_agents::validate_local_agent_relay(&input.backend, &resolved_relay_url)?;
|
||||
}
|
||||
crate::managed_agents::validate_effective_local_agent_relay(
|
||||
&input.backend,
|
||||
&resolved_relay_url,
|
||||
&workspace_relay_url,
|
||||
)?;
|
||||
|
||||
(keys, private_key_nsec, pubkey, resolved_relay_url, input)
|
||||
};
|
||||
|
||||
@@ -838,6 +838,7 @@ pub async fn change_channel_member_role(
|
||||
channel_id: String,
|
||||
pubkey: String,
|
||||
role: String,
|
||||
app: AppHandle,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<(), String> {
|
||||
let uuid = parse_channel_uuid(&channel_id)?;
|
||||
@@ -848,6 +849,12 @@ pub async fn change_channel_member_role(
|
||||
"owner" => return Err("cannot assign owner role — use transfer ownership".into()),
|
||||
other => return Err(format!("invalid role: {other}")),
|
||||
};
|
||||
let relay_url = crate::relay::relay_ws_url_with_override(&state);
|
||||
crate::managed_agents::validate_local_agent_members_from_store(
|
||||
std::slice::from_ref(&pubkey),
|
||||
&relay_url,
|
||||
|| crate::managed_agents::load_managed_agents(&app),
|
||||
)?;
|
||||
let builder = events::build_add_member(uuid, &pubkey, Some(role_str))?;
|
||||
submit_event(builder, &state).await?;
|
||||
Ok(())
|
||||
|
||||
@@ -12,8 +12,8 @@ pub(crate) use agent_env::{
|
||||
baked_build_env, build_buzz_agent_provider_defaults, discovery_env_with_baked_floor,
|
||||
};
|
||||
pub(crate) use relay_policy::{
|
||||
validate_local_agent_members_from_store, validate_local_agent_relay,
|
||||
validate_managed_agent_relay_pin,
|
||||
validate_effective_local_agent_relay, validate_local_agent_members_from_store,
|
||||
validate_local_agent_relay, validate_managed_agent_relay_pin,
|
||||
};
|
||||
mod backend;
|
||||
pub(crate) mod config_bridge;
|
||||
|
||||
@@ -80,6 +80,34 @@ where
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn validate_effective_local_agent_relay(
|
||||
backend: &BackendKind,
|
||||
relay_pin: &str,
|
||||
workspace_relay_url: &str,
|
||||
) -> Result<(), String> {
|
||||
validate_effective_local_agent_relay_with_policy(
|
||||
backend,
|
||||
relay_pin,
|
||||
workspace_relay_url,
|
||||
super::internal_build(),
|
||||
baked_allowlist,
|
||||
)
|
||||
}
|
||||
|
||||
fn validate_effective_local_agent_relay_with_policy<F>(
|
||||
backend: &BackendKind,
|
||||
relay_pin: &str,
|
||||
workspace_relay_url: &str,
|
||||
internal: bool,
|
||||
allowlist: F,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
F: FnOnce() -> Result<Vec<String>, String>,
|
||||
{
|
||||
let effective = crate::relay::effective_agent_relay_url(relay_pin, workspace_relay_url);
|
||||
validate_local_agent_relay_with_policy(backend, &effective, internal, allowlist)
|
||||
}
|
||||
|
||||
/// Validate a legacy explicit record pin at persistence boundaries. Empty pins
|
||||
/// are workspace-relative and are checked when their effective relay is known.
|
||||
pub(crate) fn validate_managed_agent_relay_pin(record: &ManagedAgentRecord) -> Result<(), String> {
|
||||
@@ -219,6 +247,44 @@ mod tests {
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_pin_validates_the_effective_workspace_relay() {
|
||||
assert!(validate_effective_local_agent_relay_with_policy(
|
||||
&BackendKind::Local,
|
||||
"",
|
||||
"wss://public.example",
|
||||
true,
|
||||
allowlist,
|
||||
)
|
||||
.is_err());
|
||||
assert!(validate_effective_local_agent_relay_with_policy(
|
||||
&BackendKind::Local,
|
||||
"",
|
||||
"wss://buzz.block.builderlab.xyz",
|
||||
true,
|
||||
allowlist,
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_relay_validation_skips_oss_and_provider_backends() {
|
||||
let provider = BackendKind::Provider {
|
||||
id: "provider".into(),
|
||||
config: serde_json::json!({}),
|
||||
};
|
||||
for (backend, internal) in [(&BackendKind::Local, false), (&provider, true)] {
|
||||
assert!(validate_effective_local_agent_relay_with_policy(
|
||||
backend,
|
||||
"",
|
||||
"not-even-a-relay",
|
||||
internal,
|
||||
|| Err("allowlist must not load".into()),
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn member_enrollment_validates_only_matching_local_agents() {
|
||||
let provider = BackendKind::Provider {
|
||||
@@ -251,6 +317,15 @@ mod tests {
|
||||
assert_eq!(calls.get(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn role_change_rejects_matching_local_agent_before_membership_emit() {
|
||||
let records = vec![record("local", BackendKind::Local)];
|
||||
let result = validate_local_agent_members_with(&records, &["LOCAL".into()], |backend| {
|
||||
validate_local_agent_relay_with_policy(backend, "wss://public.example", true, allowlist)
|
||||
});
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oss_member_enrollment_does_not_load_the_agent_store() {
|
||||
let loads = std::cell::Cell::new(0);
|
||||
|
||||
Reference in New Issue
Block a user