fix(permissions): validate network access flow

This commit is contained in:
2026-07-26 10:52:46 +02:00
parent 3047dc9195
commit 13fb538880
70 changed files with 4784 additions and 158 deletions

View File

@ -1,8 +1,8 @@
//! System-level agent permissions, distinct from filesystem/bash permissions.
//!
//! This model stores the policy the user wants IdeA to apply. It does not claim
//! that an external assistant runtime can be elevated live: resolution combines
//! the wanted policy with a read-only runtime probe.
//! that an external assistant runtime can be elevated live: resolution exposes
//! the wanted policy separately from the observed runtime state.
use serde::{Deserialize, Serialize};
@ -235,11 +235,22 @@ pub struct RuntimePermissionSnapshot {
pub effective_network: NetworkPolicy,
/// Runtime lock details.
pub runtime_lock: RuntimeLock,
/// Control state exposed to the UI.
pub control: SystemPermissionControl,
/// Runtime control state, distinct from editing the persisted wanted policy.
pub runtime_control: SystemPermissionControl,
}
impl RuntimePermissionSnapshot {
/// Snapshot for an agent with no known active runtime lock.
#[must_use]
pub fn unobserved() -> Self {
const REASON: &str = "No active runtime network permission state is currently observed.";
Self {
effective_network: NetworkPolicy::Deny,
runtime_lock: RuntimeLock::none(),
runtime_control: SystemPermissionControl::read_only(REASON),
}
}
/// Snapshot for a runtime that IdeA cannot inspect or elevate.
#[must_use]
pub fn locked_uninspectable() -> Self {
@ -248,7 +259,7 @@ impl RuntimePermissionSnapshot {
Self {
effective_network: NetworkPolicy::Deny,
runtime_lock: RuntimeLock::locked("external-runtime", REASON),
control: SystemPermissionControl::read_only(REASON),
runtime_control: SystemPermissionControl::read_only(REASON),
}
}
}
@ -263,8 +274,10 @@ pub struct ResolvedAgentSystemPermissions {
pub effective: NetworkPolicy,
/// Runtime lock state.
pub runtime_lock: RuntimeLock,
/// Whether the UI can edit the policy live.
/// Whether the UI can edit the persisted wanted policy.
pub control: SystemPermissionControl,
/// Whether IdeA can inspect or change the active runtime state.
pub runtime_control: SystemPermissionControl,
}
/// Resolves wanted system permissions against the runtime snapshot.
@ -283,7 +296,8 @@ pub fn resolve_agent_system_permissions(
wanted,
effective,
runtime_lock: runtime.runtime_lock,
control: runtime.control,
control: SystemPermissionControl::editable(),
runtime_control: runtime.runtime_control,
}
}
@ -306,7 +320,7 @@ mod tests {
}
#[test]
fn locked_runtime_controls_effective_policy_without_fake_allow() {
fn locked_runtime_controls_effective_policy_without_making_wanted_read_only() {
let agent = AgentId::new_random();
let doc = ProjectSystemPermissions::default();
@ -319,6 +333,31 @@ mod tests {
assert_eq!(resolved.wanted, None);
assert_eq!(resolved.effective, NetworkPolicy::Deny);
assert_eq!(resolved.runtime_lock.state, RuntimeLockState::Locked);
assert_eq!(resolved.control.mode, SystemPermissionControlMode::ReadOnly);
assert_eq!(resolved.control.mode, SystemPermissionControlMode::Editable);
assert_eq!(
resolved.runtime_control.mode,
SystemPermissionControlMode::ReadOnly
);
}
#[test]
fn unobserved_runtime_has_no_active_lock_and_preserves_wanted_effective_policy() {
let agent = AgentId::new_random();
let doc = ProjectSystemPermissions::new(
Some(SystemPermissionSet::new(Some(NetworkPolicy::Allow))),
vec![],
);
let resolved =
resolve_agent_system_permissions(&doc, agent, RuntimePermissionSnapshot::unobserved());
assert_eq!(resolved.wanted, Some(NetworkPolicy::Allow));
assert_eq!(resolved.effective, NetworkPolicy::Allow);
assert_eq!(resolved.runtime_lock.state, RuntimeLockState::None);
assert_eq!(resolved.control.mode, SystemPermissionControlMode::Editable);
assert_eq!(
resolved.runtime_control.mode,
SystemPermissionControlMode::ReadOnly
);
}
}