Skip to content

Examples

Practical patterns for classifying a Chrome extension's permissions with ext-permission-risk. These mirror the audit flow used by the zovo.one extension scanner.

Classify a single permission

The simplest case: look up one token from a manifest.

use ext_permission_risk::{risk_of, RiskLevel};

let info = risk_of("cookies").unwrap();
println!("cookies: {} — {}", info.level.label(), info.description);
// cookies: HIGH — Reads and modifies all cookies for any site the extension
//           has host access to, including session and auth cookies.

Classify a whole manifest

A manifest has two arrays — permissions (named API tokens) and host_permissions (match-patterns). Classify both with risk_of_or_pattern, which recognises host patterns in addition to named tokens.

use ext_permission_risk::{risk_of_or_pattern, highest_risk, RiskLevel};

let manifest_perms = [
    "activeTab", "storage", "cookies",          // named tokens
    "https://*.example.com/*",                  // scoped host pattern
];

// Overall risk: the max across every token.
let overall = highest_risk(manifest_perms.iter().copied());
assert_eq!(overall, Some(RiskLevel::High));     // cookies dominates

// Per-permission breakdown.
for p in manifest_perms {
    let r = risk_of_or_pattern(p);
    println!("{:<28} {}  {}", p, r.level.label(), r.description);
}

Recognise host match-patterns

<all_urls> and *://*/* grant access to the entire web and are High. A scoped pattern like https://*.example.com/* grants access to one domain family and is Medium — meaningfully less broad.

use ext_permission_risk::{risk_of_or_pattern, RiskLevel};

assert_eq!(risk_of_or_pattern("<all_urls>").level,           RiskLevel::High);
assert_eq!(risk_of_or_pattern("https://*/*").level,          RiskLevel::High);
assert_eq!(risk_of_or_pattern("https://*.example.com/*").level, RiskLevel::Medium);

Compute a verdict

A common scanner pattern: derive a one-word verdict from the highest risk present.

use ext_permission_risk::{highest_risk, RiskLevel};

fn verdict(perms: &[&str]) -> &'static str {
    match highest_risk(perms.iter().copied()) {
        Some(RiskLevel::High)   => "dangerous — requests broad access, review carefully",
        Some(RiskLevel::Medium) => "caution — sensitive access, check the publisher",
        Some(RiskLevel::Low)    => "low risk — minimal access requested",
        None                    => "no recognised permissions",
    }
}

assert_eq!(verdict(&["activeTab", "storage"]), "low risk — minimal access requested");
assert_eq!(verdict(&["tabs", "history"]),      "caution — sensitive access, check the publisher");
assert_eq!(verdict(&["cookies", "<all_urls>"]), "dangerous — requests broad access, review carefully");

Iterate the full table

Render a complete reference page, or audit every permission the table knows about.

use ext_permission_risk::{all_permissions, RiskLevel};

let high: Vec<_> = all_permissions()
    .iter()
    .filter(|p| p.level == RiskLevel::High)
    .map(|p| p.permission)
    .collect();

println!("{} HIGH-risk permissions known", high.len());
assert!(high.contains(&"cookies"));
assert!(high.contains(&"<all_urls>"));

Integration note

This crate deliberately has no JSON or manifest-parsing dependency — the scanner is expected to parse manifest.json itself (with serde_json or similar) and pass the resulting permissions / host_permissions string arrays to risk_of / risk_of_or_pattern. That keeps the classification logic pure, testable, and reusable across any Rust tool that wants to reason about extension permissions — including the zovo.one scanner.