Skip to content

Commit

Permalink
support permissioned/trusted scriptlets
Browse files Browse the repository at this point in the history
  • Loading branch information
antonok-edm committed Aug 2, 2023
1 parent b2468fe commit a7a385a
Show file tree
Hide file tree
Showing 10 changed files with 468 additions and 109 deletions.
1 change: 1 addition & 0 deletions benches/bench_redirect_performance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ fn build_resources_for_filters(#[allow(unused)] filters: &[NetworkFilter]) -> Re
kind: ResourceType::Mime(MimeType::from_extension(&redirect)),
content: base64::encode(redirect),
dependencies: vec![],
permission: Default::default(),
}
})
.for_each(|resource| {
Expand Down
38 changes: 21 additions & 17 deletions src/cosmetic_filter_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use crate::filters::cosmetic::CosmeticFilter;
use crate::filters::cosmetic::CosmeticFilterMask;
use crate::resources::ResourceStorage;
use crate::resources::{PermissionMask, ResourceStorage};
use crate::utils::Hash;

use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -245,7 +245,7 @@ impl CosmeticFilterCache {
let mut remove_selectors = HashSet::new();
let mut remove_attrs = HashMap::<_, Vec<_>>::new();
let mut remove_classes = HashMap::<_, Vec<_>>::new();
let mut script_injections = HashSet::new();
let mut script_injections = HashMap::<&str, PermissionMask>::new();
let mut exceptions = HashSet::new();

let hashes: Vec<&Hash> = request_entities.iter().chain(request_hostnames.iter()).collect();
Expand All @@ -268,7 +268,9 @@ impl CosmeticFilterCache {
// special behavior: `script_injections` doesn't have to own the strings yet, since the
// scripts need to be fetched and templated later
if let Some(s) = self.specific_rules.inject_script.get(hash) {
script_injections.extend(s);
s.iter().for_each(|(s, mask)| {
script_injections.entry(s).and_modify(|entry| *entry |= *mask).or_insert(*mask);
});
}

populate_map(hash, &self.specific_rules.style, &mut style_selectors);
Expand Down Expand Up @@ -307,7 +309,7 @@ impl CosmeticFilterCache {
// same logic but not using prune_set since strings are unowned, (see above)
if let Some(s) = self.specific_rules.uninject_script.get(hash) {
s.iter().for_each(|s| {
script_injections.remove(s);
script_injections.remove(s.as_str());
});
}

Expand All @@ -331,8 +333,8 @@ impl CosmeticFilterCache {
};

let mut injected_script = String::new();
script_injections.iter().for_each(|s| {
if let Ok(filled_template) = resources.get_scriptlet_resource(s) {
script_injections.iter().for_each(|(s, mask)| {
if let Ok(filled_template) = resources.get_scriptlet_resource(s, *mask) {
injected_script += "try {\n";
injected_script += &filled_template;
injected_script += "\n} catch ( e ) { }\n";
Expand Down Expand Up @@ -390,7 +392,7 @@ pub(crate) struct HostnameRuleDb {
/// `example.com##+js(acis, Number.isNan)`.
///
/// The parameter is the contents of the `+js(...)` syntax construct.
pub inject_script: HostnameFilterBin<String>,
pub inject_script: HostnameFilterBin<(String, PermissionMask)>,
/// Hostname-specific rules to except a scriptlet to inject along with any arguments, e.g.
/// `example.com#@#+js(acis, Number.isNan)`.
///
Expand Down Expand Up @@ -449,8 +451,8 @@ impl HostnameRuleDb {
let kind = match (unhide, script_inject, rule.action) {
(false, false, None) => Hide(selector),
(true, false, None) => Unhide(selector),
(false, true, None) => InjectScript(selector),
(true, true, None) => UninjectScript(selector),
(false, true, None) => InjectScript((selector, rule.permission)),
(true, true, None) => UninjectScript((selector, rule.permission)),
(false, false, Some(CosmeticFilterAction::Style(s))) => Style((selector, s)),
(true, false, Some(CosmeticFilterAction::Style(s)) )=> Unstyle((selector, s)),
(false, false, Some(CosmeticFilterAction::Remove)) => Remove(selector),
Expand Down Expand Up @@ -484,7 +486,7 @@ impl HostnameRuleDb {
Hide(s) => self.hide.insert(token, s),
Unhide(s) => self.unhide.insert(token, s),
InjectScript(s) => self.inject_script.insert(token, s),
UninjectScript(s) => self.uninject_script.insert(token, s),
UninjectScript((s, _)) => self.uninject_script.insert(token, s),
Remove(s) => self.remove.insert(token, s),
Unremove(s) => self.unremove.insert(token, s),
Style(s) => self.style.insert(token, s),
Expand All @@ -502,8 +504,8 @@ impl HostnameRuleDb {
enum SpecificFilterType {
Hide(String),
Unhide(String),
InjectScript(String),
UninjectScript(String),
InjectScript((String, PermissionMask)),
UninjectScript((String, PermissionMask)),
Remove(String),
Unremove(String),
Style((String, String)),
Expand Down Expand Up @@ -550,7 +552,7 @@ mod cosmetic_cache_tests {
fn cache_from_rules(rules: Vec<&str>) -> CosmeticFilterCache {
let parsed_rules = rules
.iter()
.map(|r| CosmeticFilter::parse(r, false).unwrap())
.map(|r| CosmeticFilter::parse(r, false, Default::default()).unwrap())
.collect::<Vec<_>>();

CosmeticFilterCache::from_rules(parsed_rules)
Expand Down Expand Up @@ -663,6 +665,7 @@ mod cosmetic_cache_tests {
kind: ResourceType::Template,
content: base64::encode("set-constant.js, {{1}}, {{2}}"),
dependencies: vec![],
permission: Default::default(),
},
Resource::simple("nowebrtc.js", MimeType::ApplicationJavascript, "nowebrtc.js"),
Resource::simple("window.open-defuser.js", MimeType::ApplicationJavascript, "window.open-defuser.js"),
Expand Down Expand Up @@ -853,7 +856,7 @@ mod cosmetic_cache_tests {
let cfcache = CosmeticFilterCache::from_rules(
rules
.iter()
.map(|r| CosmeticFilter::parse(r, false).unwrap())
.map(|r| CosmeticFilter::parse(r, false, Default::default()).unwrap())
.collect::<Vec<_>>(),
);

Expand Down Expand Up @@ -921,7 +924,7 @@ mod cosmetic_cache_tests {
let cfcache = CosmeticFilterCache::from_rules(
rules
.iter()
.map(|r| CosmeticFilter::parse(r, false).unwrap())
.map(|r| CosmeticFilter::parse(r, false, Default::default()).unwrap())
.collect::<Vec<_>>(),
);
let resources = ResourceStorage::default();
Expand Down Expand Up @@ -987,7 +990,7 @@ mod cosmetic_cache_tests {
let cfcache = CosmeticFilterCache::from_rules(
rules
.iter()
.map(|r| CosmeticFilter::parse(r, false).unwrap())
.map(|r| CosmeticFilter::parse(r, false, Default::default()).unwrap())
.collect::<Vec<_>>(),
);
let resources = ResourceStorage::default();
Expand Down Expand Up @@ -1022,7 +1025,7 @@ mod cosmetic_cache_tests {
let cfcache = CosmeticFilterCache::from_rules(
rules
.iter()
.map(|r| CosmeticFilter::parse(r, false).unwrap())
.map(|r| CosmeticFilter::parse(r, false, Default::default()).unwrap())
.collect::<Vec<_>>(),
);
let resources = ResourceStorage::from_resources([
Expand All @@ -1032,6 +1035,7 @@ mod cosmetic_cache_tests {
kind: ResourceType::Template,
content: base64::encode("abort-on-property-read.js, {{1}}"),
dependencies: vec![],
permission: Default::default(),
}
]);

Expand Down
4 changes: 2 additions & 2 deletions src/data_format/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl From<&HostnameRuleDb> for LegacyHostnameRuleDb {
}
}
for (hash, bin) in v.inject_script.0.iter() {
for f in bin {
for (f, _mask) in bin {
db.entry(*hash)
.and_modify(|v| v.push(LegacySpecificFilterType::ScriptInject(f.to_owned())))
.or_insert_with(|| vec![LegacySpecificFilterType::ScriptInject(f.to_owned())]);
Expand Down Expand Up @@ -104,7 +104,7 @@ impl Into<HostnameRuleDb> for LegacyHostnameRuleDb {
LegacySpecificFilterType::Unhide(s) => unhide.insert(&hash, s),
LegacySpecificFilterType::Style(s, st) => style.insert(&hash, (s, st)),
LegacySpecificFilterType::UnhideStyle(s, st) => unstyle.insert(&hash, (s, st)),
LegacySpecificFilterType::ScriptInject(s) => inject_script.insert(&hash, s),
LegacySpecificFilterType::ScriptInject(s) => inject_script.insert(&hash, (s, Default::default())),
LegacySpecificFilterType::UnhideScriptInject(s) => uninject_script.insert(&hash, s),
}
}
Expand Down
69 changes: 69 additions & 0 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,4 +828,73 @@ mod tests {
assert!(engine.check_network_request(&request).matched);
}*/
}

#[test]
fn scriptlet_permissions() {
use crate::resources::{PermissionMask, ResourceType};
const UBO_PERM: PermissionMask = PermissionMask::from_bits(0b00000001);
const BRAVE_PERM: PermissionMask = PermissionMask::from_bits(0b00000011);

let resources = [
Resource::simple("refresh-defuser.js", MimeType::ApplicationJavascript, "refresh-defuser"),
Resource {
name: "trusted-set-cookie.js".to_string(),
aliases: vec![],
kind: ResourceType::Mime(MimeType::ApplicationJavascript),
content: base64::encode("trusted-set-cookie"),
dependencies: vec![],
permission: UBO_PERM,
},
Resource {
name: "brave-fix.js".to_string(),
aliases: vec![],
kind: ResourceType::Mime(MimeType::ApplicationJavascript),
content: base64::encode("brave-fix"),
dependencies: vec![],
permission: BRAVE_PERM,
},
];

let mut filter_set = FilterSet::new(false);
filter_set.add_filters([
"sub1.example.com##+js(refresh-defuser)",
"sub2.example.com##+js(trusted-set-cookie)",
"sub3.example.com##+js(brave-fix)"
], Default::default());
filter_set.add_filters([
"sub4.example.com##+js(refresh-defuser)",
"sub5.example.com##+js(trusted-set-cookie)",
"sub6.example.com##+js(brave-fix)"
], ParseOptions {
permissions: UBO_PERM,
..Default::default()
});
filter_set.add_filters([
"sub7.example.com##+js(refresh-defuser)",
"sub8.example.com##+js(trusted-set-cookie)",
"sub9.example.com##+js(brave-fix)"
], ParseOptions {
permissions: BRAVE_PERM,
..Default::default()
});

let mut engine = Engine::from_filter_set(filter_set, true);
engine.use_resources(resources);

fn wrap_try(scriptlet_content: &str) -> String {
format!("try {{\n{}\n}} catch ( e ) {{ }}\n", scriptlet_content)
}

assert_eq!(engine.url_cosmetic_resources("https://sub1.example.com").injected_script, wrap_try("refresh-defuser"));
assert_eq!(engine.url_cosmetic_resources("https://sub2.example.com").injected_script, "");
assert_eq!(engine.url_cosmetic_resources("https://sub3.example.com").injected_script, "");

assert_eq!(engine.url_cosmetic_resources("https://sub4.example.com").injected_script, wrap_try("refresh-defuser"));
assert_eq!(engine.url_cosmetic_resources("https://sub5.example.com").injected_script, wrap_try("trusted-set-cookie"));
assert_eq!(engine.url_cosmetic_resources("https://sub6.example.com").injected_script, "");

assert_eq!(engine.url_cosmetic_resources("https://sub7.example.com").injected_script, wrap_try("refresh-defuser"));
assert_eq!(engine.url_cosmetic_resources("https://sub8.example.com").injected_script, wrap_try("trusted-set-cookie"));
assert_eq!(engine.url_cosmetic_resources("https://sub9.example.com").injected_script, wrap_try("brave-fix"));
}
}
Loading

0 comments on commit a7a385a

Please sign in to comment.