Skip to content

Commit 7e619a5

Browse files
author
Awesome
committed
feat: finalize XenSpace cleanup and Geo IP experience
1 parent 58762fe commit 7e619a5

29 files changed

Lines changed: 1848 additions & 461 deletions

‎AppDataCleaner.m‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ - (void)clearKeychainTargets:(NSArray<NSString *> *)targets
877877
freshRequestForBundleIdentifier:targets[index]
878878
profileID:profileID
879879
generationID:generationID
880-
includeSharedAccessGroups:NO
880+
includeSharedAccessGroups:YES
881881
includeSynchronizableItems:NO
882882
now:[NSDate date]
883883
ttl:30

‎AppVersionSpoofingViewController.m‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ - (void)loadAppsData {
388388
- (void)addButtonTapped {
389389
// Open scope tab where apps can be added
390390
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Add Apps"
391-
message:@"Please add apps to the scope list using the ProjectX tab (HomeTab)."
391+
message:@"Please add apps to the scope list using the XenSpace tab (HomeTab)."
392392
preferredStyle:UIAlertControllerStyleAlert];
393393

394394
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];

‎Info.plist‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
<key>CFBundleInfoDictionaryVersion</key>
1212
<string>6.0</string>
1313
<key>CFBundleName</key>
14-
<string>ProjectX</string>
14+
<string>XenSpace</string>
15+
<key>CFBundleDisplayName</key>
16+
<string>XenSpace</string>
1517
<key>CFBundlePackageType</key>
1618
<string>APPL</string>
1719
<key>CFBundleShortVersionString</key>

‎KeychainCommand.m‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,11 @@ + (instancetype)planForBundleIdentifier:(NSString *)bundleIdentifier
279279
@"Process application identifier has an invalid team prefix");
280280
return nil;
281281
}
282-
283282
NSMutableOrderedSet<NSString *> *approvedGroups = [NSMutableOrderedSet
284-
orderedSetWithObject:applicationIdentifier];
283+
orderedSet];
284+
if ([accessGroups containsObject:applicationIdentifier]) {
285+
[approvedGroups addObject:applicationIdentifier];
286+
}
285287
NSMutableSet<NSString *> *sharedGroups = [NSMutableSet set];
286288
if (includeSharedAccessGroups) {
287289
for (id candidate in accessGroups) {
@@ -294,6 +296,11 @@ + (instancetype)planForBundleIdentifier:(NSString *)bundleIdentifier
294296
[sharedGroups addObject:candidate];
295297
}
296298
}
299+
if (approvedGroups.count == 0) {
300+
PXKeychainCommandFail(error, PXKeychainCommandErrorRejected,
301+
@"No signed Keychain access group is permitted for this request");
302+
return nil;
303+
}
297304

298305
NSArray<NSString *> *keychainClasses = @[
299306
PXKeychainClassGenericPassword,

‎KeychainCommandTests.m‎

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,7 @@ static void testOneShotControllerClearsVintedSignedCustomGroup(void) {
356356
@"requestID": request.requestID,
357357
@"targetBundleID": request.targetBundleID,
358358
@"success": @YES,
359-
@"results": successfulOneShotResults(
360-
@[applicationIdentifier, vintedAccessGroup],
361-
vintedAccessGroup
362-
),
359+
@"results": successfulOneShotResults(@[vintedAccessGroup], vintedAccessGroup),
363360
@"failureCode": @"",
364361
@"protectedSharedAccessGroupCount": @0
365362
} error:nil];
@@ -377,7 +374,9 @@ static void testOneShotControllerClearsVintedSignedCustomGroup(void) {
377374
assert(response.protectedSharedAccessGroupCount == 0);
378375
assert(execution.invocationCount == 1);
379376
assert(([execution.lastWorkerEntitlements[@"keychain-access-groups"] isEqualToArray:
380-
@[applicationIdentifier, vintedAccessGroup]]));
377+
@[vintedAccessGroup]]));
378+
assert(![execution.lastWorkerEntitlements[@"keychain-access-groups"]
379+
containsObject:applicationIdentifier]);
381380
}
382381

