Initial commit

This commit is contained in:
Michael Pfaff 2021-01-20 21:50:36 -05:00
commit 60f07375ef
Signed by: michael
GPG Key ID: E53B118B12B5C7F9
5 changed files with 1801 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

1614
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "tide_csp"
version = "0.1.0"
authors = ["Michael Pfaff <michael@pfaff.dev>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tide = "0.15.0"

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# Tide Tracing
Simple middleware for Tide to add CSP headers without needing to know the syntax.

173
src/lib.rs Normal file
View File

@ -0,0 +1,173 @@
use core::fmt::Display;
use core::future::Future;
use core::pin::Pin;
use tide::Next;
use tide::Request;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Directive {
ChildSrc(Vec<String>),
ConnectSrc(Vec<String>),
DefaultSrc(Vec<String>),
FontSrc(Vec<String>),
FrameSrc(Vec<String>),
ImgSrc(Vec<String>),
ManifestSrc(Vec<String>),
MediaSrc(Vec<String>),
ObjectSrc(Vec<String>),
PrefetchSrc(Vec<String>),
ScriptSrc(Vec<String>),
ScriptSrcElem(Vec<String>),
ScriptSrcAttr(Vec<String>),
StyleSrc(Vec<String>),
StyleSrcElem(Vec<String>),
StyleSrcAttr(Vec<String>),
WorkerSrc(Vec<String>),
BaseUri(Vec<String>),
PluginTypes(Vec<String>),
Sandbox(Sandbox),
FormAction(Vec<String>),
FrameAncestors(Vec<String>),
NavigateTo(Vec<String>),
ReportTo(String),
RequireTrustedTypesFor(TrustedTypesTarget),
TrustedTypes(TrustedTypes),
UpgradeInsecureRequests,
}
impl Display for Directive {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
Self::ChildSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::ConnectSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::DefaultSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::FontSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::FrameSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::ImgSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::ManifestSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::MediaSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::ObjectSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::PrefetchSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::ScriptSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::ScriptSrcElem(sources) => write!(f, "{}", sources.join(" ")),
Self::ScriptSrcAttr(sources) => write!(f, "{}", sources.join(" ")),
Self::StyleSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::StyleSrcElem(sources) => write!(f, "{}", sources.join(" ")),
Self::StyleSrcAttr(sources) => write!(f, "{}", sources.join(" ")),
Self::WorkerSrc(sources) => write!(f, "{}", sources.join(" ")),
Self::BaseUri(base_uris) => write!(f, "{}", base_uris.join(" ")),
Self::PluginTypes(plugin_types) => write!(f, "{}", plugin_types.join(" ")),
Self::Sandbox(sandbox) => write!(f, "{}", sandbox),
Self::FormAction(form_actions) => write!(f, "{}", form_actions.join(" ")),
Self::FrameAncestors(frame_ancestors) => write!(f, "{}", frame_ancestors.join(" ")),
Self::NavigateTo(targets) => write!(f, "{}", targets.join(" ")),
Self::ReportTo(report_url) => write!(f, "report-to {}", report_url),
Self::RequireTrustedTypesFor(target) => {
write!(f, "require-trusted-types-for {}", target)
}
Self::TrustedTypes(trusted_types) => write!(f, "trusted-types {}", trusted_types),
Self::UpgradeInsecureRequests => write!(f, "upgrade-insecure-requests"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Sandbox {
AllowDownloadsWithoutUserActivation,
AllowForms,
AllowModals,
AllowOrienationLock,
AllowPointerLock,
AllowPopups,
AllowPopupsToEscapeSandbox,
AllowPresentation,
AllowSameOrigin,
AllowScripts,
AllowStorageAccessByUserActivation,
AllowTopNavigation,
AllowTopNavigationByUserActivation,
}
impl Display for Sandbox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(
f,
"{}",
match self {
Self::AllowDownloadsWithoutUserActivation =>
"allow-downloads-without-user-activation",
Self::AllowForms => "allow-forms",
Self::AllowModals => "allow-modals",
Self::AllowOrienationLock => "allow-orienation-lock",
Self::AllowPointerLock => "allow-pointer-lock",
Self::AllowPopups => "allow-popups",
Self::AllowPopupsToEscapeSandbox => "allow-popups-to-escape-sandbox",
Self::AllowPresentation => "allow-presentation",
Self::AllowSameOrigin => "allow-same-origin",
Self::AllowScripts => "allow-scripts",
Self::AllowStorageAccessByUserActivation =>
"allow-storage-access-by-user-activation",
Self::AllowTopNavigation => "allow-top-navigation",
Self::AllowTopNavigationByUserActivation =>
"allow-top-navigation-by-user-activation",
}
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TrustedTypesTarget {
Script,
}
impl Display for TrustedTypesTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "'script'")
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TrustedTypes {
None,
Values {
policy_names: Vec<String>,
allow_duplicates: bool,
},
}
impl Display for TrustedTypes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
Self::None => write!(f, "'none'"),
Self::Values {
policy_names,
allow_duplicates: true,
} => write!(f, "{} 'allow-duplicates'", policy_names.join(" ")),
Self::Values {
policy_names,
allow_duplicates: false,
} => write!(f, "{}", policy_names.join(" ")),
}
}
}
pub fn csp_middleware<'a, State: 'static + Clone + Send + Sync>(
directives: &'a [Directive],
) -> impl Fn(Request<State>, Next<'a, State>) -> Pin<Box<dyn Future<Output = tide::Result> + Send + 'a>>
{
move |req: Request<State>, next: Next<'a, State>| {
Box::pin(async move {
let mut res = next.run(req).await;
res.insert_header(
"Content-Security-Policy",
format!(
"{}",
directives
.iter()
.fold(String::new(), |a, b| a + &b.to_string() + ";")
),
);
Ok(res)
})
}
}