A portable Public Suffix List engine for registrable-domain decisions in MoonBit.
Dependencies
moon run examples/reviewlookup api.shop.example.co.uk: suffix=co.uk, registrable=example.co.uk
PSL update: +PRIVATE glideos.app
api.glideos.app: registrable glideos.app -> api.glideos.app
Cookie Domain=.glideos.app: glideos.app accepted -> cookie Domain attribute 'glideos.app' is a public suffix
changed hosts=1, changed Cookie scopes=1let rules =
#|com
#|co.uk
#|*.ck
#|!www.ck
let suffixes = @moonsuffix.SuffixList::parse(rules).unwrap()
let result = suffixes.lookup("api.shop.example.co.uk").unwrap()
println(result.public_suffix()) // co.uk
println(result.registrable_domain()) // Some(example.co.uk)
println(result.matched_rule()) // co.uklet result = suffixes.lookup_with_options(
"api.example.com",
@moonsuffix.LookupOptions::strict_icann(),
).unwrap()moon run cmd/mainlet list = @suffix_idna.parse_psl("公司.cn\ncom\n").unwrap()
let result = @suffix_idna.lookup(list, "商店.公司.cn").unwrap()
println(result.public_suffix()) // xn--55qx5d.cn
println(result.registrable_domain().unwrap()) // xn--czrs0t.xn--55qx5d.cnlet report = @suffix_idna.lookup_batch(
list,
["商店.公司.cn", "商店..公司.cn", "xn--czrs0t.xn--55qx5d.cn"],
)
println(report.to_csv())let scope = @suffix_idna.resolve_cookie_scope(
list, "api.商店.公司.cn", Some(".商店.公司.cn"),
).unwrap()
println(scope.domain()) // xn--czrs0t.xn--55qx5d.cn
println(@suffix_idna.cookie_scope_matches_host(scope, "别的.商店.公司.cn")) // Ok(true)let first = suffixes.schemeful_site("https", "shop.example.com").unwrap()
let second = suffixes.schemeful_site("https", "api.example.com").unwrap()
let third = suffixes.schemeful_site("http", "api.example.com").unwrap()
println(first.same_site(second)) // true
println(first.same_site(third)) // falselet scopes = suffixes.shareable_cookie_scopes("api.shop.example.com").unwrap()
for scope in scopes {
println(scope.domain())
}moon run --target native cmd/snapshot \
examples/audit/old.psl \
old.snapshot \
--revision example-v1moon run --target native cmd/snapshot \
examples/idna/rules.psl unicode.snapshot \
--revision unicode-example-v1 --idnamoon run --target native cmd/lookup \
unicode.snapshot 商店.公司.cn --idna --strict-icannmoon run --target native cmd/conformance \
examples/conformance/rules.psl examples/conformance/cases.txtmoon run --target native cmd/bench \
public_suffix_list.dat examples/bench/hosts.txt --idnamoon run --target native cmd/embed \
public_suffix_list.dat embedded_psl.mbt \
--revision <upstream-commit-sha> --idnamoon run --target native cmd/audit \
examples/audit/old.psl \
examples/audit/new.psl \
examples/audit/hosts.txt \
--from example-v1 \
--to example-v2 \
--strict-icannmoon run --target native cmd/audit \
old.snapshot new.snapshot examples/audit/hosts.txt \
--bundles --strict-icann --fail-on-impactmoon run --target native cmd/audit \
examples/audit/old.psl examples/audit/new.psl \
examples/audit/stable-hosts.txt \
--from example-v1 --to example-v2 --strict-icann \
--cookie-inventory examples/audit/cookies.tsv --fail-on-impactmoon run --target native cmd/audit \
examples/audit/old-unicode.psl examples/audit/new-unicode.psl \
examples/audit/unicode-hosts.txt \
--from old --to new --idna --strict-icann \
--cookie-inventory examples/audit/unicode-cookies.tsv --fail-on-impactmoon run --target native cmd/snapshot \
examples/audit/old.psl old.snapshot --revision example-v1
moon run --target native cmd/policy-audit \
old.snapshot examples/audit/hosts.txt --fail-on-impactmoon run --target native cmd/snapshot \
examples/audit/old.psl classify.snapshot --revision example-v1
moon run --target native cmd/classify \
classify.snapshot examples/classify/hosts.txt --strict-icannmoon run --target native cmd/snapshot \
examples/idna/rules.psl unicode.snapshot \
--revision unicode-example-v1 --idna
moon run --target native cmd/classify \
unicode.snapshot examples/classify/unicode-hosts.txt \
--idna --strict-icannlet coverage = suffixes.analyze_rule_coverage(
["api.example.com", "www.www.ck", "service.internal"],
@moonsuffix.LookupOptions::browser_default(),
)
println(coverage.observed_rule_count())
println(coverage.to_csv())moon fmt
moon info
moon check --target wasm --deny-warn
moon test --target wasm --deny-warn
moon test --target wasm-gc --deny-warn
moon test --target js --deny-warn
moon run cmd/main
moon run examples/idna
moon check cmd/audit --target native --deny-warn
moon test cmd/audit --target native --deny-warn
moon check cmd/snapshot --target native --deny-warn
moon test cmd/snapshot --target native --deny-warn
moon check cmd/lookup --target native --deny-warn
moon test cmd/lookup --target native --deny-warn
moon check cmd/conformance --target native --deny-warn
moon test cmd/conformance --target native --deny-warn
moon check cmd/policy-audit --target native --deny-warn
moon test cmd/policy-audit --target native --deny-warn
moon check cmd/classify --target native --deny-warn
moon test cmd/classify --target native --deny-warn
moon check cmd/bench --target native --deny-warn
moon test cmd/bench --target native --deny-warn
moon check cmd/embed --target native --deny-warn
moon test cmd/embed --target native --deny-warnpub(all) enum ConformanceActual {
NullInput
RejectedDomain(DomainError)
RejectedHostname(String)
NoRegistrableDomain
RegistrableDomain(String)
} derive(Eq, Debug)pub struct ConformanceFailure {
line_ : Int
input_ : String?
expected_ : String?
actual_ : ConformanceActual
}pub struct ConformanceReport {
total_count_ : Int
passed_count_ : Int
failures_ : ReadOnlyArray[ConformanceFailure]
}fn CookieScope::matches_host(self : CookieScope, request_host : String) -> Result[Bool, CookieScopeError]pub struct CookieScopeImpact {
index_ : Int
input_ : CookieScopeInput
kind_ : CookieScopeImpactKind
before_ : Result[CookieScope, CookieScopeError]
after_ : Result[CookieScope, CookieScopeError]
}pub(all) enum CookieScopeImpactKind {
CookieBecameAccepted
CookieBecameRejected
CookieScopeChanged
}pub struct CookieScopeImpactReport {
from_revision_ : String
to_revision_ : String
from_sha256_ : String
to_sha256_ : String
scanned_count_ : Int
unchanged_count_ : Int
impacts_ : ReadOnlyArray[CookieScopeImpact]
}fn CookieScopeImpactReport::impacts(self : CookieScopeImpactReport) -> ReadOnlyArray[CookieScopeImpact]pub struct CookieScopeInput {
request_host_ : String
domain_attribute_ : String?
}pub struct DomainImpact {
index_ : Int
input_ : String
kind_ : ImpactKind
before_ : Result[Lookup, DomainError]
after_ : Result[Lookup, DomainError]
}pub struct Lookup {
normalized_domain : String
public_suffix : String
registrable_domain : String?
matched_rule : String
rule_kind : RuleKind
rule_section : RuleSection?
}pub struct LookupTrace {
outcome_ : Result[Lookup, DomainError]
candidates_ : ReadOnlyArray[RuleCandidate]
}pub struct PolicyImpactReport {
before_options_ : LookupOptions
after_options_ : LookupOptions
scanned_count_ : Int
unchanged_count_ : Int
impacts_ : ReadOnlyArray[DomainImpact]
}pub struct RuleCandidate {
rule_ : String
kind_ : RuleKind
section_ : RuleSection?
eligible_ : Bool
selected_ : Bool
}pub struct RuleCoverageEntry {
rule_ : String
section_ : RuleSection
kind_ : RuleKind
selected_count_ : Int
}pub struct RuleCoverageReport {
entries_ : ReadOnlyArray[RuleCoverageEntry]
scanned_count_ : Int
explicit_count_ : Int
implicit_count_ : Int
unlisted_count_ : Int
invalid_count_ : Int
observed_rule_count_ : Int
}pub struct SchemefulSite {
scheme_ : String
registrable_domain_ : String
}pub(all) enum SiteError {
InvalidSiteDomain(DomainError)
NoRegistrableDomain(String)
InvalidSiteScheme(String)
InvalidSiteHost(String, String)
} derive(Eq, Debug)pub struct Snapshot {
source_revision_ : String
psl_text_ : String
sha256_ : String
rule_count_ : Int
}fn Snapshot::analyze_cookie_scope_impact(self : Snapshot, newer : Snapshot, inputs : Array[CookieScopeInput], options : LookupOptions) -> CookieScopeImpactReportfn Snapshot::analyze_impact(self : Snapshot, newer : Snapshot, domains : Array[String], options : LookupOptions) -> SnapshotImpactfn Snapshot::restore_compiled_bundle_text(bundle : String) -> Result[(Snapshot, SuffixList), SnapshotLoadError]pub(all) enum SnapshotChange {
AddedRule(String, RuleSection)
RemovedRule(String, RuleSection)
MovedRule(String, RuleSection, RuleSection)
} derive(Eq, Debug)pub struct SnapshotComparison {
older_ : Snapshot
newer_ : Snapshot
before_list_ : SuffixList
after_list_ : SuffixList
}fn SnapshotComparison::analyze_cookie_scope_impact(self : SnapshotComparison, inputs : Array[CookieScopeInput], options : LookupOptions) -> CookieScopeImpactReportfn SnapshotComparison::analyze_impact(self : SnapshotComparison, domains : Array[String], options : LookupOptions) -> SnapshotImpactpub struct SnapshotDiff {
from_revision_ : String
to_revision_ : String
from_sha256_ : String
to_sha256_ : String
changes_ : ReadOnlyArray[SnapshotChange]
added_count_ : Int
removed_count_ : Int
moved_count_ : Int
}pub struct SnapshotImpact {
from_revision_ : String
to_revision_ : String
from_sha256_ : String
to_sha256_ : String
scanned_count_ : Int
unchanged_count_ : Int
impacts_ : ReadOnlyArray[DomainImpact]
}pub(all) enum SnapshotLoadError {
InvalidManifest(String)
InvalidRevisionEncoding
InvalidDigest(String)
InvalidRuleCount(String)
InvalidSnapshotRules(ListParseError)
NonCanonicalPsl
DigestMismatch(String, String)
RuleCountMismatch(Int, Int)
} derive(Eq, Debug)pub struct SuffixList {
children : Array[Map[String, Int]]
exact_sections : Array[Array[RuleSection]]
wildcard_sections : Array[Array[RuleSection]]
exception_sections : Array[Array[RuleSection]]
rule_count_ : Int
}fn SuffixList::analyze_policy_impact(self : SuffixList, domains : Array[String], before_options : LookupOptions, after_options : LookupOptions) -> PolicyImpactReportfn SuffixList::analyze_rule_coverage(self : SuffixList, domains : Array[String], options : LookupOptions) -> RuleCoverageReportfn SuffixList::lookup_batch_with_options(self : SuffixList, domains : Array[String], options : LookupOptions) -> BatchReportfn SuffixList::lookup_with_options(self : SuffixList, domain : String, options : LookupOptions) -> Result[Lookup, DomainError]fn SuffixList::registrable_domain(self : SuffixList, domain : String) -> Result[String?, DomainError]fn SuffixList::resolve_cookie_scope(self : SuffixList, request_host : String, domain_attribute : String?) -> Result[CookieScope, CookieScopeError]fn SuffixList::resolve_cookie_scope_with_options(self : SuffixList, request_host : String, domain_attribute : String?, options : LookupOptions) -> Result[CookieScope, CookieScopeError]fn SuffixList::same_registrable_site(self : SuffixList, left : String, right : String) -> Result[Bool, SiteError]fn SuffixList::same_registrable_site_with_options(self : SuffixList, left : String, right : String, options : LookupOptions) -> Result[Bool, SiteError]fn SuffixList::schemeful_site(self : SuffixList, scheme : String, hostname : String) -> Result[SchemefulSite, SiteError]fn SuffixList::schemeful_site_with_options(self : SuffixList, scheme : String, hostname : String, options : LookupOptions) -> Result[SchemefulSite, SiteError]fn SuffixList::shareable_cookie_scopes(self : SuffixList, request_host : String) -> Result[ReadOnlyArray[CookieScope], CookieScopeError]fn SuffixList::shareable_cookie_scopes_with_options(self : SuffixList, request_host : String, options : LookupOptions) -> Result[ReadOnlyArray[CookieScope], CookieScopeError]fn SuffixList::site_key_with_options(self : SuffixList, domain : String, options : LookupOptions) -> Result[String, SiteError]fn SuffixList::trace_lookup_with_options(self : SuffixList, domain : String, options : LookupOptions) -> Result[LookupTrace, DomainError]fn SuffixList::verify_psl_test_file(self : SuffixList, test_text : String) -> Result[ConformanceReport, ConformanceParseError]fn SuffixList::verify_psl_test_file_with_normalizer(self : SuffixList, test_text : String, normalize : (String) -> Result[String, String]) -> Result[ConformanceReport, ConformanceParseError]Install
Download zipA portable Public Suffix List engine for registrable-domain decisions in MoonBit.
Dependencies