## What does this PR do?
Fixes#431. Since #389, `swift build -c release` crashes the compiler on
Linux x86_64. The two `unimplemented()` calls in `UnimplementedSigner`
(`Sources/XKit/Signer/Signer.swift`) set it off:
- they pick the parameter-pack overload of `unimplemented`, with an
empty pack;
- that call, from an `async` function, hits a Swift compiler bug,
reported as swiftlang/swift#94055: `CoroSplitPass` emits an invalid GEP,
and LLVM's FunctionAttrs pass then crashes.
This PR replaces the two calls with what `unimplemented()` does when
called: `reportIssue(...)`, then `throw UnimplementedFailure(...)`. The
test dependency keeps the same behavior, and nothing changes outside it.
## How was it tested?
In upstream's own image (the `build-limd` stage of the `Dockerfile`, on
`swift:6.4.2-jammy`), x86_64:
- `swift build -c release --product xtool`: builds, and `xtool --help`
runs. Without this change it crashes, as described in #431.
- `swift test`: `Test run with 61 tests in 7 suites passed`.
- The bisect, the reduction to these two calls, and the minimal
reproducer are in #431 and swiftlang/swift#94055.
Not tested: macOS, arm64, signing on a device with this exact commit.
swiftlint was not run.
## AI tool usage
How much of the code in this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI in writing code. I reviewed the
finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
## What does this PR do?
Password login can terminate with an illegal-instruction crash on Linux,
leaving the user logged out. Anisette client time was formatted with
DateFormatter and an explicitly assigned Gregorian calendar. That
formatting path reaches Foundation’s NSCalendar bridging code, where the
reported crash occurs.
AnisetteData now formats client time with Date.ISO8601FormatStyle,
avoiding the DateFormatter calendar bridge. The header retains its UTC
timestamp format with whole seconds and a trailing Z. New parameterized
tests check the complete header dictionary for the Unix epoch, a year
boundary, leap day, and a timestamp with fractional seconds.
Fixes#184
## How was it tested?
Ran `swift test` locally on macOS for the xtool AnisetteData change; it
fails the same way on the base branch, so the failure predates this
change (it fails the same way on upstream/main).
`swift build` failed because an async call in a defer body prevented
compilation.
`swift test` failed during compilation with the same async-in-defer
error, so the tests did not run.
`repo_lint` ran without reported output.
`make lint SWIFTLINT_FLAGS='--reporter=github-actions-logging' | tee
swiftlint.log` ran with no lint diagnostics shown in the transcript.
The tests in `Tests/XToolTests/AnisetteDataTests.swift` fail without the
fix in the xtool AnisetteData change (checked by reverting the source
changes).
## AI tool usage
How much of the code in this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI in writing code. I reviewed the
finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
AI was used for assistance.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Bug Fixes**
* Client timestamps now use a consistent UTC ISO 8601 format with dashed
dates, colon-separated times, and no fractional seconds. This applies to
timestamps with or without fractional seconds in the source date.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
## What does this PR do?
Fixes issues with missing devices and duplicate names when creating new
profiles. Supersedes #427 and #428.
Fixes#425, fixes#426
## How was it tested?
- [x] Ran locally
- [x] Confirmed that free accounts also support multiple profiles as
long as the names are different.
## AI tool usage
How much of the code in this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI in writing code. I reviewed the
finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Bug Fixes**
* Profile creation now uses only enabled devices that support iOS apps.
* Existing profiles with the exact generated profile name are removed
before a replacement is created, including when multiple matches exist.
* Profile handling now stops when the operation is canceled.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Valentin Brosseau <c4software@gmail.com>
## What does this PR do?
I'm not sure if this is needed but, for good measure, updates remaining
GSA requests to use an ephemeral client (followup from #247)
## How was it tested?
- [x] Tested flows involving all of these requests
## AI tool usage
How much of the code in this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI in writing code. I reviewed the
finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Improvements**
* Selected account and lookup requests now use temporary HTTP
connections. Existing request details, response handling, and progress
reporting remain supported, while other requests continue through the
standard connection flow. This change applies across the app’s related
account and lookup operations; no new user-facing controls or workflows
are introduced.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Applies the 2FA SMS login fixes from #405
## How was it tested?
- [x] 2FA SMS arrives
- [x] Can log in with SMS code
## AI tool usage
How much of the code in this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI in writing code. I reviewed the
finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Improvements**
* SMS verification requests now use an updated phone verification format
and numeric phone-number IDs.
* Code validation also uses numeric phone-number IDs, keeping the
identifier format consistent across SMS authentication steps. The
verification request and code-validation request now follow the same
updated format.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Corne Steenhuis <cornesteenhuis@hotmail.com>
## What does this PR do?
Renames GrandSlamMethod to GrandSlamRequestContent and adds support for
json & PUT requests
## How was it tested?
- [x] Still compiles
- [x] `xtool auth`
## AI tool usage
How much of the code in this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI in writing code. I reviewed the
finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Requests can now send either JSON or XML bodies using POST or PUT. GET
requests are sent without a body.
* **Improvements**
* Authentication and verification requests now use a consistent request
format while retaining their existing behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Replaces zsign with the
[apple-codesign](https://github.com/indygreg/apple-platform-rs/tree/0ebbd2ef3b96d5adccc30b1cad4deea38cd735c4/apple-codesign)
Rust crate. It's much more usable as a library (while zsign has bare
prints all over the place + we had to fork it to maintain a public
interface), it supports macOS-style bundles, and also has other features
that we would've needed to further patch zsign for.
## How was it tested?
- [x] Ran an ad-hoc signed app on the simulator (with a subsequent PR)
- [x] Ran an app with App Groups on device, confirmed that the app group
is present
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Updated Signing**
* App signing now uses an updated signing engine and supports
certificate-based and ad hoc signing.
* Entitlement data, including data provided through entitlement files,
is passed through during signing.
* Deep signing is enabled in the development command.
* **Validation**
* Signing rejects invalid app or entitlement file paths and duplicate
entitlement paths.
* **Compatibility**
* Signing contexts can be created without selecting a signer.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Resolves the FIXME in `DeveloperServicesCapability`: App Groups are no
longer filtered out on free teams, and
`DeveloperServicesAddAppOperation` assigns at most one group per app on
a free team (the constraint the FIXME mentioned).
Free teams can register and assign app groups through the Xcode
DeveloperServices API (as AltStore/SideStore do), so a widget and its
app can share `UserDefaults(suiteName:)` with a free Apple ID.
## How was it tested?
Built on Linux from `main` (4d0c429). Installed a Flutter app with a
WidgetKit extension on an iPhone (iOS 27.2) with a free Apple ID: the
app gets `group.XTL-<team>.<id>` in its entitlements and the widget
reads the app's data through it. `make lint` not run (no SwiftLint on
this machine).
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Bug Fixes**
* App Groups are now available to free teams.
* Apps assigned to multiple App Groups are rejected with a clear error
message.
* Invalid app errors now include the app’s URL path, making it easier to
identify which app could not be added.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kabir Oberai <oberai.kabir@gmail.com>
Cesar Andres Pereira Bernal
·
2026-10-30 02:11:54 -04:00
## The bug
`DeveloperServicesAssignAppGroupsOperation` sends one
`assignApplicationGroupToAppId` request per app group, concurrently.
That endpoint replaces the App ID's groups with the ones in the request,
so an app whose entitlements list two or more groups ends up with only
one of them on the App ID: whichever request Apple handles last. The
provisioning profile then carries one group while the signed binary
claims two, and the device refuses the install with
`ApplicationVerificationFailed`.
Setting the groups by hand in the developer portal does not help,
because the next `xtool dev build --sign` sends the per-group requests
again.
## The fix
- `DeveloperServicesAssignAppGroupRequest` takes `groupIDs:
[DeveloperServicesAppGroup.ID]` and sends `applicationGroups` as an
array. The endpoint accepts an array; it was being given a single string
before.
- `DeveloperServicesAssignAppGroupsOperation.perform()` still finds or
creates each group concurrently, then sends a single assign request
carrying every group ID, and returns the group IDs in the order the
entitlements listed them, so the rewritten entitlement is stable between
builds.
The only caller, `DeveloperServicesAddAppOperation`, is unchanged.
## Related
#324 would report the resulting mismatch between the entitlements and
the profile as a clear error at signing time; this change removes the
cause, so an app with several groups gets a profile that grants all of
them.
## Checked
Built on Linux with the Swift 6.4 toolchain and used to sign an iOS app
whose main target has two app groups and a Share extension. The app's
`embedded.mobileprovision` now carries both groups and the device
installs the app. The same app signed with the unpatched build got one
group in the profile and `ApplicationVerificationFailed` on install.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01UKzxgxaLwK7Ap5LMwj9MxH
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Apps can now be assigned multiple app groups in a single operation.
The assignment replaces the app’s existing group associations with the
selected groups, so any groups not included in the new assignment are no
longer associated with the app.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: David Reinecke <david.reinecke@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Kabir Oberai <oberai.kabir@gmail.com>
## What does this PR do?
Small utils refactors to make subsequent PRs cleaner.
- Minor tweaks to how we determine / handle the xtool build version
- Factor out TemporaryDirectory cache location
## How was it tested?
- [x] Cache directory logic is unchanged
- [x] Checked `xtool --version`
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Version information now distinguishes clean builds from builds with
uncommitted changes, displaying a `-dirty` suffix when applicable.
* Nonblocking file-locking operations are now available to external
consumers.
* **Changes**
* Version reporting uses `XTOOL_VERSION` when set; otherwise, it uses
available Git tag or commit information.
* Temporary files now use the tool’s cache directory, honoring
`XTL_TMPDIR` and platform cache settings. On Linux, the location follows
`XDG_CACHE_HOME` when it is an absolute path; otherwise, the user cache
directory is used.
* `TMPDIR` is no longer used to select the temporary-file location.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Adds `Dependency`s for console IO. Also fixes#268 by explicitly
handling non-pipe input.
## How was it tested?
Ran on macOS + Linux in Docker. I have a future PR that overrides the
dependencies in certain cases, which also appears to work correctly.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
Did not scrutinize the tests too much tbh but they look reasonable. Easy
enough to update/remove them in the future if needed.
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Added support for reading command-line input, including asynchronous
prompts, line-based responses, and graceful handling of end-of-input.
* Added coordinated cleanup when commands finish.
* **Improvements**
* Command messages across authentication, device management,
installation, SDK, and other workflows now use consistent output
handling; displayed messages and command behavior remain unchanged.
* Improved handling of complete output when the system temporarily
cannot accept more data.
* **Tests**
* Added coverage for command input and output, including stream
behavior, cancellation, and shutdown.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Fixes the latest source of HTTP 503s during Apple ID login
## How was it tested?
- [x] `xtool auth login`
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Improvements**
* Requests now use the user-agent value provided with device
information, rather than a fixed value.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Hi! xtool didn't know about the `com.apple.developer.applesignin`
entitlement, so every time I installed my app, the capability sync
removed Sign in with Apple from the App ID. The new provisioning profile
didn't have it, and the device refused the app ("A valid provisioning
profile for this executable was not found").
This maps the entitlement to the `APPLE_ID_AUTH` capability, as a
primary App ID, so the capability stays and the profile includes it.
## How was it tested?
I built xtool from this branch on macOS and installed my app (a SwiftPM
app with `entitlementsPath` and the Sign in with Apple entitlement) on
an iPhone with a paid team. The App ID kept the capability, the install
worked, and native Sign in with Apple works in the app. Before the
change the same install failed every time.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Added support for configuring the Apple Sign In entitlement.
* Apple Sign In is now available as a supported entitlement type,
including serialization and configuration support.
* Configures Apple ID app consent as the primary app consent option.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Fixes#378, solving error -80046. Seems Apple now blocks
Android-identifying clients from using gsa (at least the 2fa bits). Use
Mac Anisette instead. cf
https://github.com/nab138/iloader/commit/902597564315442f00ab299dcc758ee0e13b8fa8
Alas, this does mean we have to retire xadi and switch back to
Omnisette.
## How was it tested?
- [x] Ran locally on macOS
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Bug Fixes**
* Two-factor requests now use the device’s configured User-Agent.
* **Updates**
* Anisette requests now use the Omnisette provider and a new default
server. The optional server URL override remains available.
* The password-login warning now explains the virtual Mac and
third-party service involved, clarifies that credentials are sent only
to Apple, and recommends considering a secondary Apple ID.
* **Behavior Changes**
* The Android-specific ADI provider is no longer available.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
`secondaryAuth` can't finish logging in: GSA rejects POST
/auth/verify/phone/put?mode=sms with HTTP 403 ("Could not connect to
iCloud"). The JSON flavor of those endpoints (GET /auth, PUT
/auth/verify/phone/) answers 401 as well.
GsService2/validate still accepts a code generated on a trusted device
(Settings > Sign-In & Security > Get Verification Code), so ask for one
instead of failing. Tested on WSL (Ubuntu 26.04) with an iOS 27 device.
## What does this PR do?
<!-- Focus on *what* the PR does in essence. The *how* should come
second, if it's relevant. -->
## How was it tested?
<!-- e.g. Ran locally on macOS and added unit tests -->
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Two-factor authentication can fall back to a trusted device when an
SMS request returns HTTP 403.
* If trusted-device authentication also returns HTTP 403, users receive
instructions for manually generating a verification code, and code
validation can continue. Other errors are still reported.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Kabir Oberai <oberai.kabir@gmail.com>
## What does this PR do?
Simplifies the Anisette handling code. Removes some unnecessary fields
(including hard-coded Mac serial numbers, cf #348) from our http
requests amongst other things.
## How was it tested?
Tested on macOS:
- [x] Can log in after resetting provisioning
- [x] Can run `xtool ds teams`
- [x] Can run `xtool ds certificates`
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Updates**
* Anisette-backed requests now use device information provided with
Anisette data, including an Android 14 device identity.
* Developer service requests no longer include client-information and
device-ID headers.
* The Anisette command now displays fetched details in sorted order and
uses the configured Anisette provider instead of prompting for
provisioning data.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Fixes#305 (thanks @0x53 for the detailed diagnosis).
`Certificate.serialNumber()` stripped leading `0` *characters* from the
hex string, so a serial like `0337…` became `337…` and never matched the
`serialNumber` returned by the API. xtool then treated its stored
certificate as missing: free teams are prompted to revoke a valid
certificate on every run, and paid teams take the same path and try to
create another certificate, which @McoreD confirmed fails with `409 …
You already have a current Development certificate or a pending
certificate request.`
The character-level `drop` predates b6a40ca, which switched to
fixed-width `%02hhX` formatting; since then it removes the significant
zero of any first byte below `0x10`. swift-certificates already strips
leading zero bytes (including DER sign padding) when parsing, so the
`drop` isn't needed for padding.
- `serialNumber()` now drops only leading zero *bytes*, matching the API
and `openssl x509 -serial`.
- The stored-certificate lookup uses a new
`Certificate.hasSerialNumber(_:)`, which compares serials numerically
(ignoring case and leading zeros), so it doesn't depend on Apple's exact
formatting.
- Profile creation no longer finds the signing certificate with
`filter[serialNumber]`. The API lists serials with the leading zero
(`0E41…`), but the filter only matches them without it (`E41…`), so once
`serialNumber()` kept the zero the filter matched nothing and profile
creation failed with a 409
([details](https://github.com/xtool-org/xtool/pull/306#issuecomment-6181157860)).
It now fetches all pages of certificates and matches them with
`hasSerialNumber(_:)`. Fix by @McoreD, with pagination added so a
certificate on a later page isn't missed.
## How was it tested?
- Added `Tests/XToolTests/CertificateTests.swift`: builds a cert with
the serial from #305, round-trips it through DER, and checks
`serialNumber()` and `hasSerialNumber(_:)`, plus a high-bit serial so
DER sign padding doesn't leak into the string. The leading-zero test
fails on `main` (returns `33742570D6F9684A23311711956A084`) and passes
with this change.
- `swift test` (17 tests) and `make lint` pass on macOS (Xcode 27, Swift
6.4, `XTL_CI=1`).
- @McoreD tested on Linux (Arch, Swift 6.4.1, Xcode 27 SDK, on top of
1.22.2) with a paid team whose certificate serial starts with `0`: the
stored certificate is reused, and provisioning, signing and install to
an iPhone (iOS 27) succeed end to end (tested before pagination was
added). `CertificateTests` also pass on Linux.
- Not tested with a free team.
To generate test certificates, the test target now depends on `Crypto`
and `X509`, which XKit already uses.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Certificate serial numbers now preserve leading zero nibbles while
omitting unnecessary padding.
* Certificate matching now ignores letter case and leading zeros, and
profile lookups find matching certificates even when the API’s
serial-number filter treats leading-zero values differently.
* Certificate lookups continue to use the existing fallback when no
matching certificate is found.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: McoreD <McoreD@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## What does this PR do?
Adds Apple G2 & G3 Root CAs to Linux trust root. Fixes#339.
Apple now uses gsd.apple.com for part of the auth flow, which uses the
G3 root.
## How was it tested?
Tried to log in on Linux. Failed before, works after.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved TLS certificate trust on Linux by recognizing Apple Root CA,
G2, and G3 certificates when verifying secure connections. Existing TLS
validation behavior remains unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Fixes#329 (error `-80045` on gsa login).
We were sending client info generated from Android Anisette, but
claiming to be a Mac. Seems like GrandSlam no longer likes this
mismatch.
## How was it tested?
- [x] Run `xtool ds certificates` after updating -> logged out,
provisioning reset
- [x] Re-auth works
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Updates**
* Authentication now identifies the client as a Pixel 7 running Android
14 rather than using a macOS-style client description.
* The provider ID is now consistent across platforms. Existing
provisioning may be reset if its saved provider ID differs.
* Before password login, a notice explains the emulated device and
environment, recommends considering a secondary Apple ID, and says
credentials are sent only to Apple.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Updates SwiftLint to v0.65.1
## How was it tested?
CI
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Removed inactive legacy networking implementations; active app
behavior and user-facing capabilities are unchanged.
* Updated the configured SwiftLint version and improved selection of its
Linux download for supported processor architectures. macOS continues to
use its existing download.
* No changes to app features or user workflows are included in this
update.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Bumps pointfree deps and explicitly disables OpenCombine so that we
don't have different dependency graphs when building xtool on Darwin vs
Linux hosts.
## How was it tested?
A bunch of `swift package resolve`s
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Updated supporting package versions and refreshed command-line and
file-operation handling.
* These changes do not add or remove app features or alter its public
interface. No action is needed to continue using the app.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Updates macOS build to use the fully-local XADI as the Anisette provider
instead of Omnisette. Includes a one-time migration, logging the user
out on macOS when they upgrade.
## How was it tested?
General:
- [x] Log in and confirm that there's no calls to omnisette
- [x] `xtool dev` works
Migration:
- [x] Log into Apple ID using an old version of xtool
- [x] Migrate to new xtool, run `xtool dev build -s` and confirm it
fails due to being logged out
- [x] Log in on new xtool, confirm that we stay logged in
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Authentication now supports tracking the anisette provider used to
create Xcode tokens.
* Xcode authentication tokens are automatically cleared when the
provider changes, helping ensure users sign in again with current
credentials.
* Anisette provider identification is supported across macOS and Linux.
* **Bug Fixes**
* Improved authentication error messages, including clearer messaging
for logout-related errors.
* Prevented outdated Xcode authentication tokens from being reused after
a provider change.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
The xadi repo (https://github.com/xtool-org/xadi) now vends an
artifactbundle for xadi, so we don't need to separately build/install it
during the xtool build. This also makes it super easy to support macOS
later (#259).
## How was it tested?
- [x] Confirmed I can sign in (including provisioning)
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
## What does this PR do?
Fixes HTTP 503 on GsService… again (followup from #244)
Seems like GSA has stopped accepting `com.apple.dt.Xcode` in the client
info string. Use `akd` instead.
## How was it tested?
- [x] Logging in before failed
- [x] Logging in after succeeds
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
## What does this PR do?
Improves support for HTTP MITM
## How was it tested?
Did some MITMing on macOS + Linux via Docker, using Proxyman and
mitmproxy. Didn't test `XTL_HTTP_PROXY` but Sol pinkie promises me that
it's correct.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
Ref #243. `makeRequest` never inspected `response.status`, so a non-2xx
body was
returned as if it had succeeded. Every caller decodes that body as a
property list
or JSON, so Apple answering with an HTML error page surfaced as:
```
Error: DecodingError.dataCorrupted: Data was corrupted. Debug description: The given
data was not a valid property list.. Underlying error: ... Encountered unknown tag
html on line 1
```
which says nothing about what actually happened. In the case that
prompted #243, GSA
was returning `503 Service Temporarily Unavailable` for the `apptokens`
operation. It
now reads:
```
Error: POST https://gsa.apple.com/grandslam/GsService2 failed: HTTP 503 Service
Unavailable (text/html): <html> <head><title>503 Service Temporarily
Unavailable</title></head> ...
```
### Notes
- The response body is only included in the message for `text/html` and
`text/plain`,
i.e. gateway error pages. Bodies from the auth endpoints can carry
account details
(the 2FA requests use `Accept: application/x-buddyml`), and errors get
printed
verbatim in places such as `DevCommand.swift:164`, so those are left
out. It also
avoids emitting replacement characters for binary bodies.
- The non-2xx path does not report progress, so `XADIProvider`'s
download no longer
prints `100%` immediately before failing.
- Body collection moved to `collect(_:onProgress:)` so there is a single
exit point.
Buffer pre-sizing and progress reporting are unchanged.
- `SDKBuilder` already does this check by hand on `send()`, so it is
unaffected.
### Behaviour changes worth a look
- `XADIProvider` writes the Apple Music APK download straight to disk; a
failed
download now throws instead of leaving a corrupt `applemusic.apk`.
- The 2FA requests (`GrandSlamTrustedDeviceRequest`,
`GrandSlamSMSAuthRequest`,
`GrandSlamValidateRequest`) use no-op decoders, so a rejected request is
currently
discarded and xtool goes on to prompt for a code that was never sent —
the symptom
in #235. Those now fail loudly.
### Two things I left alone
- `GrandSlamTwoFactorAuthenticateOperation.swift:47` catches
`GrandSlamOperationError`
with code `-21669` to report an incorrect verification code, but that
error is only
thrown by `GrandSlamOperationDecoder`, which the validate requests do
not use, so the
catch is unreachable. With this change a wrong code surfaces as a raw
HTTP error
instead. Mapping the status back to `incorrectVerificationCode` seemed
like your call,
since I do not know what GSA returns for a bad code.
- If any endpoint reports errors in a decodable body under a non-2xx
status, this guard
would pre-empt it. I could not find one — `developerservices2` returns
HTTP 200 with
`resultCode`/`userString` in the body, which is untouched here.
Built and linted on Linux (`swift build --product xtool`, `make lint`: 0
violations).
---------
Co-authored-by: Robetronica <noreply@Robetronica.com>
Co-authored-by: Kabir Oberai <oberai.kabir@gmail.com>
Ref #216: the public API seems to now complain when passing in
`capabilities`, but the field is still required when using the private
API.
Co-authored-by: sfusco <slf@stevenfusco.com>
- We already do the `isFree` declarations in
`DeveloperServiceCapability.swift`. Don't need to duplicate it on the
entitlement types.
- Add support for creating capabilities with raw names
- We now have a single tmpdir "root" that can be recreated at launch to
clean up old stragglers
- The location of this tmpdir root can be controlled by `XTL_TMPDIR` or
`TMPDIR`. In general the path is `$TMPDIR/sh.xtool`.
With this change it should be possible to `export XTL_TMPDIR=/var/tmp`
if `/tmp` doesn't have enough space, which fixes#23.
Previously this was shadowing the user's cwd path onto the device, e.g.
if you were installing from `/home/foo/bar` it would create
`/home/foo/bar/PublicStaging` on the device. For uniformity with other
tools like `ideviceinstaller`, clean this up and just create
`/PublicStaging`.
This was vestigial, and besides the fact that we don't actually do any
work in this stage, we also never update the progress here so it gets
stuck at 0%.
- Always try to register the device and just return gracefully if it
fails (prevents races, pagination issues)
- Allow `SigningContext` (and therefore `AutoSigner`) to be created
without a target device: just skip device registration.
We pull the list of App IDs in `upsertApp` to see if we already created
the App ID. This broke when the user had too many App IDs, since DS
returned a paginated response and we only looked at the first page.
We could paginate the whole list (as seen in #89) but that's slow.
Instead, just filter the query to the expected App ID. This is a prefix
search so we might still receive multiple responses, but it's much less
likely that the user has like 10(? I'm not sure what the default/max
pagination limit is) apps registered with the XTL-ABC.com.foo.bar prefix
(they could have PlugIns but usually just a small number of them.)
This is a tentative fix for #87.