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
103
104
105
106
107
//! The response headers a packaged application serves.
//!
//! ## Why the policy is not in `tauri.conf.json`
//!
//! Tauri's `app.security.csp` applies to the documents Tauri itself serves
//! through its asset protocol. A packaged Rahti application does not use it:
//! every document comes from the embedded HTTP server, on a `http://127.0.0.1`
//! origin, and the only Content-Security-Policy a browser will apply to those
//! is one that arrives in *their* response headers.
//!
//! So it is set here, by the server that serves them. The value in
//! `rahti.native.json` is the one that reaches the page; the value in the
//! generated `tauri.conf.json` covers the shell's own placeholder document and
//! is the same string so that neither can be mistaken for the other.
//!
//! ## Why it matters more here than on the web
//!
//! A cross-site scripting bug in a web page steals a session. The same bug in
//! a native shell reaches the native command bridge as well. The policy is the
//! layer that stops injected markup from loading an attacker's script at all,
//! and it is worth being strict about in exactly the place where being wrong
//! costs the most.
use OnceLock;
use Request;
use ;
use Next;
use Response;
static POLICY: = new;
/// Install the policy the [`security_headers`] layer will send.
///
/// Called once by the native host, before the router is built. A second call
/// is ignored: the policy belongs to the launch, and a layer whose policy
/// could change mid-run is a layer whose behaviour cannot be reasoned about.
/// The installed policy, if there is one.
/// Add the security headers to every response.
///
/// Wired by the native host, outside the application's own layers:
///
/// ```ignore
/// router.layer(axum::middleware::from_fn(rahti_native::security_headers))
/// ```
///
/// Existing values are left alone. A page that set its own policy meant it,
/// and a layer that overwrote it would be a layer that quietly widened
/// somebody's deliberate narrowing.
pub async
/// Put a Rahti router behind the native security layers.
///
/// One call rather than two `.layer(...)` lines in every generated shell, for
/// two reasons. The shell then needs no `axum` dependency of its own — it
/// never names a type from it — and the *order* of the two layers is decided
/// here, where it can be tested, rather than in generated code where getting
/// it backwards would be invisible.
///
/// The order: `axum` applies the last `.layer` outermost, so the gate is added
/// second and runs first. A request that did not come from this launch is
/// refused before it reaches the auth guard, the CSRF layer, a handler, or the
/// static file service.
///
/// `loopback_token` is `security.loopbackToken`. When it is off the layer is
/// not in the router at all, rather than present and deciding it does not
/// apply.