pub struct PacPolicy { /* private fields */ }pac only.Expand description
Safety envelope for PAC evaluation (pac-boa; ignored by WinHttpPacResolver).
Defaults: no DNS, drop internal answers, myIpAddress() → 127.0.0.1, 5 s timeout,
boa’s own recursion/stack limits, and a loop cap boa does not impose at all.
Heap unbounded — OS-sourced PAC + DNS = high severity.
One default is not a limit: the crate reads no time zone, so
local_utc_offset is 0 and weekdayRange, dateRange and
timeRange answer in GMT whether or not the script passed "GMT" — a browser reads the
host’s zone there. with_local_utc_offset is the only way
to move them, and a fixed offset does not follow DST.
Every limit on this type is a VM limit: it bounds a script that is already running, and
none of them bounds parsing. boa_parser 0.21.1 is a recursive-descent parser with no
depth limit of its own, so nesting alone overflows the native stack before evaluation
starts — measured through this type on the thread BoaEvaluator spawns for a script
(x86-64 Windows, the stack std::thread gives a spawn by default): an optimized build
parses 99 nested ( and aborts on 100, in a 256-byte script; the same source unoptimized
aborts on 17. A native stack overflow aborts the process rather than panicking, so
neither the dedicated evaluation thread nor the timeout contains it. A caller that
accepts a PAC body it does not control has to isolate the process itself; no setting here
substitutes for that.
Neither figure is a ceiling to design against. ( is the shape they were taken on, not
the only one that recurses — [ and {a: overflow too, and unoptimized they do it at a
comparable depth — so a screen written for ( screens (. And with
with_timeout(None) there is no spawned thread to measure at all:
the script parses on the calling thread, under whatever stack that thread was given.
Upstream has the bug open
(boa#4397) and closed the parser guard
written for it (boa#4772) unmerged over the
Test262 conformance it cost; 0.22.0 ships with none, so raising the dependency is not the
way out either.
The script arrives over the network, which is why the defaults are what they are:
standalone PAC libraries have been broken exactly there — pac-resolver
escaped its Node.js vm and reached RCE
(CVE-2021-23406) and pacparser
had CVE-2023-37360.
Implementations§
Source§impl PacPolicy
impl PacPolicy
Sourcepub fn new() -> Self
pub fn new() -> Self
The default policy: no DNS, no internal addresses, no real local IP, 5 s budget.
Sourcepub fn with_dns_resolution(self, enabled: bool) -> Self
pub fn with_dns_resolution(self, enabled: bool) -> Self
Allow dnsResolve, isResolvable and isInNet to perform name resolution.
With the default false, dnsResolve returns null and no DNS query is made.
Sourcepub fn with_internal_addresses(self, allowed: bool) -> Self
pub fn with_internal_addresses(self, allowed: bool) -> Self
Allow answers in internal space (loopback, RFC 1918, link-local, CGNAT, … and the
IPv4-mapped spelling of those). Default false drops them.
Classic dnsResolve is IPv4-only, so a native IPv6 answer — a ULA, say — never
reaches this flag either way. IP literals in the script are never filtered.
Sourcepub fn with_my_ip_address(self, address: IpAddr) -> Self
pub fn with_my_ip_address(self, address: IpAddr) -> Self
Set the address myIpAddress() reports. Unset → 127.0.0.1 (no auto-discovery).
Sourcepub fn with_timeout(self, timeout: Option<Duration>) -> Self
pub fn with_timeout(self, timeout: Option<Duration>) -> Self
Wall-clock budget, or None to remove it (blocks the caller if the script hangs).
Some(Duration::ZERO) is a budget of nothing, not the absence of one: every
evaluation answers Error::PacTimeout. None is how a
caller asks for no limit. WinHttpPacResolver::with_timeout answers zero the same
way — it is named in backticks rather than linked because that type exists only
under pac-windows-native on Windows, and a link to it fails the doc build
everywhere else.
The budget bounds the call, not the script. On expiry BoaEvaluator returns
Error::PacTimeout and abandons the evaluation thread,
which runs on until one of the VM limits stops it — so that thread, and whatever it
has allocated by then, outlives the call that asked for it. Nothing here interrupts
a running script: bounding the work is what the limits below are for.
Read that per call and it sounds like untidiness; the cost is in the aggregate.
Abandoned threads do not queue behind each other, so an application resolving
repeatedly against a script that always overruns holds roughly
overrun ÷ timeout of them at once, each spinning a core until its own VM limit
lands. With the defaults that ratio is not small: a while (true) {} reaches
DEFAULT_PAC_LOOP_LIMIT in about 93 seconds on an unoptimized build of this
crate, against a budget of DEFAULT_PAC_TIMEOUT. Shortening the budget
widens the ratio rather than narrowing it. What bounds this is a lower
PacPolicy::with_max_loop_iterations, or not letting the calls stack up.
Sourcepub fn with_max_loop_iterations(self, limit: u64) -> Self
pub fn with_max_loop_iterations(self, limit: u64) -> Self
Loop-iteration cap (u64::MAX disables). Honoured via boa_engine RuntimeLimits.
Iterations, not allocations — the same distinction
DEFAULT_PAC_STACK_SIZE_LIMIT draws. The limit is checked where the engine
re-enters a loop body, so whatever a builtin allocates within a call it never
reaches: a script that spends its memory in one String.prototype.repeat rather
than in a loop has nothing stopping it — the cap never fires, and
PacPolicy::with_timeout ends the wait rather than the work. That is the gap the
type doc’s “Heap unbounded” names.
Sourcepub fn with_recursion_limit(self, limit: usize) -> Self
pub fn with_recursion_limit(self, limit: usize) -> Self
Recursion depth cap. Default DEFAULT_PAC_RECURSION_LIMIT is boa’s own.
Sourcepub fn with_stack_size_limit(self, limit: usize) -> Self
pub fn with_stack_size_limit(self, limit: usize) -> Self
Value-stack entry cap (not bytes / OS stack). Default DEFAULT_PAC_STACK_SIZE_LIMIT.
Sourcepub fn with_local_utc_offset(self, seconds: i32) -> Self
pub fn with_local_utc_offset(self, seconds: i32) -> Self
Seconds from UTC treated as “local” for date/time predicates (default 0 = GMT).
Sourcepub fn with_now(self, now: SystemTime) -> Self
pub fn with_now(self, now: SystemTime) -> Self
Pin the clock the time-dependent host functions see.
Intended for tests and for reproducing a routing decision after the fact.
Sourcepub fn resolve_dns(&self) -> bool
pub fn resolve_dns(&self) -> bool
Whether name resolution is permitted.
Sourcepub fn allow_internal_addresses(&self) -> bool
pub fn allow_internal_addresses(&self) -> bool
Whether resolution results in internal address space are kept.
Sourcepub fn my_ip_address(&self) -> IpAddr
pub fn my_ip_address(&self) -> IpAddr
The address myIpAddress() reports, or 127.0.0.1 when unset.
Sourcepub fn max_loop_iterations(&self) -> u64
pub fn max_loop_iterations(&self) -> u64
The loop iteration cap.
Sourcepub fn recursion_limit(&self) -> usize
pub fn recursion_limit(&self) -> usize
The function-call recursion depth cap.
Sourcepub fn stack_size_limit(&self) -> usize
pub fn stack_size_limit(&self) -> usize
The cap on the engine’s internal value-stack length.
Sourcepub fn local_utc_offset(&self) -> i32
pub fn local_utc_offset(&self) -> i32
The offset from UTC, in seconds, that counts as local time.
Sourcepub fn now(&self) -> Option<SystemTime>
pub fn now(&self) -> Option<SystemTime>
The pinned clock, when with_now was used.
Trait Implementations§
impl Copy for PacPolicy
impl Eq for PacPolicy
impl StructuralPartialEq for PacPolicy
Auto Trait Implementations§
impl Freeze for PacPolicy
impl RefUnwindSafe for PacPolicy
impl Send for PacPolicy
impl Sync for PacPolicy
impl Unpin for PacPolicy
impl UnsafeUnpin for PacPolicy
impl UnwindSafe for PacPolicy
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.