Skip to main content

ecat_security/
layer.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2use crate::{SecurityError, SecurityScanner, evaluate, request_parts};
3use http::Request;
4use std::future::Future;
5use std::pin::Pin;
6use std::sync::Arc;
7use std::task::{Context as TaskCtx, Poll};
8
9// ── Tower Layer ──
10
11#[derive(Clone)]
12pub struct SecurityLayer {
13    scanner: Arc<SecurityScanner>,
14}
15
16impl SecurityLayer {
17    pub fn new() -> Self {
18        Self {
19            scanner: Arc::new(SecurityScanner::new()),
20        }
21    }
22}
23
24impl Default for SecurityLayer {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl<S> tower::Layer<S> for SecurityLayer {
31    type Service = SecurityService<S>;
32
33    fn layer(&self, inner: S) -> Self::Service {
34        SecurityService {
35            inner,
36            scanner: Arc::clone(&self.scanner),
37        }
38    }
39}
40
41#[derive(Clone)]
42pub struct SecurityService<S> {
43    inner: S,
44    scanner: Arc<SecurityScanner>,
45}
46
47impl<S, B> tower::Service<Request<B>> for SecurityService<S>
48where
49    S: tower::Service<Request<B>> + Send + 'static,
50    S::Future: Send + 'static,
51    S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
52    // 拦截时直接返回 403 响应而非 Err:Err 经服务端 no_error 归一到
53    // Infallible 时触发 unreachable panic,会打崩 worker 线程
54    S::Response: From<axum::response::Response> + Send,
55    B: Send + 'static,
56{
57    type Response = S::Response;
58    type Error = SecurityError;
59    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
60
61    fn poll_ready(&mut self, cx: &mut TaskCtx<'_>) -> Poll<Result<(), Self::Error>> {
62        self.inner
63            .poll_ready(cx)
64            .map_err(|e| SecurityError::Inner(e.into()))
65    }
66
67    fn call(&mut self, req: Request<B>) -> Self::Future {
68        let scanner = Arc::clone(&self.scanner);
69        let parts = request_parts(&req);
70        let strings: Vec<&str> = parts.iter().map(|s| s.as_str()).collect();
71        let results = scanner.scan_parts(&strings);
72
73        if let Some(err) = evaluate(&results) {
74            use axum::response::IntoResponse;
75            let resp: S::Response = err.into_response().into();
76            return Box::pin(async move { Ok(resp) });
77        }
78
79        let fut = self.inner.call(req);
80        Box::pin(async move { fut.await.map_err(|e| SecurityError::Inner(e.into())) })
81    }
82}
83
84// ── Body-scanning variant ──
85
86/// Tower layer that scans URI, headers, **and** the request body.
87///
88/// The body is read exactly once (up to [`body_limit`](Self::body_limit)
89/// bytes) and passed through to the inner service, so handlers still receive
90/// the full payload. Use this instead of [`SecurityLayer`] when request
91/// bodies must also be checked for SQLi/XSS payloads.
92#[derive(Clone)]
93pub struct SecurityBodyLayer {
94    scanner: Arc<SecurityScanner>,
95    body_limit: usize,
96}
97
98impl SecurityBodyLayer {
99    pub fn new() -> Self {
100        Self {
101            scanner: Arc::new(SecurityScanner::new()),
102            body_limit: 10 * 1024 * 1024,
103        }
104    }
105
106    /// Maximum body size (in bytes) that will be buffered and scanned.
107    /// Larger bodies are rejected with a 413 (Payload Too Large) rather
108    /// than buffered.
109    pub fn body_limit(mut self, limit: usize) -> Self {
110        self.body_limit = limit;
111        self
112    }
113}
114
115impl Default for SecurityBodyLayer {
116    fn default() -> Self {
117        Self::new()
118    }
119}
120
121impl<S> tower::Layer<S> for SecurityBodyLayer {
122    type Service = SecurityBodyService<S>;
123
124    fn layer(&self, inner: S) -> Self::Service {
125        SecurityBodyService {
126            inner,
127            scanner: Arc::clone(&self.scanner),
128            body_limit: self.body_limit,
129        }
130    }
131}
132
133#[derive(Clone)]
134pub struct SecurityBodyService<S> {
135    inner: S,
136    scanner: Arc<SecurityScanner>,
137    body_limit: usize,
138}
139
140impl<S> tower::Service<Request<axum::body::Body>> for SecurityBodyService<S>
141where
142    S: tower::Service<Request<axum::body::Body>> + Clone + Send + 'static,
143    S::Future: Send + 'static,
144    S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
145{
146    type Response = S::Response;
147    type Error = SecurityError;
148    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
149
150    fn poll_ready(&mut self, cx: &mut TaskCtx<'_>) -> Poll<Result<(), Self::Error>> {
151        self.inner
152            .poll_ready(cx)
153            .map_err(|e| SecurityError::Inner(e.into()))
154    }
155
156    fn call(&mut self, req: Request<axum::body::Body>) -> Self::Future {
157        let scanner = Arc::clone(&self.scanner);
158        let body_limit = self.body_limit;
159        let mut inner = self.inner.clone();
160        let parts = request_parts(&req);
161        let strings: Vec<&str> = parts.iter().map(|s| s.as_str()).collect();
162        let header_results = scanner.scan_parts(&strings);
163
164        Box::pin(async move {
165            let (parts, body) = req.into_parts();
166            // Read the body exactly once; the collected bytes become the new
167            // body so downstream handlers can still access the payload.
168            let bytes = match axum::body::to_bytes(body, body_limit).await {
169                Ok(bytes) => bytes,
170                Err(e) => {
171                    // LengthLimitError = 超限:413;其余读体错误为 500
172                    let inner: Box<dyn std::error::Error + Send + Sync> = e.into_inner();
173                    if inner
174                        .downcast_ref::<http_body_util::LengthLimitError>()
175                        .is_some()
176                    {
177                        return Err(SecurityError::BodyTooLarge);
178                    }
179                    return Err(SecurityError::Inner(inner));
180                }
181            };
182
183            let mut results = header_results;
184            results.extend(scanner.scan_body(&bytes));
185
186            if let Some(err) = evaluate(&results) {
187                return Err(err);
188            }
189
190            let req = Request::from_parts(parts, axum::body::Body::from(bytes));
191            inner
192                .call(req)
193                .await
194                .map_err(|e| SecurityError::Inner(e.into()))
195        })
196    }
197}