//! In adblocking terms, [`Resource`]s are special placeholder scripts, images, //! video files, etc. that can be returned as drop-in replacements for harmful //! equivalents from remote servers. Resources also encompass scriptlets, which //! can be injected into pages to inhibit malicious behavior. //! //! If the `resource-assembler` feature is enabled, the #![cfg_attr(not(feature = "resource-assembler"), doc = "`resource_assembler`")] #![cfg_attr(feature = "resource-assembler", doc = "[`resource_assembler`]")] //! module will assist with the construction of [`Resource`]s directly from the uBlock Origin //! project.
mod resource_storage; pub(crate) use resource_storage::parse_scriptlet_args; #[doc(inline)] pubuse resource_storage::{
AddResourceError, InMemoryResourceStorage, ResourceImpl, ResourceStorage,
ResourceStorageBackend, ScriptletResourceError,
};
use memchr::memrchr as find_char_reverse; use serde::{Deserialize, Serialize};
/// Specifies a set of permissions required to inject a scriptlet resource. /// /// Permissions can be specified when parsing individual lists using [`crate::FilterSet`] in /// order to propagate the permission level to all filters contained in the list. /// /// In practice, permissions are used to limit the risk of third-party lists having access to /// powerful scriptlets like uBlock Origin's `trusted-set-cookie`, which has the ability to set /// arbitrary cookies to arbitrary values on visited sites. /// /// ### Example /// /// ``` /// # use adblock::Engine; /// # use adblock::lists::ParseOptions; /// # use adblock::resources::{MimeType, PermissionMask, Resource, ResourceStorage, ResourceType}; /// # let mut filter_set = adblock::lists::FilterSet::default(); /// # let untrusted_filters = vec![""]; /// # let trusted_filters = vec![""]; /// const COOKIE_ACCESS: PermissionMask = PermissionMask::from_bits(0b00000001); /// const LOCALSTORAGE_ACCESS: PermissionMask = PermissionMask::from_bits(0b00000010); /// /// // `untrusted_filters` will not be able to use privileged scriptlet injections. /// filter_set.add_filters( /// untrusted_filters, /// Default::default(), /// ); /// // `trusted_filters` will be able to inject scriptlets requiring `COOKIE_ACCESS` /// // permissions or `LOCALSTORAGE_ACCESS` permissions. /// filter_set.add_filters( /// trusted_filters, /// ParseOptions { /// permissions: COOKIE_ACCESS | LOCALSTORAGE_ACCESS, /// ..Default::default() /// }, /// ); /// /// let mut engine = Engine::from_filter_set(filter_set, true); /// // The `trusted-set-cookie` scriptlet cannot be injected without `COOKIE_ACCESS` /// // permission. /// engine.use_resources([Resource { /// name: "trusted-set-cookie.js".to_string(), /// aliases: vec![], /// kind: ResourceType::Mime(MimeType::ApplicationJavascript), /// content: base64::encode("document.cookie = '...';"), /// dependencies: vec![], /// permission: COOKIE_ACCESS, /// }]); /// ``` #[derive(Serialize, Deserialize, Clone, Copy, Default)] #[repr(transparent)] #[serde(transparent)] pubstruct PermissionMask(u8);
impl PermissionMask { /// Construct a new [`PermissionMask`] with the given bitmask. Use /// [`PermissionMask::default()`] instead if you don't want to restrict or grant any /// permissions. pubconstfn from_bits(bits: u8) -> Self { Self(bits)
}
pubfn to_bits(&self) -> u8 { self.0
}
/// Can `filter_mask` authorize injecting a resource requiring `self` permissions? pubfn is_injectable_by(&self, filter_mask: PermissionMask) -> bool { // For any particular bit index, the scriptlet is injectable if: // (there is a requirement, AND the filter meets it) OR (there's no requirement) // in other words: // (self & filter_mask) | (!self) == 1 // (self | !self) & (filter_mask | !self) == 1 // filter_mask | !self == 1 // !(filter_mask | !self) == 0 // !filter_mask & self == 0 // which we can compare across *all* bits using bitwise operations, hence:
!filter_mask.0 & self.0 == 0
}
/// The default value for [`PermissionMask`] is one which provides no additional permissions. fn is_default(&self) -> bool { self.0 == 0
}
}
/// Struct representing a resource that can be used by an adblocking engine. #[derive(Serialize, Deserialize, Clone)] pubstruct Resource { /// Represents the primary name of the resource, often a filename pub name: String, /// Represents secondary names that can be used to access the resource #[serde(default)] pub aliases: Vec<String>, /// How to interpret the resource data within `content` pub kind: ResourceType, /// The resource data, encoded using standard base64 configuration pub content: String, /// Optionally contains the name of any dependencies used by this resource. Currently, this /// only applies to `application/javascript` and `fn/javascript` MIME types. /// /// Aliases should never be added to this list. It should only contain primary/canonical /// resource names. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub dependencies: Vec<String>, /// Optionally defines permission levels required to use this resource for a scriptlet /// injection. See [`PermissionMask`] for more details. /// /// If there is any customized permission, this resource cannot be used for redirects. /// /// This field is similar to the `requiresTrust` field from uBlock Origin's scriptlet /// resources, except that it supports up to 8 different trust "domains". #[serde(default, skip_serializing_if = "PermissionMask::is_default")] pub permission: PermissionMask,
}
impl Resource { /// Convenience constructor for tests. Creates a new [`Resource`] with no aliases or /// dependencies. Content will be automatically base64-encoded by the constructor. #[cfg(test)] pubfn simple(name: &str, kind: MimeType, content: &str) -> Self { use base64::{engine::Engine as _, prelude::BASE64_STANDARD}; Self {
name: name.to_string(),
aliases: vec![],
kind: ResourceType::Mime(kind),
content: BASE64_STANDARD.encode(content),
dependencies: vec![],
permission: Default::default(),
}
}
}
/// Different ways that the data within the `content` field of a `Resource` can be interpreted. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "lowercase")] pubenum ResourceType { /// Interpret the data according to the MIME type represented by `type`
Mime(MimeType), /// Interpret the data as a Javascript scriptlet template, with embedded template /// parameters in the form of `{{1}}`, `{{2}}`, etc. Note that `Mime(ApplicationJavascript)` /// can still be used as a templated resource, for compatibility purposes.
Template,
}
impl ResourceType { /// Can resources of this type be used as network redirects? pubfn supports_redirect(&self) -> bool {
!matches!( self,
ResourceType::Template | ResourceType::Mime(MimeType::FnJavascript)
)
}
/// Can resources of this type be used for scriptlet injections? pubfn supports_scriptlet_injection(&self) -> bool {
matches!( self,
ResourceType::Template | ResourceType::Mime(MimeType::ApplicationJavascript)
)
}
}
/// Acceptable MIME types for resources used by `$redirect` and `+js(...)` adblock rules. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(into = "&str")] #[serde(from = "std::borrow::Cow<'static, str>")] pubenum MimeType { /// `"text/css"`
TextCss, /// `"image/gif"`
ImageGif, /// `"text/html"`
TextHtml, /// `"application/javascript"`
ApplicationJavascript, /// `"application/json"`
ApplicationJson, /// `"audio/mp3"`
AudioMp3, /// `"video/mp4"`
VideoMp4, /// `"image/png"`
ImagePng, /// `"text/plain"`
TextPlain, /// `"text/xml"`
TextXml, /// Custom MIME type invented for the uBlock Origin project. Represented by `"fn/javascript"`. /// Used to describe JavaScript functions that can be used as dependencies of other JavaScript /// resources.
FnJavascript, /// Any other unhandled MIME type. Maps to `"application/octet-stream"` when re-serialized.
Unknown,
}
impl MimeType { /// Infers a resource's MIME type according to the extension of its path pubfn from_extension(resource_path: &str) -> Self { iflet Some(extension_index) = find_char_reverse(b'.', resource_path.as_bytes()) { match &resource_path[extension_index + 1..] { "css" => MimeType::TextCss, "gif" => MimeType::ImageGif, "html" => MimeType::TextHtml, "js" => MimeType::ApplicationJavascript, "json" => MimeType::ApplicationJson, "mp3" => MimeType::AudioMp3, "mp4" => MimeType::VideoMp4, "png" => MimeType::ImagePng, "txt" => MimeType::TextPlain, "xml" => MimeType::TextXml,
_ => { #[cfg(test)]
eprintln!("Unrecognized file extension on: {resource_path:?}");
MimeType::Unknown
}
}
} else {
MimeType::Unknown
}
}
/// Should the MIME type decode as valid UTF8? pubfn is_textual(&self) -> bool {
matches!( self, Self::ApplicationJavascript
| Self::FnJavascript
| Self::ApplicationJson
| Self::TextCss
| Self::TextPlain
| Self::TextHtml
| Self::TextXml
)
}
/// Can the MIME type have dependencies on other resources? pubfn supports_dependencies(&self) -> bool {
matches!(self, Self::ApplicationJavascript | Self::FnJavascript)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.