trace2e_core/traceability/
validation.rs

1//! Request validation and filtering for traceability operations.
2//!
3//! This module provides validation mechanisms to ensure that incoming requests
4//! contain valid, accessible resources before they are processed by the core
5//! traceability services. It acts as a gatekeeper to prevent invalid operations
6//! from reaching the business logic layer.
7//!
8//! ## Validation Categories
9//!
10//! **Process Validation**: Verifies that process IDs refer to actual running processes
11//! by querying the system process table.
12//!
13//! **Stream Validation**: Ensures that socket addresses are well-formed and compatible
14//! (e.g., both IPv4 or both IPv6) for network stream operations.
15//!
16//! ## Integration
17//!
18//! The validator is designed to work with the P2M service through the embedded validation
19//! feature. Invalid requests are rejected early with descriptive error messages before
20//! consuming system resources.
21
22use std::net::SocketAddr;
23
24use sysinfo::{Pid, System};
25
26/// Resource validator for P2M requests.
27///
28/// This validator ensures that incoming Process-to-Middleware requests reference
29/// valid, accessible system resources before they are processed by the core services.
30/// It performs system-level validation that cannot be done at the type level.
31///
32/// ## Validation Rules
33///
34/// - **Process IDs**: Must correspond to currently running processes
35/// - **Socket Addresses**: Must be parseable and use compatible address families
36/// - **File Descriptors**: Accepted without validation (OS handles validity)
37///
38/// ## Usage
39///
40/// The validator methods are called by the P2M service when resource validation
41/// is enabled through the `with_resource_validation(true)` builder method.
42#[derive(Debug, Clone)]
43pub struct ResourceValidator;
44
45impl ResourceValidator {
46    /// Validates that a process ID corresponds to a currently running process.
47    ///
48    /// Queries the system process table to verify that the specified PID
49    /// is associated with an active process. This prevents operations on
50    /// stale or invalid process identifiers.
51    ///
52    /// # Arguments
53    /// * `pid` - Process identifier to validate
54    ///
55    /// # Returns
56    /// `true` if the process exists and is accessible, `false` otherwise
57    pub fn is_valid_process(&self, pid: i32) -> bool {
58        let mut system = System::new();
59        system.refresh_all();
60        system.process(Pid::from(pid as usize)).is_some()
61    }
62
63    /// Validates that socket addresses are well-formed and compatible.
64    ///
65    /// Ensures that both local and peer socket addresses can be parsed
66    /// and use compatible address families (both IPv4 or both IPv6).
67    /// This prevents network operations with mismatched or invalid addresses.
68    ///
69    /// # Arguments
70    /// * `local_socket` - Local socket address string
71    /// * `peer_socket` - Peer socket address string
72    ///
73    /// # Returns
74    /// `true` if both addresses are valid and compatible, `false` otherwise
75    pub fn is_valid_stream(&self, local_socket: &str, peer_socket: &str) -> bool {
76        match (local_socket.parse::<SocketAddr>(), peer_socket.parse::<SocketAddr>()) {
77            (Ok(local_socket), Ok(peer_socket)) => {
78                (local_socket.is_ipv4() && peer_socket.is_ipv4())
79                    || (local_socket.is_ipv6() && peer_socket.is_ipv6())
80            }
81            _ => false,
82        }
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use tower::Service;
89
90    use crate::{
91        traceability::{
92            api::{P2mRequest, P2mResponse},
93            core::{
94                compliance::ComplianceService, provenance::ProvenanceService,
95                sequencer::SequencerService,
96            },
97            p2m::P2mApiService,
98        },
99        transport::nop::M2mNop,
100    };
101
102    #[tokio::test]
103    async fn unit_traceability_provenance_service_p2m_validator() {
104        let mut p2m_service = P2mApiService::new(
105            SequencerService::default(),
106            ProvenanceService::default(),
107            ComplianceService::default(),
108            M2mNop,
109        )
110        .with_resource_validation(true);
111
112        assert_eq!(
113            p2m_service
114                .call(P2mRequest::LocalEnroll { pid: 1, fd: 1, path: "test".to_string() })
115                .await
116                .unwrap(),
117            P2mResponse::Ack
118        );
119
120        assert_eq!(
121            p2m_service
122                .call(P2mRequest::RemoteEnroll {
123                    pid: 1,
124                    local_socket: "127.0.0.1:8080".to_string(),
125                    peer_socket: "127.0.0.1:8081".to_string(),
126                    fd: 1
127                })
128                .await
129                .unwrap(),
130            P2mResponse::Ack
131        );
132
133        let P2mResponse::Grant(flow_id) =
134            p2m_service.call(P2mRequest::IoRequest { pid: 1, fd: 1, output: true }).await.unwrap()
135        else {
136            panic!("Expected P2mResponse::Grant");
137        };
138        assert_eq!(
139            p2m_service
140                .call(P2mRequest::IoReport { pid: 1, fd: 1, grant_id: flow_id, result: true })
141                .await
142                .unwrap(),
143            P2mResponse::Ack
144        );
145
146        assert_eq!(
147            p2m_service
148                .call(P2mRequest::LocalEnroll { pid: 0, fd: 1, path: "test".to_string() }) // pid 0 is invalid
149                .await
150                .unwrap_err()
151                .to_string(),
152            "Traceability error, process not found (pid: 0)"
153        );
154
155        assert_eq!(
156            p2m_service
157                .call(P2mRequest::RemoteEnroll {
158                    pid: 1,
159                    local_socket: "bad_socket".to_string(),
160                    peer_socket: "bad_socket".to_string(),
161                    fd: 1
162                })
163                .await
164                .unwrap_err()
165                .to_string(),
166            "Traceability error, invalid stream (local_socket: bad_socket, peer_socket: bad_socket)"
167        );
168    }
169}