Skip to content

Commit 49a5def

Browse files
6.4.10 (#1380)
- Don't accept forbidden control characters as chat input - Update ssdp implementation to version 0.4 - Fix some more bugs
2 parents 35b7a15 + c44acab commit 49a5def

21 files changed

+139
-22
lines changed

.github/workflows/beta.build-push.yml

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ on:
99

1010
workflow_dispatch:
1111

12+
permissions: {}
13+
1214
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
1315
jobs:
1416
# This workflow contains a single job called "build"
@@ -27,6 +29,9 @@ jobs:
2729
EXPORT_OPTIONS_CATALYST_APPSTORE: "../scripts/exportOptions/Stable_Catalyst_ExportOptions.plist"
2830
EXPORT_OPTIONS_CATALYST_APP_EXPORT: "../scripts/exportOptions/Beta_Catalyst_ExportOptions.plist"
2931
EXPORT_OPTIONS_IOS: "../scripts/exportOptions/Beta_iOS_ExportOptions.plist"
32+
permissions:
33+
actions: write # needed to upload artifacts
34+
contents: write # needed for git push
3035
# Steps represent a sequence of tasks that will be executed as part of the job
3136
steps:
3237
- uses: actions/checkout@v4
@@ -250,6 +255,8 @@ jobs:
250255
APP_NAME: "Monal"
251256
APP_DIR: "Monal.app"
252257
BUILD_TYPE: "Beta"
258+
permissions:
259+
contents: write # needed for git push in updateLocalization
253260
# Steps represent a sequence of tasks that will be executed as part of the job
254261
steps:
255262
- uses: actions/checkout@v4

.github/workflows/create-stable-pr.yml

+4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ on:
55
branches: [ beta ]
66
workflow_dispatch:
77

8+
permissions: {}
9+
810
jobs:
911
create-pull-request:
1012
runs-on: ubuntu-latest
13+
permissions:
14+
pull-requests: write
1115
steps:
1216
- name: Checkout Beta Branch
1317
uses: actions/checkout@v4

.github/workflows/develop-push.yml

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ on:
99

1010
workflow_dispatch:
1111

12+
permissions: {}
13+
1214
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
1315
jobs:
1416
# This workflow contains a single job called "build"
@@ -27,6 +29,8 @@ jobs:
2729
ALPHA_UPLOAD_SECRET: ${{ secrets.ALPHA_UPLOAD_SECRET }}
2830
EXPORT_OPTIONS_CATALYST_APP_EXPORT: "../scripts/exportOptions/Alpha_Catalyst_ExportOptions.plist"
2931
EXPORT_OPTIONS_IOS: "../scripts/exportOptions/Alpha_iOS_ExportOptions.plist"
32+
permissions:
33+
actions: write # needed to upload artifacts
3034
# Steps represent a sequence of tasks that will be executed as part of the job
3135
steps:
3236
- uses: actions/checkout@v4

.github/workflows/pr-semver-title.yml

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ on:
1313
required: true
1414
type: number
1515

16+
permissions: {}
17+
1618
jobs:
1719
check-pr-semver-title:
1820
runs-on: ubuntu-latest

.github/workflows/publish-quicksy-release.yml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: Publish Quicksy release
22
on:
33
repository_dispatch:
44
types: [quicksy_release]
5+
permissions: {}
56
jobs:
67
extractChangelog:
78
runs-on: self-hosted

.github/workflows/publish-stable-release.yml

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: Publish release
22
on:
33
repository_dispatch:
44
types: [monal_release]
5+
permissions: {}
56
jobs:
67
extractChangelog:
78
runs-on: self-hosted
@@ -34,6 +35,8 @@ jobs:
3435
name: Promote draft release to live release
3536
runs-on: ubuntu-latest
3637
needs: [extractChangelog]
38+
permissions:
39+
contents: write
3740
steps:
3841
- name: Promote draft release to live release
3942
run: |

.github/workflows/quicksy.build-push.yml

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ on:
99

1010
workflow_dispatch:
1111

12+
permissions: {}
13+
1214
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
1315
jobs:
1416
# This workflow contains a single job called "build"
@@ -20,6 +22,9 @@ jobs:
2022
BUILD_SCHEME: "Quicksy"
2123
BUILD_TYPE: "AppStore-Quicksy"
2224
EXPORT_OPTIONS_IOS: "../scripts/exportOptions/Quicksy_Stable_iOS_ExportOptions.plist"
25+
permissions:
26+
actions: write # needed to upload artifacts
27+
contents: write # needed for git push
2328
# Steps represent a sequence of tasks that will be executed as part of the job
2429
steps:
2530
- uses: actions/checkout@v4

.github/workflows/stable.build-push.yml

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ on:
99

1010
workflow_dispatch:
1111

12+
permissions: {}
13+
1214
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
1315
jobs:
1416
# This workflow contains a single job called "build"
@@ -23,6 +25,9 @@ jobs:
2325
EXPORT_OPTIONS_CATALYST_APPSTORE: "../scripts/exportOptions/Stable_Catalyst_ExportOptions.plist"
2426
EXPORT_OPTIONS_CATALYST_APP_EXPORT: "../scripts/exportOptions/Beta_Catalyst_ExportOptions.plist"
2527
EXPORT_OPTIONS_IOS: "../scripts/exportOptions/Stable_iOS_ExportOptions.plist"
28+
permissions:
29+
actions: write # needed to upload artifacts
30+
contents: write # needed for git push
2631
# Steps represent a sequence of tasks that will be executed as part of the job
2732
steps:
2833
- uses: actions/checkout@v4

.github/workflows/update-translations.yml

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ name: update-translations
55
on:
66
workflow_dispatch:
77

8+
permissions: {}
9+
810
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
911
jobs:
1012
# This workflow contains a single job called "build"
@@ -18,6 +20,8 @@ jobs:
1820
EXPORT_OPTIONS_CATALYST_APPSTORE: "../scripts/exportOptions/Stable_Catalyst_ExportOptions.plist"
1921
EXPORT_OPTIONS_CATALYST_APP_EXPORT: "../scripts/exportOptions/Beta_Catalyst_ExportOptions.plist"
2022
EXPORT_OPTIONS_IOS: "../scripts/exportOptions/Beta_iOS_ExportOptions.plist"
23+
permissions:
24+
contents: write # needed for git push in updateLocalization
2125
# Steps represent a sequence of tasks that will be executed as part of the job
2226
steps:
2327
- uses: actions/checkout@v4

Monal/Classes/DebugView.swift

+4
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ struct CrashTestingView: View {
144144
Toggle(isOn: $defaultDB.hasCompletedOnboarding) {
145145
Text("Don't show onboarding")
146146
}
147+
148+
Button("Reset the state of all accounts") {
149+
MLXMPPManager.sharedInstance().resetAllAccountStates()
150+
}
147151
}
148152

149153
Text("The following buttons allow you to forcefully crash the app using several different methods to test the crash handling.")

Monal/Classes/HelperTools.h

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ void swizzle(Class c, SEL orig, SEL new);
203203
+(NSNumber*) currentTimestampInSeconds;
204204
+(NSNumber*) dateToNSNumberSeconds:(NSDate*) date;
205205

206+
+(NSString*) removeInvalidXMLCharactersFromString:(NSString*) inputString;
206207
+(BOOL) constantTimeCompareAttackerString:(NSString* _Nonnull) str1 withKnownString:(NSString* _Nonnull) str2;
207208

208209
+(BOOL) isIP:(NSString*) host;

Monal/Classes/HelperTools.m

+25
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ -(void) swizzled_queueLogMessage:(DDLogMessage*) logMessage asynchronously:(BOOL
9393
static NSObject* _suspensionHandling_lock = nil;
9494
static BOOL _suspensionHandling_isSuspended = NO;
9595
static NSMutableDictionary* _versionInfoCache;
96+
static NSCharacterSet* _invalidXMLCharacters;
9697
static MLStreamRedirect* _stdoutRedirector = nil;
9798
static MLStreamRedirect* _stderrRedirector = nil;
9899
static volatile void (*_oldExceptionHandler)(NSException*) = NULL;
@@ -378,6 +379,25 @@ +(void) initialize
378379
u_int32_t i = arc4random();
379380
_processID = [self hexadecimalString:[NSData dataWithBytes:&i length:sizeof(i)]];
380381

382+
//values taken from https://www.w3.org/TR/2008/REC-xml-20081126/#charsets
383+
NSMutableCharacterSet* validXMLCharacters = [NSMutableCharacterSet characterSetWithCharactersInString:@"\t\n\r"]; // #x9, #xA, #xD
384+
[validXMLCharacters formUnionWithCharacterSet:[NSCharacterSet characterSetWithRange:NSMakeRange(0x20, 0xD7FF - 0x20 + 1)]];
385+
[validXMLCharacters formUnionWithCharacterSet:[NSCharacterSet characterSetWithRange:NSMakeRange(0xE000, 0xFFFD - 0xE000 + 1)]];
386+
[validXMLCharacters formUnionWithCharacterSet:[NSCharacterSet characterSetWithRange:NSMakeRange(0x10000, 0x10FFFF - 0x10000 + 1)]];
387+
388+
NSMutableString* notRecommendedXMLCharacters = [NSMutableString new];
389+
for(unichar i = 0x007F; i <= 0x0084; i++)
390+
[notRecommendedXMLCharacters appendFormat:@"%C", i];
391+
392+
for(unichar i = 0x0086; i <= 0x009F; i++)
393+
[notRecommendedXMLCharacters appendFormat:@"%C", i];
394+
395+
for(unichar i = 0xFDD0; i <= 0xFDEF; i++)
396+
[notRecommendedXMLCharacters appendFormat:@"%C", i];
397+
398+
[validXMLCharacters removeCharactersInString:notRecommendedXMLCharacters];
399+
_invalidXMLCharacters = [validXMLCharacters invertedSet];
400+
381401
//shamelessly stolen from utils.ip in conversations source
382402
IPV4 = [NSRegularExpression regularExpressionWithPattern:@"\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z" options:0 error:nil];
383403
IPV6_HEX4DECCOMPRESSED = [NSRegularExpression regularExpressionWithPattern:@"\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z" options:0 error:nil];
@@ -2913,6 +2933,11 @@ +(NSArray*) splitString:(NSString*) string withSeparator:(NSString*) separator a
29132933
return result;
29142934
}
29152935

2936+
+(NSString*) removeInvalidXMLCharactersFromString:(NSString*) inputString
2937+
{
2938+
return [[inputString componentsSeparatedByCharactersInSet:_invalidXMLCharacters] componentsJoinedByString:@""];
2939+
}
2940+
29162941
//see https://nachtimwald.com/2017/04/02/constant-time-string-comparison-in-c/
29172942
+(BOOL) constantTimeCompareAttackerString:(NSString* _Nonnull) str1 withKnownString:(NSString* _Nonnull) str2
29182943
{

Monal/Classes/MLConstants.h

+7-7
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,19 @@ typedef NS_ENUM(NSUInteger, MLAudioState) {
8888
//some useful macros
8989
#define weakify(var) __weak __typeof__(var) AHKWeak_##var = var
9090
#define strongify(var) _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wshadow\"") __strong __typeof__(var) var = AHKWeak_##var; _Pragma("clang diagnostic pop")
91-
#define nilWrapper(var) (var == nil ? (id)[NSNull null] : (id)var)
92-
#define nilExtractor(var) ((id)var == [NSNull null] ? nil : var)
93-
#define nilDefault(var, def) (var == nil || (id)var == [NSNull null] ? def : var)
91+
#define nilWrapper(var) ({id _var=(id)(var); _var == nil ? [NSNull null] : _var;})
92+
#define nilExtractor(var) ({id _var=(id)(var); _var == [NSNull null] ? nil : _var;})
93+
#define nilDefault(var, def) ({id _var=(id)(var); _var == nil || _var == [NSNull null] ? (id)(def) : _var;})
9494
#define nilDefaultEnum(var, def) (((NSNumber*)nilDefault(var, def)).integerValue)
9595
#define nilDefaultBool(var, def) (((NSNumber*)nilDefault(var, def)).boolValue)
9696
#define nilDefaultInt(var, def) (((NSNumber*)nilDefault(var, def)).intValue)
9797
#define nilDefaultDouble(var, def) (((NSNumber*)nilDefault(var, def)).doubleValue)
98-
#define emptyDefault(var, eq, def) (var == nil || (id)var == [NSNull null] || [var isEqual:eq] ? def : var)
99-
#define updateIfIdNotEqual(a, b) if(a != b && ![a isEqual:b]) a = b
100-
#define updateIfPrimitiveNotEqual(a, b) if(a != b) a = b
98+
#define emptyDefault(var, eq, def) ({id _var=(id)(var); _var == nil || _var == [NSNull null] || [_var isEqual:(eq)] ? (def) : _var;})
99+
#define updateIfIdNotEqual(a, b) if((a) != (b) && ![(a) isEqual:(b)]) (a) = (b)
100+
#define updateIfPrimitiveNotEqual(a, b) if((a) != (b)) (a) = (b)
101101
#define var __auto_type
102102
#define let const __auto_type
103-
#define bool2str(b) (b ? @"YES" : @"NO")
103+
#define bool2str(b) ((b) ? @"YES" : @"NO")
104104

105105
#define min(a, b) \
106106
({ __typeof__ (a) _a = (a); \

Monal/Classes/MLXMPPManager.h

+5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ NS_ASSUME_NONNULL_BEGIN
4848
*/
4949
-(void) connectAccount:(NSNumber*) accountNo;
5050

51+
/**
52+
does a full reset for every account, as if they were freshly created
53+
*/
54+
-(void) resetAllAccountStates;
55+
5156
#pragma mark XMPP commands
5257
/**
5358
Remove a contact from an account

Monal/Classes/MLXMPPManager.m

+16
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,22 @@ -(void) disconnectAll
615615
DDLogVerbose(@"manager disconnecAll done");
616616
}
617617

618+
-(void) resetAllAccountStates
619+
{
620+
//make sure now account transitions from enabled to disabled while resetting everything
621+
//(accounts transitioned, will still be present in connectedXMPP and thus resetted by [xmppAccount resetAccountState], too)
622+
@synchronized(_connectedXMPP) {
623+
//reset disabled accounts
624+
NSMutableDictionary* newState = [xmpp invalidateState:nil];
625+
for(NSDictionary* account in [[DataLayer sharedInstance] accountList])
626+
if(![account[kEnabled] boolValue])
627+
[[DataLayer sharedInstance] persistState:newState forAccount:account[kAccountID]];
628+
629+
//reset enabled accounts
630+
for(xmpp* xmppAccount in [self connectedXMPP])
631+
[xmppAccount resetAccountState];
632+
}
633+
}
618634
-(void) connectIfNecessary
619635
{
620636
DDLogVerbose(@"manager connectIfNecessary");

Monal/Classes/SCRAM.m

+5-5
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ -(void) setSSDPMechanisms:(NSArray<NSString*>*) mechanisms andChannelBindingType
6969
MLAssert(!_serverFirstMessageParsed, @"SCRAM handler already parsed server-first-message!");
7070
DDLogVerbose(@"Creating SDDP string: %@\n%@", mechanisms, cbTypes);
7171
NSMutableString* ssdpString = [NSMutableString new];
72-
[ssdpString appendString:[[mechanisms sortedArrayUsingSelector:@selector(compare:)] componentsJoinedByString:@","]];
72+
[ssdpString appendString:[[mechanisms sortedArrayUsingSelector:@selector(compare:)] componentsJoinedByString:@"\x1e"]];
7373
if(cbTypes != nil)
7474
{
75-
[ssdpString appendString:@"|"];
76-
[ssdpString appendString:[[cbTypes sortedArrayUsingSelector:@selector(compare:)] componentsJoinedByString:@","]];
75+
[ssdpString appendString:@"\x1f"];
76+
[ssdpString appendString:[[cbTypes sortedArrayUsingSelector:@selector(compare:)] componentsJoinedByString:@"\x1e"]];
7777
}
7878
_ssdpString = [ssdpString copy];
7979
DDLogVerbose(@"SDDP string is now: %@", _ssdpString);
@@ -112,12 +112,12 @@ -(MLScramStatus) parseServerFirstMessage:(NSString*) str
112112
_salt = [HelperTools dataWithBase64EncodedString:msg[@"s"]];
113113
_iterationCount = (uint32_t)[msg[@"i"] integerValue];
114114
//check if SSDP downgrade protection triggered, if provided
115-
if(msg[@"d"] != nil && _ssdpString != nil)
115+
if(msg[@"h"] != nil && _ssdpString != nil)
116116
{
117117
_ssdpSupported = YES;
118118
//calculate base64 encoded SSDP hash and compare it to server sent value
119119
NSString* ssdpHash =[HelperTools encodeBase64WithData:[self hash:[_ssdpString dataUsingEncoding:NSUTF8StringEncoding]]];
120-
if(![HelperTools constantTimeCompareAttackerString:msg[@"d"] withKnownString:ssdpHash])
120+
if(![HelperTools constantTimeCompareAttackerString:msg[@"h"] withKnownString:ssdpHash])
121121
return MLScramStatusSSDPTriggered;
122122
}
123123
if(_iterationCount < 4096)

Monal/Classes/chatViewController.m

+2
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,8 @@ -(void) resignTextView
11441144

11451145
// Trim leading spaces
11461146
NSString* cleanString = [self.chatInput.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
1147+
cleanString = [HelperTools removeInvalidXMLCharactersFromString:cleanString];
1148+
11471149
// Only send msg that have at least one character
11481150
if(cleanString.length > 0)
11491151
{

Monal/Classes/xmpp.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ typedef void (^monal_iq_handler_t)(XMPPIQ* _Nullable);
116116
-(void) disconnect:(BOOL) explicitLogout;
117117
-(void) reconnect;
118118
-(void) reconnect:(double) wait;
119+
-(void) resetAccountState;
119120

120121
-(void) setPubSubNotificationsForNodes:(NSArray* _Nonnull) nodes persistState:(BOOL) persistState;
121122

@@ -235,7 +236,7 @@ typedef void (^monal_iq_handler_t)(XMPPIQ* _Nullable);
235236
-(void) publishStatusMessage:(NSString*) message;
236237
-(void) delayIncomingMessageStanzasForArchiveJid:(NSString*) archiveJid;
237238

238-
+(NSMutableDictionary*) invalidateState:(NSDictionary*) dic;
239+
+(NSMutableDictionary*) invalidateState:(NSDictionary* _Nullable) dic;
239240
-(void) updateIqHandlerTimeouts;
240241

241242
-(void) addReconnectionHandler:(MLHandler*) handler;

Monal/Classes/xmpp.m

+18
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,24 @@ -(void) reconnectWithStreamError:(MLXMLNode* _Nullable) streamError andWaitingTi
13061306
}];
13071307
}
13081308

1309+
-(void) resetAccountState
1310+
{
1311+
NSMutableDictionary* newState = [xmpp invalidateState:nil];
1312+
if([[DataLayer sharedInstance] isAccountEnabled:self.accountNo])
1313+
{
1314+
[self dispatchAsyncOnReceiveQueue: ^{
1315+
[self disconnect:YES]; //make sure we are in a safe state before resetting our state
1316+
[[DataLayer sharedInstance] persistState:newState forAccount:self.accountNo];
1317+
[self connect]; //this will reread persisted state saved above
1318+
}];
1319+
}
1320+
else
1321+
{
1322+
[[DataLayer sharedInstance] persistState:newState forAccount:self.accountNo];
1323+
[self readState]; //better safe than sorry
1324+
}
1325+
}
1326+
13091327
#pragma mark XMPP
13101328

13111329
-(void) prepareXMPPParser

0 commit comments

Comments
 (0)