use memchr::{memchr as find_char, memmem}; use once_cell::sync::Lazy; use regex::Regex; use serde::{Deserialize, Serialize};
use std::collections::HashSet; use std::convert::{TryFrom, TryInto};
/// By default, ABP rules do not block top-level document requests. There's no way to express that /// in content blocking format, so instead it's approximated with a rule that applies an exception /// to any first-party requests that are document types. /// /// This rule should be added after all other network rules. pubfn ignore_previous_fp_documents() -> CbRule { letmut resource_type = HashSet::new();
resource_type.insert(CbResourceType::Document);
CbRule {
trigger: CbTrigger {
url_filter: String::from(".*"),
resource_type: Some(resource_type),
load_type: vec![CbLoadType::FirstParty],
..CbTrigger::default()
},
action: CbAction {
typ: CbType::IgnorePreviousRules,
selector: None,
},
}
}
/// Rust representation of a single content blocking rule. /// /// This can be deserialized with `serde_json` directly into the correct format. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pubstruct CbRule { pub action: CbAction, pub trigger: CbTrigger,
}
impl CbRule { /// If this returns false, the rule will not compile and should not be used. fn is_ascii(&self) -> bool { self.action.selector.iter().all(|s| s.is_ascii())
&& self.trigger.url_filter.is_ascii()
&& self
.trigger
.if_domain
.iter()
.flatten()
.all(|d| d.is_ascii())
&& self
.trigger
.unless_domain
.iter()
.flatten()
.all(|d| d.is_ascii())
&& self
.trigger
.if_top_url
.iter()
.flatten()
.all(|d| d.is_ascii())
&& self
.trigger
.unless_top_url
.iter()
.flatten()
.all(|d| d.is_ascii())
}
}
/// Corresponds to the `action` field of a Safari content blocking rule. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pubstruct CbAction { #[serde(rename = "type")] pub typ: CbType, /// Specify a string that defines a selector list. This value is required when the action type /// is css-display-none. If it's not, the selector field is ignored by Safari. Use CSS /// identifiers as the individual selector values, separated by commas. Safari and WebKit /// supports all of its CSS selectors for Safari content-blocking rules. #[serde(default, skip_serializing_if = "Option::is_none")] pub selector: Option<String>,
}
/// Corresponds to the `action.type` field of a Safari content blocking rule. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pubenum CbType { /// Stops loading of the resource. If the resource was cached, the cache is ignored.
Block, /// Strips cookies from the header before sending to the server. Only cookies otherwise /// acceptable to Safari's privacy policy can be blocked. Combining with ignore-previous-rules /// doesn't override the browser’s privacy settings.
BlockCookies, /// Hides elements of the page based on a CSS selector. A selector field contains the selector /// list. Any matching element has its display property set to none, which hides it.
CssDisplayNone, /// Ignores previously triggered actions.
IgnorePreviousRules, /// Changes a URL from http to https. URLs with a specified (nondefault) port and links using /// other protocols are unaffected.
MakeHttps,
}
/// Corresponds to possible entries in the `trigger.load_type` field of a Safari content blocking /// rule. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pubenum CbLoadType {
FirstParty,
ThirdParty,
}
/// Corresponds to possible entries in the `trigger.resource_type` field of a Safari content /// blocking rule. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pubenum CbResourceType {
Document,
Image,
StyleSheet,
Script,
Font,
Raw,
SvgDocument,
Media,
Popup,
}
/// Corresponds to the `trigger` field of a Safari content blocking rule. #[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pubstruct CbTrigger { /// Specifies a pattern to match the URL against. pub url_filter: String, #[serde(default, skip_serializing_if = "Option::is_none")] /// A Boolean value. The default value is false. pub url_filter_is_case_sensitive: Option<bool>, /// An array of strings matched to a URL's domain; limits action to a list of specific domains. /// Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to match domain /// and subdomains. Can't be used with unless-domain. #[serde(default, skip_serializing_if = "Option::is_none")] pub if_domain: Option<Vec<String>>, /// An array of strings matched to a URL's domain; acts on any site except domains in a /// provided list. Values must be lowercase ASCII, or punycode for non-ASCII. Add * in front to /// match domain and subdomains. Can't be used with if-domain. #[serde(default, skip_serializing_if = "Option::is_none")] pub unless_domain: Option<Vec<String>>, /// An array of strings representing the resource types (how the browser intends to use the /// resource) that the rule should match. If not specified, the rule matches all resource /// types. Valid values: document, image, style-sheet, script, font, raw (Any untyped load), /// svg-document, media, popup. #[serde(default, skip_serializing_if = "Option::is_none")] pub resource_type: Option<HashSet<CbResourceType>>, /// An array of strings that can include one of two mutually exclusive values. If not /// specified, the rule matches all load types. first-party is triggered only if the resource /// has the same scheme, domain, and port as the main page resource. third-party is triggered /// if the resource is not from the same domain as the main page resource. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub load_type: Vec<CbLoadType>, /// An array of strings matched to the entire main document URL; limits the action to a /// specific list of URL patterns. Values must be lowercase ASCII, or punycode for non-ASCII. /// Can't be used with unless-top-url. #[serde(default, skip_serializing_if = "Option::is_none")] pub if_top_url: Option<Vec<String>>, /// An array of strings matched to the entire main document URL; acts on any site except URL /// patterns in provided list. Values must be lowercase ASCII, or punycode for non-ASCII. Can't /// be used with if-top-url. #[serde(default, skip_serializing_if = "Option::is_none")] pub unless_top_url: Option<Vec<String>>,
}
/// Possible failure reasons when attempting to convert an adblock rule into content filtering /// syntax. #[derive(Debug)] pubenum CbRuleCreationFailure { /// Currently, only filter rules parsed in debug mode can be translated into equivalent content /// blocking syntax.
NeedsDebugMode, /// Content blocking rules cannot have if-domain and unless-domain together at the same time.
UnlessAndIfDomainTogetherUnsupported, /// A network filter rule with only the given content type flags was provided, and none of them /// are supported. If at least one supported content type is provided, no failure will occur /// and unsupported types will be silently dropped.
NoSupportedNetworkOptions(NetworkFilterMask), /// Network rules with redirect options cannot be represented in content blocking syntax.
NetworkRedirectUnsupported, /// Network rules with generichide options cannot be supported in content blocking syntax.
NetworkGenerichideUnsupported, /// Network rules with badfilter options cannot be supported in content blocking syntax.
NetworkBadFilterUnsupported, /// Network rules with csp options cannot be supported in content blocking syntax.
NetworkCspUnsupported, /// Network rules with removeparam options cannot be supported in content blocking syntax.
NetworkRemoveparamUnsupported, /// Content blocking syntax only supports a subset of regex features, namely: /// - Matching any character with “.”. /// - Matching ranges with the range syntax [a-b]. /// - Quantifying expressions with “?”, “+” and “*”. /// - Groups with parenthesis. /// /// It may be possible to correctly convert some full-regex rules, but others use unsupported /// features (e.g. quantified repetition with {...}) that make conversion to content blocking /// syntax impossible.
FullRegexUnsupported, /// `Blocker`-internal `NetworkFilter`s can be represented in optimized form, but these cannot /// be currently converted into content blocking syntax.
OptimizedRulesUnsupported, /// Cosmetic rules with entities (e.g. google.*) rather than hostnames cannot be represented in /// content blocking syntax.
CosmeticEntitiesUnsupported, /// Cosmetic rules with custom action specification (i.e. `:style(...)`) cannot be represented /// in content blocking syntax.
CosmeticActionRulesNotSupported, /// Cosmetic rules with scriptlet injections (i.e. `+js(...)`) cannot be represented in content /// blocking syntax.
ScriptletInjectionsNotSupported, /// Valid content blocking rules can only include ASCII characters.
RuleContainsNonASCII, /// `from` as a `domain` alias is not currently supported in content blocking syntax.
FromNotSupported, /// Content blocking rules cannot support procedural cosmetic filter operators.
ProceduralCosmeticFiltersUnsupported,
}
impl TryFrom<ParsedFilter> for CbRuleEquivalent { type Error = CbRuleCreationFailure;
fn try_from(v: ParsedFilter) -> Result<Self, Self::Error> { match v {
ParsedFilter::Network(f) => f.try_into(),
ParsedFilter::Cosmetic(f) => Ok(Self::SingleRule(f.try_into()?)),
}
}
}
/// Some adblock rules cannot be directly represented by a single content blocking rule. This enum /// serves as an intermediate conversion step that provides extra context on why one rule turned /// into multiple rules. /// /// The contained rules can be accessed using `IntoIterator`. #[allow(clippy::large_enum_variant)] pubenum CbRuleEquivalent { /// In most successful cases, an ABP rule can be converted into a single content blocking rule.
SingleRule(CbRule), /// If a network rule has more than one specified resource type, one of those types is /// `Document`, and no load type is specified, then the rule should be split into two content /// blocking rules: the first has all original resource types except `Document`, and the second /// only specifies `Document` with a third-party load type.
SplitDocument(CbRule, CbRule),
}
impl IntoIterator for CbRuleEquivalent { type Item = CbRule; type IntoIter = CbRuleEquivalentIterator;
let (if_domain, unless_domain) = if v.opt_domains.is_some()
|| v.opt_not_domains.is_some()
{ letmut if_domain = vec![]; letmut unless_domain = vec![];
// Unwraps are okay here - any rules with opt_domains or opt_not_domains must have // an options section delimited by a '$' character, followed by a `domain=` option. let opts = &raw_line[find_char(b'$', raw_line.as_bytes()).unwrap() + "$".len()..]; let domain_start_index = iflet Some(index) = memmem::find(opts.as_bytes(), b"domain=") {
index
} else { return Err(CbRuleCreationFailure::FromNotSupported);
}; let domains_start = &opts[domain_start_index + "domain=".len()..]; let domains = iflet Some(comma) = find_char(b',', domains_start.as_bytes()) {
&domains_start[..comma]
} else {
domains_start
}
.split('|');
let lowercase = domain.to_lowercase(); let normalized_domain = if lowercase.is_ascii() {
lowercase
} else { // The network filter has already parsed successfully, so this should be // safe
idna::domain_to_ascii(&lowercase).unwrap()
};
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.