1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! Module for server content policy implementations.
use thiserror::Error;
use warg_crypto::hash::AnyHash;

mod wasm;

pub use wasm::*;

/// Represents a content policy error.
#[derive(Debug, Error)]
pub enum ContentPolicyError {
    /// The policy rejected the content with the given message.
    #[error("content was rejected by policy: {0}")]
    Rejection(String),
}

/// The result type returned by content policies.
pub type ContentPolicyResult<T> = Result<T, ContentPolicyError>;

/// A trait implemented by content policies.
pub trait ContentPolicy: Send + Sync {
    /// Creates a new stream policy for the given digest.
    ///
    /// The digest is provided so that a policy can make decisions
    /// based on the content's digest before any content is received.
    ///
    /// Upon success, returns a content stream policy that can be used
    /// to check the content as it is received.
    fn new_stream_policy(
        &self,
        digest: &AnyHash,
    ) -> ContentPolicyResult<Box<dyn ContentStreamPolicy>>;
}

/// A trait implemented by content stream policies.
pub trait ContentStreamPolicy: Send + Sync {
    /// Checks the given bytes of the content stream.
    ///
    /// The bytes represent the next chunk of data received for the
    /// content stream.
    fn check(&mut self, bytes: &[u8]) -> ContentPolicyResult<()>;

    /// Called when the content stream has finished.
    ///
    /// This method is called after all bytes have been received for
    /// the content stream.
    fn finalize(&mut self) -> ContentPolicyResult<()>;
}

/// Represents a collection of content policies.
///
/// Content policies are checked in order of their addition
/// to the collection.
#[derive(Default)]
pub struct ContentPolicyCollection {
    policies: Vec<Box<dyn ContentPolicy>>,
}

impl ContentPolicyCollection {
    /// Creates a new content policy collection.
    pub fn new() -> Self {
        Self::default()
    }

    /// Pushes a new content policy into the collection.
    pub fn push(&mut self, policy: impl ContentPolicy + 'static) {
        self.policies.push(Box::new(policy));
    }
}

impl ContentPolicy for ContentPolicyCollection {
    fn new_stream_policy(
        &self,
        digest: &AnyHash,
    ) -> ContentPolicyResult<Box<dyn ContentStreamPolicy>> {
        Ok(Box::new(ContentStreamPolicyCollection {
            policies: self
                .policies
                .iter()
                .map(|p| p.new_stream_policy(digest))
                .collect::<ContentPolicyResult<_>>()?,
        }))
    }
}

pub struct ContentStreamPolicyCollection {
    policies: Vec<Box<dyn ContentStreamPolicy>>,
}

impl ContentStreamPolicy for ContentStreamPolicyCollection {
    fn check(&mut self, bytes: &[u8]) -> ContentPolicyResult<()> {
        for policy in &mut self.policies {
            policy.check(bytes)?;
        }

        Ok(())
    }

    fn finalize(&mut self) -> ContentPolicyResult<()> {
        for policy in &mut self.policies {
            policy.finalize()?;
        }

        Ok(())
    }
}