use super::*;
#[test]
fn canonical_consts_match_the_convention() {
for svc in SERVICES.iter().chain(RETIRED_SERVICES) {
if svc.sub_unit.is_some() {
continue;
}
assert_eq!(
svc.label,
canonical_label(svc.member),
"{}'s label restates something the `com.trusty.<stem>` convention \
does not produce — either the convention changed (update \
`canonical_label`) or the literal drifted (#4919)",
svc.member
);
}
}
#[test]
fn sub_unit_labels_extend_their_base() {
for svc in SERVICES {
let Some(sub) = svc.sub_unit else { continue };
let base = canonical_label(svc.member);
assert_eq!(
svc.label,
sub_label(&base, sub),
"{}'s `{sub}` sub-unit must be named off its member's base label",
svc.member
);
}
}
#[test]
fn legacy_labels_are_never_canonical() {
for svc in SERVICES {
for legacy in svc.legacy {
assert!(
service_for_label(legacy).is_none(),
"{legacy} is listed as a legacy alias of {} but is also some \
service's canonical label — evicting it would take down a \
live unit",
svc.label
);
}
}
}
#[test]
fn every_legacy_label_resolves_to_one_service() {
let mut seen: Vec<&str> = Vec::new();
for svc in SERVICES.iter().chain(RETIRED_SERVICES) {
for label in std::iter::once(&svc.label).chain(svc.legacy.iter()) {
assert!(
!seen.contains(label),
"{label} is claimed by more than one service"
);
seen.push(label);
}
}
}
#[test]
fn retired_services_are_not_installed() {
for retired in RETIRED_SERVICES {
assert!(
!SERVICES.iter().any(|s| s.member == retired.member),
"{} is retired but still listed as a service an install writes",
retired.member
);
assert!(
retired_service_for_member(retired.member).is_some(),
"{} must be reachable by member lookup, or nothing can evict it",
retired.member
);
}
let members: Vec<&str> = RETIRED_SERVICES.iter().map(|s| s.member).collect();
assert_eq!(
members,
vec!["trusty-review", "trusty-analyze"],
"adding a retirement is a deliberate act — update this pin with it"
);
}
#[test]
fn retired_review_carries_both_its_labels() {
let labels = retired_labels_for_member("trusty-review");
assert_eq!(labels, vec![REVIEW, "com.trusty.trusty-review"]);
assert!(
retired_labels_for_member("trusty-search").is_empty(),
"a live member has nothing to evict"
);
assert!(
legacy_labels_for(REVIEW).contains(&"com.trusty.trusty-review"),
"the retired row's legacy alias must still resolve through the label lookup"
);
}
#[test]
fn pre_fix_labels_are_recorded_as_legacy() {
assert!(
legacy_labels_for(SEARCH).contains(&"com.trusty.trusty-search"),
"the label `trusty-search service install` wrote before #4919 must be \
evicted on upgrade"
);
assert!(
legacy_labels_for(SEARCH).contains(&"com.bobmatnyc.trusty-search"),
"the trusty-search Makefile's `com.bobmatnyc.*` family must be evicted \
on upgrade"
);
assert!(legacy_labels_for(CONSOLE).contains(&"com.trusty.trusty-console"));
assert!(legacy_labels_for(REVIEW).contains(&"com.trusty.trusty-review"));
assert!(legacy_labels_for(SEARCH_LOGROTATE).contains(&"com.trusty.trusty-search.logrotate"));
}
const SCAN_EXEMPT_PATHS: &[&str] = &["trusty-installer/src/commands/macos_signing"];
#[test]
fn no_stray_launchd_label_literals_in_workspace_sources() {
let root = workspace_root();
let mut files = Vec::new();
collect_scannable_files(&root, &mut files);
let rs = files.iter().filter(|p| kind_of(p) == Kind::Rust).count();
let other = files.len() - rs;
assert!(
rs > 2000 && other > 20,
"the scan found {rs} Rust and {other} build/deploy file(s) under {} — a \
broken walk would pass this test vacuously",
root.display()
);
let mut strays: Vec<String> = Vec::new();
for path in &files {
let rel = path
.strip_prefix(&root)
.unwrap_or(path)
.display()
.to_string();
if SCAN_EXEMPT_PATHS.iter().any(|ex| rel.contains(ex)) {
continue;
}
if rel.contains("trusty-common/src/launchd_labels") {
continue;
}
let Ok(body) = std::fs::read_to_string(path) else {
continue;
};
strays.extend(strays_in(&rel, kind_of(path), &body));
}
assert!(
strays.is_empty(),
"launchd label literals not owned by `trusty_common::launchd_labels` \
(#4919 — derive them from the registry instead of restating them).\n\
CHECK THE NAMESPACE FIRST (#5438): a CODESIGN identifier is not a launchd \
label. Codesign identifiers keep the full binary name on purpose \
(`com.trusty.trusty-memory`, not `com.trusty.memory`) and must NEVER be \
renamed onto the registry's convention — that invalidates the binary's \
designated requirement and re-triggers macOS TCC prompts (#2558). Bind one \
to a `readonly <NAME>_IDENTIFIER=` constant instead, which is the naming \
`codesign_stripped` exempts and \
`codesign_scripts_name_identifiers_by_convention` enforces. In a plist the \
same split runs off the KEY (#6540): launchd reads a job label from \
`<key>Label</key>` and nowhere else, so a `CFBundleIdentifier` value is \
already exempt — a hit under any other key is a real launchd label.\n \
{}",
strays.join("\n ")
);
}
fn strays_in(rel: &str, kind: Kind, body: &str) -> Vec<String> {
let mut lines = production_lines(body, kind);
if kind == Kind::Xml {
lines = bundle_identifier_stripped(lines);
}
let mut out = Vec::new();
for line in lines {
let evicting = kind != Kind::Rust && (line.contains("bootout") || line.contains("unload"));
for label in extract_labels(&codesign_stripped(&line)) {
if evicting && legacy_labels_for_any().contains(&label.as_str()) {
continue;
}
if kind != Kind::Rust && is_canonical_label(&label) {
continue;
}
out.push(format!("{rel}: {label}"));
}
}
out
}
const PLIST_BUNDLE_IDENTIFIER_KEYS: &[&str] = &["CFBundleIdentifier"];
fn bundle_identifier_stripped(lines: Vec<String>) -> Vec<String> {
let mut out: Vec<String> = Vec::with_capacity(lines.len());
let mut awaiting_value = false;
for line in lines {
let key_end = PLIST_BUNDLE_IDENTIFIER_KEYS.iter().find_map(|k| {
let marker = format!("<key>{k}</key>");
line.find(&marker).map(|i| i + marker.len())
});
match key_end {
Some(end) if line[end..].contains("<string>") => {
awaiting_value = false;
out.push(blank_plist_string(&line, end));
}
Some(_) => {
awaiting_value = true;
out.push(line);
}
None if awaiting_value && line.contains("<string>") => {
awaiting_value = false;
out.push(blank_plist_string(&line, 0));
}
None => {
if line.contains("<key>") {
awaiting_value = false;
}
out.push(line);
}
}
}
out
}
fn blank_plist_string(line: &str, from: usize) -> String {
const OPEN: &str = "<string>";
const CLOSE: &str = "</string>";
let Some(open) = line[from..].find(OPEN).map(|i| from + i + OPEN.len()) else {
return line.to_string();
};
let Some(close) = line[open..].find(CLOSE).map(|i| open + i) else {
return line.to_string();
};
let mut out = line.to_string();
out.replace_range(open..close, "");
out
}
#[test]
fn plist_bundle_identifier_is_not_a_launchd_label() {
let body = "<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleIdentifier</key>\n\
\t<string>com.trusty.console.saver</string>\n\
\t<key>CFBundleName</key>\n\t<string>TrustyConsole</string>\n\
</dict>\n</plist>\n";
let found = strays_in("crates/x/macos/saver/Info.plist", Kind::Xml, body);
assert!(
found.is_empty(),
"a CFBundleIdentifier is the codesign namespace, not a launchd label: {found:?}"
);
let bare = strays_in(
"crates/x/macos/saver/Info.plist",
Kind::Xml,
"<string>com.trusty.console.saver</string>\n",
);
assert_eq!(
bare,
vec!["crates/x/macos/saver/Info.plist: com.trusty.console.saver".to_string()],
"only a bundle-identifier key earns the exemption"
);
}
#[test]
fn plist_label_key_value_is_still_a_stray() {
let body = "\t<key>Label</key>\n\t<string>com.trusty.fixture-unregistered</string>\n";
assert_eq!(
strays_in("deploy/x.plist", Kind::Xml, body),
vec!["deploy/x.plist: com.trusty.fixture-unregistered".to_string()],
"a Label value must stay scanned"
);
let after = "\t<key>CFBundleIdentifier</key>\n\t<key>Label</key>\n\
\t<string>com.trusty.fixture-unregistered</string>\n";
assert_eq!(
strays_in("deploy/x.plist", Kind::Xml, after).len(),
1,
"the pending exemption must end at the next key"
);
}
#[test]
fn bundle_identifier_stripped_spares_a_label_on_the_same_line() {
let line = "<key>CFBundleIdentifier</key><string>com.trusty.console.saver</string>\
<key>Label</key><string>com.trusty.fixture-unregistered</string>";
let out = bundle_identifier_stripped(vec![line.to_string()]).remove(0);
assert!(
!out.contains("com.trusty.console.saver"),
"the bundle identifier must be exempt, got: {out}"
);
assert!(
out.contains("com.trusty.fixture-unregistered"),
"a launchd label on the same line must still be scanned, got: {out}"
);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Kind {
Rust,
Hash,
Xml,
}
fn production_lines(body: &str, kind: Kind) -> Vec<String> {
let mut out = Vec::new();
let mut lines = body.lines().peekable();
let mut in_block_comment = false;
while let Some(line) = lines.next() {
if kind == Kind::Rust && !in_block_comment && is_test_cfg_attribute(line) {
skip_test_item(line, &mut lines);
continue;
}
let code = strip_comment(line, kind, &mut in_block_comment);
if !code.trim().is_empty() {
out.push(code);
}
}
out
}
fn is_test_cfg_attribute(line: &str) -> bool {
let t = line.trim_start();
if !t.starts_with("#[cfg(") {
return false;
}
let is_ident = |c: char| c.is_ascii_alphanumeric() || c == '_' || c == '-';
let mut rest = t;
while let Some(idx) = rest.find("test") {
let before = &rest[..idx];
let before_ok = before.chars().next_back().is_none_or(|c| !is_ident(c));
let after_ok = rest[idx + 4..].chars().next().is_none_or(|c| !is_ident(c));
if before_ok && after_ok && !under_negation_or_disjunction(before) {
return true;
}
rest = &rest[idx + 4..];
}
false
}
fn under_negation_or_disjunction(prefix: &str) -> bool {
let bytes = prefix.as_bytes();
let mut depth: i32 = 0;
let mut open_combinators: Vec<i32> = Vec::new();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'(' {
let head = prefix[..i].trim_end();
if head.ends_with("not") || head.ends_with("any") {
open_combinators.push(depth);
}
depth += 1;
} else if bytes[i] == b')' {
depth -= 1;
open_combinators.retain(|d| *d < depth);
}
i += 1;
}
!open_combinators.is_empty()
}
fn skip_test_item<'a>(
attr_line: &str,
lines: &mut std::iter::Peekable<impl Iterator<Item = &'a str>>,
) {
let tail = attr_line
.rsplit_once("])")
.map_or_else(|| attr_line.rsplit_once(']').map(|(_, t)| t), |_| None)
.unwrap_or("")
.trim();
if !tail.is_empty() {
let opens = tail.matches('{').count();
let closes = tail.matches('}').count();
if tail.ends_with(';') || (opens > 0 && opens == closes) {
return;
}
if opens > closes {
consume_until_balanced(lines, i32::try_from(opens - closes).unwrap_or(1));
return;
}
}
let mut depth: i32 = 0;
let mut opened = false;
for line in lines.by_ref() {
depth += i32::try_from(line.matches('{').count()).unwrap_or(0);
depth -= i32::try_from(line.matches('}').count()).unwrap_or(0);
if line.contains('{') {
opened = true;
}
if opened {
if depth <= 0 {
return;
}
} else if line.trim_end().ends_with(';') {
return;
}
}
}
fn consume_until_balanced<'a>(
lines: &mut std::iter::Peekable<impl Iterator<Item = &'a str>>,
mut depth: i32,
) {
for line in lines.by_ref() {
depth += i32::try_from(line.matches('{').count()).unwrap_or(0);
depth -= i32::try_from(line.matches('}').count()).unwrap_or(0);
if depth <= 0 {
return;
}
}
}
fn strip_comment(line: &str, kind: Kind, in_block_comment: &mut bool) -> String {
let t = line.trim_start();
match kind {
Kind::Rust => {
if *in_block_comment {
if let Some((_, after)) = line.split_once("*/") {
*in_block_comment = false;
return after.to_string();
}
return String::new();
}
if t.starts_with("//") {
return String::new();
}
if let Some((before, rest)) = line.split_once("/*") {
if let Some((_, after)) = rest.split_once("*/") {
return format!("{before}{after}");
}
*in_block_comment = true;
return before.to_string();
}
line.to_string()
}
Kind::Hash => line.split('#').next().unwrap_or("").to_string(),
Kind::Xml => {
if t.starts_with("<!--") {
String::new()
} else {
line.to_string()
}
}
}
}
fn kind_of(path: &std::path::Path) -> Kind {
let name = path.file_name().unwrap_or_default().to_string_lossy();
if name.ends_with(".rs") {
Kind::Rust
} else if name.ends_with(".plist") {
Kind::Xml
} else {
Kind::Hash
}
}
fn workspace_root() -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(2)
.expect("crates/trusty-common has a workspace root two levels up")
.to_path_buf()
}
fn collect_scannable_files(dir: &std::path::Path, out: &mut Vec<std::path::PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
let name = entry.file_name();
let name = name.to_string_lossy();
if path.is_dir() {
if matches!(
name.as_ref(),
"target"
| "tests"
| "benches"
| "node_modules"
| "docs"
| ".git"
| ".claude"
| "test-data"
| "testdata"
| "vmtest-harness"
) {
continue;
}
collect_scannable_files(&path, out);
continue;
}
let is_rust = name.ends_with(".rs")
&& !name.ends_with("_tests.rs")
&& !name.ends_with("_test.rs")
&& name != "tests.rs";
let is_build_or_deploy = name == "Makefile"
|| name.ends_with(".sh")
|| name.ends_with(".plist")
|| name.ends_with(".yml");
if is_rust || is_build_or_deploy {
out.push(path);
}
}
}
#[test]
fn production_lines_skips_past_a_test_module_declaration() {
let body = "pub mod a;\n#[cfg(test)]\nmod tests;\npub mod b;\nlet x = \"deep\";\n";
let kept = production_lines(body, Kind::Rust);
assert!(
kept.iter().any(|l| l.contains("deep")),
"code below a `mod tests;` declaration must still be scanned, kept: {kept:?}"
);
assert!(!kept.iter().any(|l| l.contains("mod tests;")));
}
#[test]
fn production_lines_strips_an_inline_test_block() {
let body = "fn real() {}\n#[cfg(all(test, target_os = \"macos\"))]\nmod tests {\n let f = \"com.trusty.trusty-fixture\";\n}\nfn after() {}\n";
let kept = production_lines(body, Kind::Rust);
assert!(!kept.iter().any(|l| l.contains("trusty-fixture")));
assert!(
kept.iter().any(|l| l.contains("fn after")),
"code after the test block must survive, kept: {kept:?}"
);
}
#[test]
fn production_lines_keeps_a_feature_cfg_that_merely_contains_test() {
assert!(!is_test_cfg_attribute(
"#[cfg(feature = \"embedder-test-support\")]"
));
assert!(is_test_cfg_attribute("#[cfg(test)]"));
assert!(is_test_cfg_attribute(
"#[cfg(all(test, target_os = \"macos\"))]"
));
let body = "#[cfg(feature = \"embedder-test-support\")]\npub fn kept() {}\n";
let kept = production_lines(body, Kind::Rust);
assert!(
kept.iter().any(|l| l.contains("pub fn kept")),
"a feature cfg must not swallow the item it guards, kept: {kept:?}"
);
}
#[test]
fn production_lines_strips_hash_comments() {
let body = "# was com.trusty.trusty-search\nPLIST := com.trusty.search.plist\n";
let kept = production_lines(body, Kind::Hash);
assert_eq!(kept.len(), 1);
assert!(kept[0].contains("com.trusty.search"));
}
#[test]
fn is_test_cfg_attribute_respects_polarity() {
assert!(is_test_cfg_attribute("#[cfg(test)]"));
assert!(is_test_cfg_attribute(
"#[cfg(all(test, target_os = \"macos\"))]"
));
assert!(
!is_test_cfg_attribute("#[cfg(not(test))]"),
"`not(test)` gates code present in every non-test build"
);
assert!(
!is_test_cfg_attribute(
"#[cfg(any(all(target_os = \"macos\", target_arch = \"aarch64\"), test))]"
),
"`any(…, test)` gates code present outside test builds too"
);
assert!(!is_test_cfg_attribute("#[cfg(all(not(test), unix))]"));
}
#[test]
fn production_lines_reads_bodies_gated_on_not_test() {
let body =
"#[cfg(not(test))]\nfn cache_base_dir() {\n let x = \"com.trusty.trusty-search\";\n}\n";
let kept = production_lines(body, Kind::Rust);
assert!(
kept.iter().any(|l| l.contains("com.trusty.trusty-search")),
"a `not(test)` body is production and must be scanned, kept: {kept:?}"
);
}
#[test]
fn strip_comment_keeps_a_deref_assignment() {
let body = "*target = \"com.trusty.trusty-search\".to_string();\n";
let kept = production_lines(body, Kind::Rust);
assert!(
kept.iter().any(|l| l.contains("com.trusty.trusty-search")),
"a deref assignment is code, not a comment continuation, kept: {kept:?}"
);
}
#[test]
fn strip_comment_tracks_block_comment_state() {
let body = "/*\n * com.trusty.trusty-search was the old label\n */\nlet a = 1;\n";
let kept = production_lines(body, Kind::Rust);
assert!(
!kept.iter().any(|l| l.contains("com.trusty.trusty-search")),
"a block-comment body must stay unscanned, kept: {kept:?}"
);
assert!(kept.iter().any(|l| l.contains("let a = 1")));
}
#[test]
fn skip_test_item_consumes_nothing_when_the_item_is_on_the_attribute_line() {
let body = "#[cfg(test)] use std::fmt;\nlet x = \"com.trusty.trusty-search\";\n";
let kept = production_lines(body, Kind::Rust);
assert!(
kept.iter().any(|l| l.contains("com.trusty.trusty-search")),
"the line after a self-contained test item is production, kept: {kept:?}"
);
}
#[test]
fn codesign_stripped_spares_only_the_identifier_token() {
let line = "local X_IDENTIFIER=\"com.trusty.trusty-mpm\"; local f2=\"/x/com.bobmatnyc.trusty-search.plist\"";
let out = codesign_stripped(line);
assert!(
!out.contains("com.trusty.trusty-mpm"),
"the codesign identifier must be exempt, got: {out}"
);
assert!(
out.contains("com.bobmatnyc.trusty-search"),
"a launchd label on the same line must still be scanned, got: {out}"
);
let flag = codesign_stripped("codesign --identifier com.trusty.trusty-search /bin/x");
assert!(!flag.contains("com.trusty.trusty-search"), "got: {flag}");
}
fn legacy_labels_for_any() -> Vec<&'static str> {
SERVICES
.iter()
.flat_map(|s| s.legacy.iter().copied())
.collect()
}
#[test]
fn eviction_lines_may_name_a_legacy_label() {
let evict = "\t-launchctl bootout gui/$$(id -u)/com.trusty.trusty-search 2>/dev/null\n";
let install = "PLIST := $(HOME)/Library/LaunchAgents/com.trusty.trusty-search.plist\n";
let kept = production_lines(evict, Kind::Hash);
assert!(
!kept.is_empty(),
"the bootout line must survive comment stripping"
);
assert!(
legacy_labels_for_any().contains(&"com.trusty.trusty-search"),
"the alias must be registered for the eviction allowance to apply"
);
let install_kept = production_lines(install, Kind::Hash);
assert!(!install_kept[0].contains("bootout"));
}
fn codesign_stripped(line: &str) -> String {
const MARKERS: &[&str] = &["--identifier", "IDENTIFIER="];
let mut out = line.to_string();
for marker in MARKERS {
while let Some(idx) = out.find(marker) {
let after = idx + marker.len();
let rest = &out[after..];
let val_start = rest
.find(|c: char| !matches!(c, ' ' | '=' | '"' | '\'' | '\t'))
.unwrap_or(rest.len());
let tail = &rest[val_start..];
let val_end = tail
.find(|c: char| !(c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_'))
.unwrap_or(tail.len());
let abs_start = after + val_start;
let abs_end = abs_start + val_end;
out.replace_range(abs_start..abs_end, "");
out.replace_range(idx..after, &"_".repeat(marker.len()));
}
}
out
}
fn extract_labels(line: &str) -> Vec<String> {
const PREFIXES: &[&str] = &["com.trusty.", "com.bobmatnyc."];
let mut found = Vec::new();
for prefix in PREFIXES {
let mut rest = line;
while let Some(idx) = rest.find(prefix) {
let tail = &rest[idx..];
let end = tail
.find(|c: char| !(c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_'))
.unwrap_or(tail.len());
let token = tail[..end]
.trim_end_matches('.')
.trim_end_matches(".plist")
.trim_end_matches('.');
if token.len() > prefix.len() {
found.push(token.to_string());
}
rest = &rest[idx + prefix.len()..];
}
}
found
}
#[test]
fn eviction_outcome_only_failed_is_a_failure() {
assert!(!EvictionOutcome::Evicted.is_failure());
assert!(!EvictionOutcome::Absent.is_failure());
assert!(EvictionOutcome::Failed("still loaded after bootout".into()).is_failure());
let e = LabelEviction::new("com.trusty.review", EvictionOutcome::Absent);
assert_eq!(e.label, "com.trusty.review");
assert_eq!(e.outcome, EvictionOutcome::Absent);
}
#[test]
fn retired_analyze_carries_both_its_labels() {
assert!(
retired_service_for_member("trusty-analyze").is_some(),
"trusty-analyze runs on demand since #6350 and installs no unit"
);
assert_eq!(
retired_labels_for_member("trusty-analyze"),
vec![ANALYZE, "com.trusty.trusty-analyze"]
);
assert!(
retired_labels_for_member("trusty-memory").is_empty(),
"a live member has nothing to evict"
);
assert_eq!(
legacy_labels_for(ANALYZE),
&["com.trusty.trusty-analyze"],
"an eviction needs the retired row's legacy list just as an install needs a live one's"
);
}
fn codesign_scripts(root: &std::path::Path) -> Vec<std::path::PathBuf> {
let mut out = Vec::new();
let Ok(entries) = std::fs::read_dir(root.join("scripts")) else {
return out;
};
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_none_or(|e| e != "sh") {
continue;
}
if std::fs::read_to_string(&path).is_ok_and(|b| b.contains("codesign")) {
out.push(path);
}
}
out.sort();
out
}
fn shell_var_name(arg: &str) -> Option<String> {
let arg = arg.strip_prefix('$')?;
let arg = arg.strip_prefix('{').unwrap_or(arg);
let end = arg
.find(|c: char| !(c.is_ascii_alphanumeric() || c == '_'))
.unwrap_or(arg.len());
(end > 0).then(|| arg[..end].to_string())
}
fn shell_assignments(line: &str) -> Vec<(String, String)> {
let bytes = line.as_bytes();
let mut out = Vec::new();
for (idx, _) in line.match_indices('=') {
let head = &line[..idx];
let name_start = head
.rfind(|c: char| !(c.is_ascii_alphanumeric() || c == '_'))
.map_or(0, |i| i + 1);
let name = &head[name_start..];
if name.is_empty() || name.starts_with(|c: char| c.is_ascii_digit()) {
continue;
}
if name_start > 0 && bytes[name_start - 1] == b'-' {
continue;
}
let tail = &line[idx + 1..];
let value = tail.split(';').next().unwrap_or(tail);
out.push((name.to_string(), value.to_string()));
}
out
}
fn identifier_convention_violations(rel: &str, body: &str) -> Vec<String> {
const FLAG: &str = "--identifier";
let mut out = Vec::new();
for line in production_lines(body, Kind::Hash) {
for (idx, _) in line.match_indices(FLAG) {
let arg = line[idx + FLAG.len()..].trim_start_matches([' ', '=', '"', '\'', '\t']);
let Some(name) = shell_var_name(arg) else {
continue;
};
if !name.to_ascii_uppercase().ends_with("IDENTIFIER") {
out.push(format!(
"{rel}: `--identifier \"${name}\"` — a codesign identifier must reach the \
flag through a variable whose name ends in `_IDENTIFIER`, so its binding \
site is exempt from the launchd-label scan (#5438)"
));
}
}
let evicting = line.contains("bootout") || line.contains("unload");
for (name, value) in shell_assignments(&line) {
if name.to_ascii_uppercase().ends_with("_IDENTIFIER") {
continue;
}
for label in extract_labels(&value) {
if is_canonical_label(&label) {
continue;
}
if evicting && legacy_labels_for_any().contains(&label.as_str()) {
continue;
}
out.push(format!(
"{rel}: `{name}=\"{label}\"` — a codesign identifier must be bound to a \
`readonly <NAME>_IDENTIFIER=` constant, never renamed onto the launchd \
convention (#2558); if `{label}` really is a launchd label, take it from \
`trusty_common::launchd_labels` instead (#5438)"
));
}
}
}
out
}
#[test]
fn codesign_scripts_name_identifiers_by_convention() {
let root = workspace_root();
let scripts = codesign_scripts(&root);
assert!(
scripts.len() >= 3,
"expected the codesigning scripts under {}/scripts, found {}",
root.display(),
scripts.len()
);
assert!(
scripts.iter().any(|p| p
.file_name()
.is_some_and(|n| n.to_string_lossy().ends_with("-signed.sh"))),
"the signed installers #5438 was filed over must stay in scope, found: {scripts:?}"
);
let mut bindings = 0usize;
let mut violations: Vec<String> = Vec::new();
for path in &scripts {
let rel = path
.strip_prefix(&root)
.unwrap_or(path)
.display()
.to_string();
let Ok(body) = std::fs::read_to_string(path) else {
continue;
};
for line in production_lines(&body, Kind::Hash) {
bindings += shell_assignments(&line)
.iter()
.filter(|(n, _)| n.to_ascii_uppercase().ends_with("_IDENTIFIER"))
.count();
}
violations.extend(identifier_convention_violations(&rel, &body));
}
assert!(
bindings > 0,
"no `*_IDENTIFIER=` binding found in any of {} script(s) — the scan would \
have nothing to exempt, so the walk is reading the wrong files",
scripts.len()
);
assert!(
violations.is_empty(),
"codesign identifiers bound outside the `*_IDENTIFIER` naming the \
launchd-label scan exempts:\n {}",
violations.join("\n ")
);
}
#[test]
fn identifier_convention_violations_flag_an_unmarked_binding() {
let body = "local mem_id=\"com.trusty.trusty-memory\"\n\
codesign --force --identifier \"$mem_id\" \"$bin\"\n";
let found = identifier_convention_violations("scripts/install-fixture-signed.sh", body);
assert_eq!(
found.len(),
2,
"the binding and the flag are both violations, got: {found:?}"
);
assert!(
found.iter().any(|v| v.contains("com.trusty.trusty-memory")),
"the failure must name the label, got: {found:?}"
);
assert!(
found.iter().all(|v| v.contains("IDENTIFIER")),
"the failure must state the expected naming, got: {found:?}"
);
let binding = production_lines(body, Kind::Hash).remove(0);
assert!(
!extract_labels(&codesign_stripped(&binding)).is_empty(),
"this is the binding the label scan reads as a stray: {binding}"
);
}
#[test]
fn identifier_convention_violations_accept_the_repaired_shape() {
let body = "readonly MEMORY_IDENTIFIER=\"com.trusty.trusty-memory\"\n\
codesign --identifier \"$MEMORY_IDENTIFIER\" \"$bin\"\n\
codesign --identifier com.trusty.trusty-memory \"$bin\"\n\
local plist=\"$HOME/Library/LaunchAgents/com.trusty.memory.plist\"\n\
# local narrated=\"com.trusty.trusty-memory\"\n";
let found = identifier_convention_violations("scripts/install-fixture-signed.sh", body);
assert!(found.is_empty(), "expected no violations, got: {found:?}");
}