Skip to content

Commit 1f76692

Browse files
committed
Port armor to http-types
1 parent 178679b commit 1f76692

File tree

4 files changed

+594
-1
lines changed

4 files changed

+594
-1
lines changed

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@
9393
//! differently. But as a rule: if you know the size of the body, it's usually more efficient to
9494
//! declare it up front. But if you don't, things will still work.
9595
96-
#![forbid(rust_2018_idioms)]
9796
#![deny(missing_debug_implementations, nonstandard_style)]
9897
#![warn(missing_docs, missing_doc_code_examples, unreachable_pub)]
9998
#![cfg_attr(test, deny(warnings))]
@@ -152,6 +151,8 @@ pub use crate::cookies::Cookie;
152151
#[doc(inline)]
153152
pub mod trailers;
154153

154+
pub mod security;
155+
155156
#[cfg(feature = "hyperium_http")]
156157
mod hyperium_http;
157158

src/security/csp.rs

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
use crate::Headers;
2+
use serde::Serialize;
3+
use std::collections::HashMap;
4+
use std::fmt;
5+
6+
/// Define source value
7+
///
8+
/// [read more](https://content-security-policy.com)
9+
#[derive(Debug)]
10+
pub enum Source {
11+
/// Set source `'self'`
12+
SameOrigin,
13+
/// Set source `'src'`
14+
SRC,
15+
/// Set source `'none'`
16+
None,
17+
/// Set source `'unsafe-inline'`
18+
UnsafeInline,
19+
/// Set source `data:`
20+
Data,
21+
/// Set source `mediastream:`
22+
Mediastream,
23+
/// Set source `https:`
24+
HTTPS,
25+
/// Set source `blob:`
26+
Blob,
27+
/// Set source `filesystem:`
28+
Filesystem,
29+
/// Set source `'strict-dynamic'`
30+
StrictDynamic,
31+
/// Set source `'unsafe-eval'`
32+
UnsafeEval,
33+
/// Set source `*`
34+
Wildcard,
35+
}
36+
37+
impl fmt::Display for Source {
38+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39+
match *self {
40+
Source::SameOrigin => write!(f, "'self'"),
41+
Source::SRC => write!(f, "'src'"),
42+
Source::None => write!(f, "'none'"),
43+
Source::UnsafeInline => write!(f, "'unsafe-inline'"),
44+
Source::Data => write!(f, "data:"),
45+
Source::Mediastream => write!(f, "mediastream:"),
46+
Source::HTTPS => write!(f, "https:"),
47+
Source::Blob => write!(f, "blob:"),
48+
Source::Filesystem => write!(f, "filesystem:"),
49+
Source::StrictDynamic => write!(f, "'strict-dynamic'"),
50+
Source::UnsafeEval => write!(f, "'unsafe-eval'"),
51+
Source::Wildcard => write!(f, "*"),
52+
}
53+
}
54+
}
55+
56+
impl AsRef<str> for Source {
57+
fn as_ref(&self) -> &str {
58+
match *self {
59+
Source::SameOrigin => "'self'",
60+
Source::SRC => "'src'",
61+
Source::None => "'none'",
62+
Source::UnsafeInline => "'unsafe-inline'",
63+
Source::Data => "data:",
64+
Source::Mediastream => "mediastream:",
65+
Source::HTTPS => "https:",
66+
Source::Blob => "blob:",
67+
Source::Filesystem => "filesystem:",
68+
Source::StrictDynamic => "'strict-dynamic'",
69+
Source::UnsafeEval => "'unsafe-eval'",
70+
Source::Wildcard => "*",
71+
}
72+
}
73+
}
74+
75+
/// Define `report-to` directive value
76+
///
77+
/// [MDN | report-to](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to)
78+
#[derive(Serialize, Debug)]
79+
pub struct ReportTo {
80+
#[serde(skip_serializing_if = "Option::is_none")]
81+
group: Option<String>,
82+
max_age: i32,
83+
endpoints: Vec<ReportToEndpoint>,
84+
#[serde(skip_serializing_if = "Option::is_none")]
85+
include_subdomains: Option<bool>,
86+
}
87+
88+
/// Define `endpoints` for `report-to` directive value
89+
///
90+
/// [MDN | report-to](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to)
91+
#[derive(Serialize, Debug)]
92+
pub struct ReportToEndpoint {
93+
url: String,
94+
}
95+
96+
/// Build a `Content-Security-Policy` header.
97+
///
98+
/// `Content-Security-Policy` (CSP) HTTP headers are used to prevent cross-site
99+
/// injections. [Read more](https://helmetjs.github.io/docs/csp/)
100+
///
101+
/// [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
102+
///
103+
/// # Examples
104+
///
105+
/// ```
106+
/// let mut policy = http_types::security::new()
107+
/// .default_src(http_types::security::Source::SameOrigin)
108+
/// .default_src("areweasyncyet.rs")
109+
/// .script_src(http_types::security::Source::SameOrigin)
110+
/// .object_src(http_types::security::Source::None)
111+
/// .base_uri(http_types::security::Source::None)
112+
/// .upgrade_insecure_requests();
113+
///
114+
/// let mut headers = http::Headers::new();
115+
/// policy.apply(&mut headers);
116+
///
117+
/// assert_eq!(headers["content-security-policy"], "base-uri 'none'; default-src 'self' areweasyncyet.rs; object-src 'none'; script-src 'self'; upgrade-insecure-requests");
118+
/// ```
119+
120+
#[derive(Debug)]
121+
pub struct ContentSecurityPolicy {
122+
policy: Vec<String>,
123+
report_only_flag: bool,
124+
directives: HashMap<String, Vec<String>>,
125+
}
126+
127+
impl Default for ContentSecurityPolicy {
128+
/// Sets the Content-Security-Policy default to "script-src 'self'; object-src 'self'"
129+
fn default() -> Self {
130+
let policy = String::from("script-src 'self'; object-src 'self'");
131+
ContentSecurityPolicy {
132+
policy: vec![policy],
133+
report_only_flag: false,
134+
directives: HashMap::new(),
135+
}
136+
}
137+
}
138+
139+
impl ContentSecurityPolicy {
140+
/// Create a new instance.
141+
pub fn new() -> Self {
142+
Self {
143+
policy: Vec::new(),
144+
report_only_flag: false,
145+
directives: HashMap::new(),
146+
}
147+
}
148+
149+
fn insert_directive<T: AsRef<str>>(&mut self, directive: &str, source: T) {
150+
let directive = String::from(directive);
151+
let directives = self.directives.entry(directive).or_insert_with(Vec::new);
152+
let source: String = source.as_ref().to_string();
153+
directives.push(source);
154+
}
155+
156+
/// Defines the Content-Security-Policy `base-uri` directive
157+
///
158+
/// [MDN | base-uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/base-uri)
159+
pub fn base_uri<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
160+
self.insert_directive("base-uri", source);
161+
self
162+
}
163+
164+
/// Defines the Content-Security-Policy `block-all-mixed-content` directive
165+
///
166+
/// [MDN | block-all-mixed-content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/block-all-mixed-content)
167+
pub fn block_all_mixed_content(&mut self) -> &mut Self {
168+
let policy = String::from("block-all-mixed-content");
169+
self.policy.push(policy);
170+
self
171+
}
172+
173+
/// Defines the Content-Security-Policy `connect-src` directive
174+
///
175+
/// [MDN | connect-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src)
176+
pub fn connect_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
177+
self.insert_directive("connect-src", source);
178+
self
179+
}
180+
181+
/// Defines the Content-Security-Policy `default-src` directive
182+
///
183+
/// [MDN | default-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src)
184+
pub fn default_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
185+
self.insert_directive("default-src", source);
186+
self
187+
}
188+
189+
/// Defines the Content-Security-Policy `font-src` directive
190+
///
191+
/// [MDN | font-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/font-src)
192+
pub fn font_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
193+
self.insert_directive("font-src", source);
194+
self
195+
}
196+
197+
/// Defines the Content-Security-Policy `form-action` directive
198+
///
199+
/// [MDN | form-action](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/form-action)
200+
pub fn form_action<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
201+
self.insert_directive("form-action", source);
202+
self
203+
}
204+
205+
/// Defines the Content-Security-Policy `frame-ancestors` directive
206+
///
207+
/// [MDN | frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
208+
pub fn frame_ancestors<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
209+
self.insert_directive("frame-ancestors", source);
210+
self
211+
}
212+
213+
/// Defines the Content-Security-Policy `frame-src` directive
214+
///
215+
/// [MDN | frame-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src)
216+
pub fn frame_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
217+
self.insert_directive("frame-src", source);
218+
self
219+
}
220+
221+
/// Defines the Content-Security-Policy `img-src` directive
222+
///
223+
/// [MDN | img-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/img-src)
224+
pub fn img_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
225+
self.insert_directive("img-src", source);
226+
self
227+
}
228+
229+
/// Defines the Content-Security-Policy `media-src` directive
230+
///
231+
/// [MDN | media-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/media-src)
232+
pub fn media_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
233+
self.insert_directive("media-src", source);
234+
self
235+
}
236+
237+
/// Defines the Content-Security-Policy `object-src` directive
238+
///
239+
/// [MDN | object-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/object-src)
240+
pub fn object_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
241+
self.insert_directive("object-src", source);
242+
self
243+
}
244+
245+
/// Defines the Content-Security-Policy `plugin-types` directive
246+
///
247+
/// [MDN | plugin-types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/plugin-types)
248+
pub fn plugin_types<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
249+
self.insert_directive("plugin-types", source);
250+
self
251+
}
252+
253+
/// Defines the Content-Security-Policy `require-sri-for` directive
254+
///
255+
/// [MDN | require-sri-for](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-sri-for)
256+
pub fn require_sri_for<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
257+
self.insert_directive("require-sri-for ", source);
258+
self
259+
}
260+
261+
/// Defines the Content-Security-Policy `report-uri` directive
262+
///
263+
/// [MDN | report-uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri)
264+
pub fn report_uri<T: AsRef<str>>(&mut self, uri: T) -> &mut Self {
265+
self.insert_directive("report-uri", uri);
266+
self
267+
}
268+
269+
/// Defines the Content-Security-Policy `report-to` directive
270+
///
271+
/// [MDN | report-to](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to)
272+
pub fn report_to(&mut self, endpoints: Vec<ReportTo>) -> &mut Self {
273+
for endpoint in endpoints.iter() {
274+
match serde_json::to_string(&endpoint) {
275+
Ok(json) => {
276+
let policy = format!("report-to {}", json);
277+
self.policy.push(policy);
278+
}
279+
Err(error) => {
280+
println!("{:?}", error);
281+
}
282+
}
283+
}
284+
self
285+
}
286+
287+
/// Defines the Content-Security-Policy `sandbox` directive
288+
///
289+
/// [MDN | sandbox](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox)
290+
pub fn sandbox<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
291+
self.insert_directive("sandbox", source);
292+
self
293+
}
294+
295+
/// Defines the Content-Security-Policy `script-src` directive
296+
///
297+
/// [MDN | script-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src)
298+
pub fn script_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
299+
self.insert_directive("script-src", source);
300+
self
301+
}
302+
303+
/// Defines the Content-Security-Policy `style-src` directive
304+
///
305+
/// [MDN | style-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src)
306+
pub fn style_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
307+
self.insert_directive("style-src", source);
308+
self
309+
}
310+
311+
/// Defines the Content-Security-Policy `upgrade-insecure-requests` directive
312+
///
313+
/// [MDN | upgrade-insecure-requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests)
314+
pub fn upgrade_insecure_requests(&mut self) -> &mut Self {
315+
let policy = String::from("upgrade-insecure-requests");
316+
self.policy.push(policy);
317+
self
318+
}
319+
320+
/// Defines the Content-Security-Policy `worker-src` directive
321+
///
322+
/// [MDN | worker-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/worker-src)
323+
pub fn worker_src<T: AsRef<str>>(&mut self, source: T) -> &mut Self {
324+
self.insert_directive("worker-src", source);
325+
self
326+
}
327+
328+
/// Change the header to `Content-Security-Policy-Report-Only`
329+
pub fn report_only(&mut self) -> &mut Self {
330+
self.report_only_flag = true;
331+
self
332+
}
333+
334+
/// Create and retrieve the policy value
335+
fn value(&mut self) -> String {
336+
for (directive, sources) in &self.directives {
337+
let policy = format!("{} {}", directive, sources.join(" "));
338+
self.policy.push(policy);
339+
self.policy.sort();
340+
}
341+
self.policy.join("; ")
342+
}
343+
344+
/// Sets the `Content-Security-Policy` (CSP) HTTP header to prevent cross-site injections
345+
pub fn apply(&mut self, mut headers: impl AsMut<Headers>) {
346+
let name = if self.report_only_flag {
347+
"Content-Security-Policy-Report-Only"
348+
} else {
349+
"Content-Security-Policy"
350+
};
351+
headers
352+
.as_mut()
353+
.insert(name, self.value().to_owned())
354+
.unwrap();
355+
}
356+
}

0 commit comments

Comments
 (0)