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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! Plugin manifest types -- the user-facing contract that plugin files declare.
//!
//! A plugin file sets `globalThis.exports = { ... }` whose JSON-shaped subset
//! deserialises into [`PluginManifest`]. The `handler` field on the JS side
//! is intentionally NOT part of this struct -- it carries an executable
//! closure that only makes sense inside a live `QuickJS` context, so the
//! loader strips it before extraction.
use HashMap;
use ;
/// Manifest extracted from a plugin's `globalThis.exports` declaration.
///
/// Field naming follows the JS convention (`camelCase`) since that's what
/// plugin authors type. The Rust side stays `snake_case` via serde rename.
/// Declarative capability manifest bundled with the plugin.
///
/// This is the plugin sandbox's opt-in authority list: each named
/// capability is independently scoped and Rust-enforced at the binding
/// boundary, so the handler source alone cannot grant itself a privilege
/// it did not declare. Capabilities are additive and default to the
/// least-privilege value, so an absent field is back-compatible.
///
/// Covered today:
/// - **exec** (`commands`): named shell-command templates. Default-deny —
/// a `commands.run(name)` for an undeclared `name` throws.
/// - **net**: host allow-list for the handler's `request` HTTP client.
/// Empty = unrestricted (opt-in: declaring any host switches that
/// binding to default-deny). Scopes the `request` binding only;
/// `page`/`context` browser navigation is a separate, deliberately
/// ungated authority (an automation plugin must be able to navigate) —
/// see `docs/plugin-architecture.md` for why `fs` is not a capability
/// here (the handler context exposes no filesystem handle to gate).