spin_capabilities/
deny.rs1use crate::{
2 AI_MODELS, ALLOWED_OUTBOUND_HOSTS, CAPABILITY_SETS, ENVIRONMENT, FILES, InheritConfiguration,
3 KEY_VALUE_STORES, SQLITE_DATABASES, VARIABLES,
4};
5use wac_graph::types::{SubtypeChecker, are_semver_compatible};
6use wac_graph::{CompositionGraph, types::Package};
7
8pub fn apply_deny_adapter(
21 source: &[u8],
22 inherits: InheritConfiguration,
23) -> anyhow::Result<Vec<u8>> {
24 let allow = allow_list(inherits);
25
26 const SPIN_DENY_ADAPTER_BYTES: &[u8] = include_bytes!("../deny_adapter.wasm");
27
28 let mut graph = CompositionGraph::new();
29
30 let dependency_package = Package::from_bytes("dependency", None, source, graph.types_mut())?;
31
32 let dependency_id = graph.register_package(dependency_package)?;
33
34 let deny_adapter_package = Package::from_bytes(
35 "spin-deny-all-adapter",
36 None,
37 SPIN_DENY_ADAPTER_BYTES,
38 graph.types_mut(),
39 )?;
40
41 let deny_adapter_id = graph.register_package(deny_adapter_package)?;
42
43 let socket_instantiation = graph.instantiate(dependency_id);
45
46 let mut plug_exports: Vec<(String, String)> = Vec::new();
47 let mut cache = Default::default();
48 let mut checker = SubtypeChecker::new(&mut cache);
49 for (name, plug_ty) in &graph.types()[graph[deny_adapter_id].ty()].exports {
50 if allow.iter().any(|a| *a == name) {
52 continue;
53 }
54
55 let matching_import = graph.types()[graph[dependency_id].ty()]
56 .imports
57 .get(name)
58 .map(|ty| (name.clone(), ty))
59 .or_else(|| {
60 graph.types()[graph[dependency_id].ty()]
61 .imports
62 .iter()
63 .find(|(import_name, _)| are_semver_compatible(name, import_name))
64 .map(|(import_name, ty)| (import_name.clone(), ty))
65 });
66
67 if let Some((socket_name, socket_ty)) = matching_import {
68 if checker
69 .is_subtype(*plug_ty, graph.types(), *socket_ty, graph.types())
70 .is_ok()
71 {
72 plug_exports.push((name.clone(), socket_name));
73 }
74 }
75 }
76
77 if plug_exports.is_empty() {
78 return Ok(source.to_vec());
80 }
81
82 let plug_instantiation = graph.instantiate(deny_adapter_id);
83 for (plug_name, socket_name) in plug_exports {
84 let export = graph.alias_instance_export(plug_instantiation, &plug_name)?;
85 graph.set_instantiation_argument(socket_instantiation, &socket_name, export)?;
86 }
87
88 for name in graph.types()[graph[dependency_id].ty()]
90 .exports
91 .keys()
92 .cloned()
93 .collect::<Vec<_>>()
94 {
95 let export = graph.alias_instance_export(socket_instantiation, &name)?;
96 graph.export(export, &name)?;
97 }
98
99 let bytes = graph.encode(Default::default())?;
100 Ok(bytes)
101}
102
103fn allow_list(inherits: InheritConfiguration) -> Vec<&'static str> {
104 let mut allow = vec![];
105
106 match inherits {
107 InheritConfiguration::All => {
108 for (_, capability_set) in CAPABILITY_SETS {
109 allow.extend_from_slice(capability_set);
110 }
111 }
112 InheritConfiguration::Some(inherits) => {
113 for config in inherits {
114 match config.as_str() {
115 "ai_models" => allow.extend_from_slice(AI_MODELS),
116 "allowed_outbound_hosts" => allow.extend_from_slice(ALLOWED_OUTBOUND_HOSTS),
117 "environment" => allow.extend_from_slice(ENVIRONMENT),
118 "files" => allow.extend_from_slice(FILES),
119 "key_value_stores" => allow.extend_from_slice(KEY_VALUE_STORES),
120 "sqlite_databases" => allow.extend_from_slice(SQLITE_DATABASES),
121 "variables" => allow.extend_from_slice(VARIABLES),
122 _ => {}
123 }
124 }
125 }
126 InheritConfiguration::None => {}
127 }
128
129 allow
130}