pub mod allowlist;
pub mod http;
pub mod policy;
pub use allowlist::{DomainAllowlist, DomainPattern, DomainValidationResult};
pub use http::{CredentialResolver, EnvCredentialResolver, HttpProxy, NoCredentialResolver};
pub use policy::{
AllowAllDecider, DefaultPolicyDecider, DenyAllDecider, NetworkDecision, NetworkPolicyDecider,
NetworkRequest,
};
use std::sync::Arc;
use crate::sandbox::config::{
CredentialMapping, SandboxConfig, SandboxPolicy, default_credential_mappings,
};
use crate::sandbox::error::Result;
pub struct NetworkProxyBuilder {
allowlist: Vec<String>,
credential_mappings: Vec<CredentialMapping>,
credential_resolver: Arc<dyn CredentialResolver>,
policy: SandboxPolicy,
}
impl NetworkProxyBuilder {
pub fn new() -> Self {
Self {
allowlist: crate::sandbox::config::default_allowlist(),
credential_mappings: default_credential_mappings(),
credential_resolver: Arc::new(EnvCredentialResolver),
policy: SandboxPolicy::ReadOnly,
}
}
pub fn from_config(config: &SandboxConfig) -> Self {
Self {
allowlist: config.network_allowlist.clone(),
credential_mappings: default_credential_mappings(),
credential_resolver: Arc::new(EnvCredentialResolver),
policy: config.policy,
}
}
pub fn with_allowlist(mut self, domains: Vec<String>) -> Self {
self.allowlist = domains;
self
}
pub fn allow_domain(mut self, domain: &str) -> Self {
self.allowlist.push(domain.to_string());
self
}
pub fn with_credentials(mut self, mappings: Vec<CredentialMapping>) -> Self {
self.credential_mappings = mappings;
self
}
pub fn with_credential_resolver(mut self, resolver: Arc<dyn CredentialResolver>) -> Self {
self.credential_resolver = resolver;
self
}
pub fn with_policy(mut self, policy: SandboxPolicy) -> Self {
self.policy = policy;
self
}
pub fn build(self) -> HttpProxy {
let decider: Arc<dyn NetworkPolicyDecider> = if self.policy.has_full_network() {
Arc::new(AllowAllDecider)
} else {
Arc::new(DefaultPolicyDecider::new(
DomainAllowlist::new(&self.allowlist),
self.credential_mappings,
))
};
HttpProxy::new(decider, self.credential_resolver)
}
pub async fn build_and_start(self, port: u16) -> Result<HttpProxy> {
let proxy = self.build();
proxy.start(port).await?;
Ok(proxy)
}
}
impl Default for NetworkProxyBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_default() {
let builder = NetworkProxyBuilder::new();
assert!(!builder.allowlist.is_empty());
}
#[test]
fn test_builder_with_custom_allowlist() {
let builder = NetworkProxyBuilder::new()
.with_allowlist(vec!["custom.com".to_string()])
.allow_domain("another.com");
assert!(builder.allowlist.contains(&"custom.com".to_string()));
assert!(builder.allowlist.contains(&"another.com".to_string()));
}
#[tokio::test]
async fn test_builder_builds_proxy() {
let proxy = NetworkProxyBuilder::new()
.with_policy(SandboxPolicy::ReadOnly)
.build();
assert!(!proxy.is_running());
}
}