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
//! App.MenuItem protobuf payload decoder.
//!
//! The `App.MenuItem` Biome stream (`~/Library/Biome/streams/restricted/App.MenuItem/local`)
//! was introduced in macOS Tahoe 26. Each SEGB record's payload is a protobuf-encoded
//! message whose meaningful fields are:
//!
//! | Field | Wire type | Interpretation |
//! |-------|--------------------|-----------------------------|
//! | 1 | 2 (length-delimited) | Application name (UTF-8) |
//! | 2 | 2 (length-delimited) | Menu-item text (UTF-8) |
//!
//! # Field number provenance
//!
//! Apple has not published a `.proto` schema for `App.MenuItem`. The field
//! numbers above are **inferred** from:
//!
//! 1. The forensicnomicon catalog (`macos_biome_app_menuitem`) which names
//! `application` and `menu_item` as the two meaningful text fields.
//! 2. The Unit 42 research article (Palo Alto Networks, 2026):
//! <https://unit42.paloaltonetworks.com/new-macos-artifact-discovered/>
//! which describes the payload as capturing "the application name" and
//! "the exact text of menu items selected by the user."
//! 3. Standard protobuf practice: a two-field string message with no nesting
//! assigns field 1 to the first semantic attribute and field 2 to the second.
//!
//! **Validation against a real macOS Tahoe 26 sample is PENDING** — see
//! `docs/validation.md`.
//!
//! # Fallback
//!
//! Fields whose numbers are not 1 or 2 are silently skipped — the decoder is
//! forward-compatible with additional fields Apple may add. If field 1 or 2
//! contains bytes that are not valid UTF-8 they are not decoded (returned as
//! `None`); the caller can inspect the raw bytes if needed.
use crate::;
/// A decoded `App.MenuItem` payload.
/// Decode an `App.MenuItem` protobuf payload.
///
/// `payload` is the raw bytes from a [`crate::SegbRecord`] payload.
/// `timestamp_unix` is forwarded from the SEGB record's timestamp field.
///
/// # Errors
///
/// Returns `Err` only on a structurally malformed protobuf (truncated varint,
/// overflowing length-delimited field). An absent field 1 or 2 is **not** an
/// error — `application` / `menu_item` will be `None`.
/// Decode a collection of raw SEGB payloads into `AppMenuItemRecord`s.
///
/// `records` is an iterator over `(payload: &[u8], timestamp_unix: Option<f64>)` pairs.
/// Errors from individual records are returned immediately (fail-fast).
/// An error variant used when the payload cannot be decoded because the
/// protobuf structure does not match the expected `App.MenuItem` message.
///
/// Currently this is surfaced as `SegbError::MalformedVarint` or
/// `SegbError::ProtobufOverflow` from [`iter_fields`] — this function wraps
/// those cleanly.