Skip to main content

untrusted_inputs_nested_structs/
untrusted_inputs_nested_structs.rs

1use untrusted_value::derive::untrusted_inputs;
2use untrusted_value::derive::UntrustedVariant;
3use untrusted_value::SanitizeValue;
4use untrusted_value_derive_internals::IntoUntrustedVariant;
5
6// note:
7// - trusted version: support debugs
8// - untrusted version: does not support debugs, since it may be unsafe to print the values
9// Since all sub structs implement `SanitizeValue`, the unsafe version can
10// use the `SanitizeValue` macro to automatically implement the `SanitizeValue` trait.
11#[derive(Debug, UntrustedVariant)] // <-- Implements `GeneralConfigUntrusted`
12#[untrusted_derive(Clone, SanitizeValue)]
13pub struct GeneralConfig {
14    pub network: NetworkConfig,
15    pub database: DatabaseConfig,
16}
17
18#[derive(Clone, Debug, UntrustedVariant)] // <-- Implements `NetworkConfigUntrusted`
19#[untrusted_derive(Clone, SanitizeValueEnd)]
20pub struct NetworkConfig {
21    pub port: u32,
22    pub listen_address: String,
23}
24
25#[derive(Clone, Debug, UntrustedVariant)]
26#[untrusted_derive(Clone, SanitizeValueEnd)]
27pub struct DatabaseConfig {}
28
29/// Sanitize the tainted version of `NetworkConfig`
30impl SanitizeValue<NetworkConfig> for NetworkConfigUntrusted {
31    type Error = ();
32
33    fn sanitize_value(self) -> Result<NetworkConfig, Self::Error> {
34        Ok(NetworkConfig {
35            port: self.port.use_untrusted_value(),
36            listen_address: self.listen_address.use_untrusted_value(),
37        }) // in real application: do some sanitizing
38    }
39}
40
41/// Sanitize the tainted version of `DatabaseConfig`
42impl SanitizeValue<DatabaseConfig> for DatabaseConfigUntrusted {
43    type Error = ();
44
45    fn sanitize_value(self) -> Result<DatabaseConfig, Self::Error> {
46        Ok(DatabaseConfig {}) // do some sanitizing
47    }
48}
49
50// suppose this function is called by a library such like Rocket/Poem ... when a HTTP request is received
51#[untrusted_inputs]
52fn response_from_database(config: GeneralConfig) -> Result<GeneralConfig, ()> {
53    // we can not use name directly, since it is
54    // wrapped in an UntrustedValue
55
56    config.sanitize_value()
57}
58
59fn main() {
60    // do a call to index route
61    assert!(response_from_database(GeneralConfig {
62        database: DatabaseConfig {},
63        network: NetworkConfig {
64            port: 3000,
65            listen_address: "<script>alert('xss')</script>".to_string(),
66        },
67    })
68    .is_ok());
69}