fix(permissions): validate network access flow
This commit is contained in:
@ -2,11 +2,15 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::{AgentSessionFactory, SessionPlan, StructuredSessionEnvironmentPreparer};
|
||||
use domain::ports::{
|
||||
AgentSessionFactory, SessionPlan, StructuredProviderLaunchPolicy,
|
||||
StructuredSessionEnvironmentPreparer, SystemPermissionStore,
|
||||
};
|
||||
use domain::profile::StructuredAdapter;
|
||||
use domain::AgentProfile;
|
||||
use domain::{
|
||||
AgentToolPolicy, AgentToolPolicyStore, AssistantContextProvider, DomainEvent, EventBus,
|
||||
IssueRef, IssueStore, ProfileId, ProfileStore, Project, SessionId,
|
||||
IssueRef, IssueStore, NetworkPolicy, ProfileId, ProfileStore, Project, SessionId,
|
||||
};
|
||||
|
||||
use crate::terminal::StructuredSessions;
|
||||
@ -22,6 +26,7 @@ pub struct OpenTicketAssistant {
|
||||
structured: Arc<StructuredSessions>,
|
||||
policies: Arc<dyn AgentToolPolicyStore>,
|
||||
events: Arc<dyn EventBus>,
|
||||
system_permissions: Option<Arc<dyn SystemPermissionStore>>,
|
||||
}
|
||||
|
||||
/// Input for [`OpenTicketAssistant`].
|
||||
@ -68,9 +73,17 @@ impl OpenTicketAssistant {
|
||||
structured,
|
||||
policies,
|
||||
events,
|
||||
system_permissions: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wires the project system permission store used for assistant launches.
|
||||
#[must_use]
|
||||
pub fn with_system_permission_store(mut self, store: Arc<dyn SystemPermissionStore>) -> Self {
|
||||
self.system_permissions = Some(store);
|
||||
self
|
||||
}
|
||||
|
||||
/// Executes the open flow.
|
||||
///
|
||||
/// Reopening an already-live assistant for the same ticket replaces the old
|
||||
@ -136,6 +149,9 @@ impl OpenTicketAssistant {
|
||||
return Err(AppError::from(err));
|
||||
}
|
||||
};
|
||||
let structured_policy = self
|
||||
.build_ticket_assistant_structured_policy(&input.project, &profile, &environment)
|
||||
.await?;
|
||||
let session = self
|
||||
.factory
|
||||
.start(
|
||||
@ -146,6 +162,9 @@ impl OpenTicketAssistant {
|
||||
Some(&requester),
|
||||
&environment.env,
|
||||
None,
|
||||
structured_policy
|
||||
.as_ref()
|
||||
.or(environment.structured_policy.as_ref()),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
@ -180,6 +199,38 @@ impl OpenTicketAssistant {
|
||||
issue_ref: input.issue_ref,
|
||||
})
|
||||
}
|
||||
|
||||
async fn build_ticket_assistant_structured_policy(
|
||||
&self,
|
||||
project: &Project,
|
||||
profile: &AgentProfile,
|
||||
environment: &domain::ports::StructuredSessionEnvironment,
|
||||
) -> Result<Option<StructuredProviderLaunchPolicy>, AppError> {
|
||||
if profile.structured_adapter != Some(StructuredAdapter::Codex) {
|
||||
return Ok(environment.structured_policy.clone());
|
||||
}
|
||||
let network = self.resolve_project_default_network(project).await?;
|
||||
Ok(Some(StructuredProviderLaunchPolicy::Codex {
|
||||
sandbox_mode: "workspace-write".to_owned(),
|
||||
writable_roots: vec![project.root.as_str().to_owned()],
|
||||
network_access: codex_network_access(network),
|
||||
}))
|
||||
}
|
||||
|
||||
async fn resolve_project_default_network(
|
||||
&self,
|
||||
project: &Project,
|
||||
) -> Result<Option<NetworkPolicy>, AppError> {
|
||||
let Some(store) = &self.system_permissions else {
|
||||
return Ok(None);
|
||||
};
|
||||
let doc = store.load_system_permissions(project).await?;
|
||||
Ok(doc.project_default.and_then(|set| set.network))
|
||||
}
|
||||
}
|
||||
|
||||
fn codex_network_access(network: Option<NetworkPolicy>) -> bool {
|
||||
matches!(network, Some(NetworkPolicy::Allow))
|
||||
}
|
||||
|
||||
fn ticket_assistant_requester(project: &Project, issue_ref: IssueRef) -> String {
|
||||
|
||||
Reference in New Issue
Block a user