-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathno_unsafe_code.rs
More file actions
173 lines (159 loc) · 6.14 KB
/
Copy pathno_unsafe_code.rs
File metadata and controls
173 lines (159 loc) · 6.14 KB
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! Workspace-level regression test for the `#![forbid(unsafe_code)]` posture.
//!
//! Lives in `security_core/tests/` because the workspace root has no
//! `[package]` block (it is a virtual manifest). Putting the test here gives it
//! the same execution surface — every CI test run executes it — without the
//! structural cost of converting the root into a hybrid root-package.
//!
//! Asserts that every workspace member's `lib.rs` (or `main.rs` for binary
//! crates with no library) declares `#![forbid(unsafe_code)]` within the first
//! 20 lines, before any item declaration. Removal of the attribute fails the
//! build with a named-crate error.
//!
//! This is the regression test for runbook
//! `docs/slo/future/RUNBOOK-forbid-unsafe-and-geiger.md` M1.
use std::fs;
use std::path::{Path, PathBuf};
/// Every workspace crate that ships Rust code; verified manually as of M1.
///
/// Explicit list (rather than parsing the root `Cargo.toml`) so the test fails
/// loudly if a crate is added without thinking about its unsafe posture.
const CRATES_REQUIRING_FORBID: &[&str] = &[
"security_core",
"secure_errors",
"security_events",
"secure_boundary",
"secure_authz",
"secure_data",
"secure_output",
"secure_identity",
"secure_device_trust",
"secure_reference_service",
"secure_smoke_service",
"secure_network",
"secure_resilience",
"secure_privacy",
];
/// Resolves the workspace root from the per-crate test working directory.
fn workspace_root() -> PathBuf {
// CARGO_MANIFEST_DIR points at `crates/security_core/`; go up two levels.
let manifest_dir = env!("CARGO_MANIFEST_DIR");
Path::new(manifest_dir)
.parent()
.and_then(Path::parent)
.expect("CARGO_MANIFEST_DIR must be two levels below workspace root")
.to_path_buf()
}
/// Returns the lib-root or main-root path for a crate, whichever exists.
fn crate_root_file(workspace: &Path, krate: &str) -> Option<PathBuf> {
let lib = workspace.join("crates").join(krate).join("src/lib.rs");
if lib.exists() {
return Some(lib);
}
let main = workspace.join("crates").join(krate).join("src/main.rs");
if main.exists() {
return Some(main);
}
None
}
#[test]
fn every_workspace_crate_forbids_unsafe_code() {
let workspace = workspace_root();
let mut failures: Vec<String> = Vec::new();
for krate in CRATES_REQUIRING_FORBID {
let Some(file) = crate_root_file(&workspace, krate) else {
failures.push(format!(
"crate `{krate}`: no lib.rs or main.rs found under crates/{krate}/src/",
));
continue;
};
let contents = match fs::read_to_string(&file) {
Ok(s) => s,
Err(err) => {
failures.push(format!(
"crate `{krate}`: cannot read {}: {err}",
file.display()
));
continue;
}
};
let head: String = contents.lines().take(20).collect::<Vec<_>>().join("\n");
if !head.contains("#![forbid(unsafe_code)]") {
failures.push(format!(
"crate `{krate}`: expected `#![forbid(unsafe_code)]` in the first 20 lines of {}; not found.\n\
Add the attribute at the top of the file (before any item declaration).\n\
If the crate genuinely needs unsafe, document the exception in \
docs/dev-guide/unsafe-budget.md and update CRATES_REQUIRING_FORBID.",
file.display()
));
}
}
assert!(
failures.is_empty(),
"forbid(unsafe_code) regression test failed for {} crate(s):\n - {}",
failures.len(),
failures.join("\n - ")
);
}
#[test]
fn no_unsafe_keyword_in_workspace_sources() {
// Sanity check: even with `forbid(unsafe_code)`, ensure no `unsafe ` keyword
// appears in any crate's `src/`. Catches macros or includes that might
// sneak unsafe in via a path the lint cannot see.
let workspace = workspace_root();
let mut found: Vec<String> = Vec::new();
for krate in CRATES_REQUIRING_FORBID {
let src_dir = workspace.join("crates").join(krate).join("src");
if !src_dir.exists() {
continue;
}
scan_for_unsafe(&src_dir, krate, &mut found);
}
assert!(
found.is_empty(),
"found `unsafe` keyword in workspace sources (must be empty under forbid(unsafe_code)):\n - {}",
found.join("\n - ")
);
}
fn scan_for_unsafe(dir: &Path, krate: &str, found: &mut Vec<String>) {
let Ok(entries) = fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
scan_for_unsafe(&path, krate, found);
continue;
}
if path.extension().and_then(|s| s.to_str()) != Some("rs") {
continue;
}
let Ok(contents) = fs::read_to_string(&path) else {
continue;
};
for (lineno, line) in contents.lines().enumerate() {
// Skip comments to avoid false positives from doc comments mentioning unsafe.
let trimmed = line.trim_start();
if trimmed.starts_with("//") || trimmed.starts_with("/*") || trimmed.starts_with("*") {
continue;
}
// Match `unsafe ` followed by `{`, `fn`, `impl`, `trait`, or another keyword
// — this is the actual code-form of the keyword, not a string literal.
if has_unsafe_keyword(line) {
found.push(format!(
"crate `{krate}`: {}:{} — {}",
path.display(),
lineno + 1,
line.trim()
));
}
}
}
}
fn has_unsafe_keyword(line: &str) -> bool {
// `unsafe ` followed by `{`, `fn`, `impl`, or `trait` — the keyword forms
// that mark an actual unsafe block / fn / impl / trait. Avoids matching
// identifiers like `assume_unsafe_aliasing` or string literals.
const FORMS: &[&str] = &["unsafe {", "unsafe fn", "unsafe impl", "unsafe trait"];
FORMS.iter().any(|f| line.contains(f))
}