383382
static void testOneShotEntitlementPlanRejectsDuplicateSignedGroups(void) {
@@ -585,7 +584,63 @@ - (int32_t)deleteItemsForClass:(NSString *)keychainClass
585584

586585
@end
587586

588-
static void testOneShotControllerRejectsIncompleteProtectedGroupCleanup(void) {
587+
@interface VintedCustomGroupSecurityAdapter : SuccessfulSecurityAdapter
588+
@property (nonatomic, strong) NSMutableSet<NSString *> *deletedGroups;
589+
@end
590+
591+
@implementation VintedCustomGroupSecurityAdapter
592+
593+
- (instancetype)init {
594+
self = [super init];
595+
if (self) _deletedGroups = [NSMutableSet set];
596+
return self;
597+
}
598+
599+
- (NSDictionary<NSString *, id> *)entitlementsForBundleIdentifier:(NSString *)bundleIdentifier
600+
error:(NSError **)error {
601+
(void)error;
602+
assert([bundleIdentifier isEqualToString:@"lt.manodrabuziai.fr"]);
603+
return @{
604+
@"application-identifier": @"4Y2CNF6C99.lt.manodrabuziai.fr",
605+
@"keychain-access-groups": @[@"4Y2CNF6C99.com.vinted.keychain-group"]
606+
};
607+
}
608+
609+
- (int32_t)deleteItemsForClass:(NSString *)keychainClass
610+
accessGroup:(NSString *)accessGroup
611+
synchronizable:(BOOL)synchronizable {
612+
assert([accessGroup isEqualToString:@"4Y2CNF6C99.com.vinted.keychain-group"]);
613+
[self.deletedGroups addObject:accessGroup];
614+
return [super deleteItemsForClass:keychainClass
615+
accessGroup:accessGroup
616+
synchronizable:synchronizable];
617+
}
618+
619+
@end
620+
621+
static void testExecutorClearsOnlyVintedSignedCustomGroup(void) {
622+
NSString *bundleIdentifier = @"lt.manodrabuziai.fr";
623+
PXKeychainCommandRequest *request = [PXKeychainCommandRequest
624+
requestWithPropertyList:freshRequestPropertyList(bundleIdentifier)
625+
error:nil];
626+
VintedCustomGroupSecurityAdapter *adapter = [[VintedCustomGroupSecurityAdapter alloc] init];
627+
PXKeychainCommandExecutor *executor = [[PXKeychainCommandExecutor alloc]
628+
initWithValidator:[[PXKeychainCommandValidator alloc] init]
629+
securityAdapter:adapter];
630+
NSError *error = nil;
631+
PXKeychainCommandResponse *response = [executor
632+
executeRequest:request
633+
context:context(bundleIdentifier, YES, NO)
634+
now:[NSDate dateWithTimeIntervalSince1970:1010]
635+
error:&error];
636+
assert(response.isSuccessful);
637+
assert(error == nil);
638+
assert(response.results.count == 5);
639+
assert([adapter.deletedGroups isEqualToSet:
640+
[NSSet setWithObject:@"4Y2CNF6C99.com.vinted.keychain-group"]]);
641+
}
642+
643+
static void testOneShotControllerReportsProtectedGroupForExclusiveCleanup(void) {
589644
NSMutableDictionary<NSString *, id> *propertyList =
590645
[freshRequestPropertyList(@"com.example.target") mutableCopy];
591646
propertyList[@"includeSharedAccessGroups"] = @NO;
@@ -600,6 +655,15 @@ static void testOneShotControllerRejectsIncompleteProtectedGroupCleanup(void) {
600655
@"TEAM123.group.example.shared"
601656
]
602657
};
658+
execution.response = [PXKeychainCommandResponse responseWithPropertyList:@{
659+
@"schemaVersion": @(PXKeychainCommandSchemaVersion),
660+
@"requestID": request.requestID,
661+
@"targetBundleID": request.targetBundleID,
662+
@"success": @YES,
663+
@"results": successfulOneShotResults(@[@"TEAM123.com.example.target"], @""),
664+
@"failureCode": @"",
665+
@"protectedSharedAccessGroupCount": @0
666+
} error:nil];
603667
NSError *error = nil;
604668
PXKeychainOneShotResponse *response = [[[PXKeychainOneShotController alloc]
605669
initWithExecution:execution]
@@ -608,9 +672,12 @@ static void testOneShotControllerRejectsIncompleteProtectedGroupCleanup(void) {
608672
now:[NSDate dateWithTimeIntervalSince1970:1010]
609673
error:&error];
610674

611-
assert(response == nil);
612-
assert([error.domain isEqualToString:PXKeychainOneShotErrorDomain]);
613-
assert(execution.invocationCount == 0);
675+
assert(response.isSuccessful);
676+
assert(error == nil);
677+
assert(response.protectedSharedAccessGroupCount == 1);
678+
assert(execution.invocationCount == 1);
679+
assert([execution.lastWorkerEntitlements[@"keychain-access-groups"] isEqualToArray:
680+
@[@"TEAM123.com.example.target"]]);
614681
}
615682

616683

@@ -1289,7 +1356,8 @@ int main(void) {
12891356
testOneShotEntitlementPlanRejectsDuplicateSignedGroups();
12901357
testOneShotControllerDoesNotLeakGroupsAcrossMultipleSelectedApps();
12911358
testOneShotControllerRejectsSynchronizableCleanupBeforeExecution();
1292-
testOneShotControllerRejectsIncompleteProtectedGroupCleanup();
1359+
testOneShotControllerReportsProtectedGroupForExclusiveCleanup();
1360+
testExecutorClearsOnlyVintedSignedCustomGroup();
12931361
testOneShotControllerRejectsWorkerResultsOutsideExactApplicationGroup();
12941362
testFullSuccessRequiresPostDeleteVerificationForEveryScope();
12951363
testPartialFailureIsNotSuccessAndResponseContainsNoItemMetadata();

‎Makefile‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ internal-stage::
133133
@chmod 755 $(THEOS_STAGING_DIR)/usr/bin/weaponx-debug
134134
@/bin/sh "$(CURDIR)/scripts/check_roothide.sh" --check-staging "$(THEOS_STAGING_DIR)"
135135
@/bin/sh "$(CURDIR)/scripts/check_roothide.sh" --check-worker-binary "$(THEOS_STAGING_DIR)"
136+
@/bin/sh "$(CURDIR)/tests/run_geo_ip_location_tests.sh"
137+
@python3 "$(CURDIR)/tests/SmartLocationGeoIPTests.py"
138+
@python3 "$(CURDIR)/tests/HomeDeviceInfoTests.py"
139+
@python3 "$(CURDIR)/tests/SettingsAboutIconLayoutTests.py"
140+
@python3 "$(CURDIR)/tests/SettingsPendingBannerTests.py"
141+
@python3 "$(CURDIR)/tests/LocalizationParityTests.py"
136142
@/bin/sh "$(CURDIR)/tests/run_keychain_one_shot_execution_tests.sh" \
137143
"$(THEOS_STAGING_DIR)/Library/WeaponX/ProjectXKeychainWorker"
138144

‎PXGeoIPLocation.h‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#import <Foundation/Foundation.h>
2+
3+
NS_ASSUME_NONNULL_BEGIN
4+
5+
FOUNDATION_EXPORT NSString *const PXGeoIPLocationErrorDomain;
6+
7+
typedef NS_ENUM(NSInteger, PXGeoIPNetworkFamily) {
8+
PXGeoIPNetworkFamilyIPv4 = 4,
9+
PXGeoIPNetworkFamilyIPv6 = 6
10+
};
11+
12+
@interface PXGeoIPLocation : NSObject
13+
14+
@property (nonatomic, copy, readonly) NSString *publicIPAddress;
15+
@property (nonatomic, copy, readonly, nullable) NSString *ipv4Address;
16+
@property (nonatomic, copy, readonly, nullable) NSString *ipv6Address;
17+
@property (nonatomic, copy, readonly) NSString *city;
18+
@property (nonatomic, copy, readonly) NSString *region;
19+
@property (nonatomic, copy, readonly) NSString *country;
20+
@property (nonatomic, copy, readonly) NSString *countryCode;
21+
@property (nonatomic, copy, readonly) NSString *timeZoneIdentifier;
22+
@property (nonatomic, copy, readonly) NSString *address;
23+
@property (nonatomic, readonly) double latitude;
24+
@property (nonatomic, readonly) double longitude;
25+
26+
+ (nullable instancetype)locationWithJSONDictionary:(NSDictionary<NSString *, id> *)dictionary
27+
error:(NSError * _Nullable * _Nullable)error;
28+
+ (nullable instancetype)locationWithPolicyRepresentation:(NSDictionary<NSString *, id> *)dictionary
29+
error:(NSError * _Nullable * _Nullable)error;
30+
- (NSDictionary<NSString *, id> *)policyRepresentation;
31+
- (NSDictionary<NSString *, id> *)policyRepresentationWithIPv4Address:(nullable NSString *)ipv4Address
32+
ipv6Address:(nullable NSString *)ipv6Address;
33+
- (nullable NSString *)publicIPAddressForFamily:(PXGeoIPNetworkFamily)family;
34+
35+
@end
36+
37+
@interface PXGeoIPLocationService : NSObject
38+
39+
- (instancetype)init;
40+
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration
41+
endpoint:(NSURL *)endpoint;
42+
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration
43+
endpoint:(NSURL *)endpoint
44+
ipv4Endpoint:(NSURL *)ipv4Endpoint
45+
ipv6Endpoint:(NSURL *)ipv6Endpoint NS_DESIGNATED_INITIALIZER;
46+
- (NSURLSessionDataTask *)fetchCurrentLocationWithCompletion:
47+
(void (^)(PXGeoIPLocation * _Nullable location, NSError * _Nullable error))completion;
48+
- (NSURLSessionDataTask *)fetchPublicIPAddressForFamily:(PXGeoIPNetworkFamily)family
49+
completion:(void (^)(NSString * _Nullable address,
50+
NSError * _Nullable error))completion;
51+
52+
@end
53+
54+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)