1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Copyright 2026 Thomas Santerre and Moderately AI Inc.
//
// SPDX-License-Identifier: MIT OR Apache-2.0
/// Names that are blocked from use in interpreter code.
pub const DANGEROUS_NAMES: & = &;
/// Attribute names gated on all objects.
///
/// Every name here is rejected on **write** (`obj.attr = …`, `setattr`,
/// `delattr`, `__setattr__`) — the single funnel is
/// [`validate_attribute`](crate::security::validator::validate_attribute), which
/// every mutation site calls. On **read**, all of them are blocked too, EXCEPT
/// `__class__`: it aliases `type(x)`, which is already reachable via the
/// `type()` builtin, so reading it grants no capability the caller lacks. The
/// read alias is resolved by `crate::eval::names::resolve_object_attr` (a
/// universal object-level attribute) *before* the read-side validator runs;
/// `__class__` stays in this list purely so *assigning* it — in-sandbox type
/// confusion — remains blocked at every write site.
///
/// The remaining entries are the class-walk escape chain (`__bases__`,
/// `__mro__`, `__subclasses__`) and interpreter internals (`__globals__`,
/// `__code__`, `__closure__`, `__dict__`, …); reading any of them would hand
/// sandboxed code the object graph or the interpreter's own state, so they are
/// blocked in both directions. This is what severs
/// `().__class__.__bases__[0].__subclasses__()`-style probes: even though
/// `().__class__` now resolves (to `tuple`), `__bases__`/`__mro__`/
/// `__subclasses__` stay blocked, so the walk dead-ends immediately.
pub const BLOCKED_ATTRIBUTES: & = &;