Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Impressum mod.rs

  Sprache: Rust
 

//! 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.

#[cfg(feature = "resource-assembler")]
pub mod resource_assembler;

mod resource_storage;
pub(crateuse resource_storage::parse_scriptlet_args;
#[doc(inline)]
pub use 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)]
pub struct PermissionMask(u8);

impl std::fmt::Debug for PermissionMask {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "PermissionMask({:b})"self.0)
    }
}

impl core::ops::BitOr<PermissionMask> for PermissionMask {
    type Output = PermissionMask;

    fn bitor(self, rhs: PermissionMask) -> Self::Output {
        Self(self.0 | rhs.0)
    }
}

impl core::ops::BitOrAssign<PermissionMask> for PermissionMask {
    fn bitor_assign(&mut self, rhs: PermissionMask) {
        self.0 |= rhs.0;
    }
}

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.
    pub const fn from_bits(bits: u8) -> Self {
        Self(bits)
    }

    pub fn to_bits(&self) -> u8 {
        self.0
    }

    /// Can `filter_mask` authorize injecting a resource requiring `self` permissions?
    pub fn 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)]
pub struct 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)]
    pub fn 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")]
pub enum 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?
    pub fn supports_redirect(&self) -> bool {
        !matches!(
            self,
            ResourceType::Template | ResourceType::Mime(MimeType::FnJavascript)
        )
    }

    /// Can resources of this type be used for scriptlet injections?
    pub fn 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>")]
pub enum 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
    pub fn from_extension(resource_path: &str) -> Self {
        if let 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?
    pub fn 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?
    pub fn supports_dependencies(&self) -> bool {
        matches!(selfSelf::ApplicationJavascript | Self::FnJavascript)
    }
}

impl From<&str> for MimeType {
    fn from(v: &str) -> Self {
        match v {
            "text/css" => MimeType::TextCss,
            "image/gif" => MimeType::ImageGif,
            "text/html" => MimeType::TextHtml,
            "application/javascript" => MimeType::ApplicationJavascript,
            "application/json" => MimeType::ApplicationJson,
            "audio/mp3" => MimeType::AudioMp3,
            "video/mp4" => MimeType::VideoMp4,
            "image/png" => MimeType::ImagePng,
            "text/plain" => MimeType::TextPlain,
            "text/xml" => MimeType::TextXml,
            "fn/javascript" => MimeType::FnJavascript,
            _ => MimeType::Unknown,
        }
    }
}

impl From<&MimeType> for &str {
    fn from(v: &MimeType) -> Self {
        match v {
            MimeType::TextCss => "text/css",
            MimeType::ImageGif => "image/gif",
            MimeType::TextHtml => "text/html",
            MimeType::ApplicationJavascript => "application/javascript",
            MimeType::ApplicationJson => "application/json",
            MimeType::AudioMp3 => "audio/mp3",
            MimeType::VideoMp4 => "video/mp4",
            MimeType::ImagePng => "image/png",
            MimeType::TextPlain => "text/plain",
            MimeType::TextXml => "text/xml",
            MimeType::FnJavascript => "fn/javascript",
            MimeType::Unknown => "application/octet-stream",
        }
    }
}

// Required for `#[serde(from = "std::borrow::Cow<'static, str>")]`
impl From<std::borrow::Cow<'static, str>> for MimeType {
    fn from(v: std::borrow::Cow<'static, str>) -> Self {
        v.as_ref().into()
    }
}

// Required for `#[serde(into = &str)]`
impl From<MimeType> for &str {
    fn from(v: MimeType) -> Self {
        (&v).into()
    }
}

impl std::fmt::Display for MimeType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s: &str = self.into();
        write!(f, "{s}")
    }
}

#[cfg(test)]
mod permission_tests {
    use super::*;

    #[test]
    fn test_permissions() {
        {
            let resource = PermissionMask(0b00000000);
            assert!(resource.is_injectable_by(PermissionMask(0b00000000)));
            assert!(resource.is_injectable_by(PermissionMask(0b00000001)));
            assert!(resource.is_injectable_by(PermissionMask(0b00000010)));
            assert!(resource.is_injectable_by(PermissionMask(0b00000011)));
            assert!(resource.is_injectable_by(PermissionMask(0b10000000)));
            assert!(resource.is_injectable_by(PermissionMask(0b11111111)));
        }
        {
            let resource = PermissionMask(0b00000001);
            assert!(!resource.is_injectable_by(PermissionMask(0b00000000)));
            assert!(resource.is_injectable_by(PermissionMask(0b00000001)));
            assert!(!resource.is_injectable_by(PermissionMask(0b00000010)));
            assert!(resource.is_injectable_by(PermissionMask(0b00000011)));
            assert!(!resource.is_injectable_by(PermissionMask(0b10000000)));
            assert!(resource.is_injectable_by(PermissionMask(0b11111111)));
        }
        {
            let resource = PermissionMask(0b00000010);
            assert!(!resource.is_injectable_by(PermissionMask(0b00000000)));
            assert!(!resource.is_injectable_by(PermissionMask(0b00000001)));
            assert!(resource.is_injectable_by(PermissionMask(0b00000010)));
            assert!(resource.is_injectable_by(PermissionMask(0b00000011)));
            assert!(!resource.is_injectable_by(PermissionMask(0b10000000)));
            assert!(resource.is_injectable_by(PermissionMask(0b11111111)));
        }
        {
            let resource = PermissionMask(0b00000011);
            assert!(!resource.is_injectable_by(PermissionMask(0b00000000)));
            assert!(!resource.is_injectable_by(PermissionMask(0b00000001)));
            assert!(!resource.is_injectable_by(PermissionMask(0b00000010)));
            assert!(resource.is_injectable_by(PermissionMask(0b00000011)));
            assert!(!resource.is_injectable_by(PermissionMask(0b10000000)));
            assert!(resource.is_injectable_by(PermissionMask(0b11111111)));
        }
        {
            let resource = PermissionMask(0b10000011);
            assert!(!resource.is_injectable_by(PermissionMask(0b00000000)));
            assert!(!resource.is_injectable_by(PermissionMask(0b00000001)));
            assert!(!resource.is_injectable_by(PermissionMask(0b00000010)));
            assert!(!resource.is_injectable_by(PermissionMask(0b00000011)));
            assert!(!resource.is_injectable_by(PermissionMask(0b10000000)));
            assert!(resource.is_injectable_by(PermissionMask(0b11111111)));
        }
    }
}

Messung V0.5 in Prozent
C=74 H=93 G=83

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002