From 1bba1a714b08a18c7ccc6492fa86793e46d61d54 Mon Sep 17 00:00:00 2001 From: Rajdeep Kwatra Date: Sat, 6 Apr 2024 11:57:07 +1100 Subject: [PATCH] Added clamping to textstorage.attributedSubstring to avoid out-of-bounds crashes (#305) --- Proton/Sources/ObjC/PRTextStorage.m | 11 +++++++++++ Proton/Tests/Core/TextStorageTests.swift | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/Proton/Sources/ObjC/PRTextStorage.m b/Proton/Sources/ObjC/PRTextStorage.m index 0e9dcf3a..90b33a4c 100644 --- a/Proton/Sources/ObjC/PRTextStorage.m +++ b/Proton/Sources/ObjC/PRTextStorage.m @@ -73,6 +73,17 @@ - (void)edited:(NSTextStorageEditActions)editedMask range:(NSRange)editedRange c [self.textStorageDelegate textStorage:self edited:editedMask in:editedRange changeInLength:delta]; } +- (NSAttributedString *)attributedSubstringFromRange:(NSRange)range { + NSRange rangeToUse =[self clampedWithUpperBound:self.length location:range.location length:range.length]; + return [super attributedSubstringFromRange: rangeToUse]; +} + +- (NSRange)clampedWithUpperBound:(NSInteger)upperBound location:(NSInteger)location length:(NSInteger)length { + NSInteger clampedLocation = MAX(MIN(location, upperBound), 0); + NSInteger clampedLength = MAX(MIN(length, upperBound - clampedLocation), 0); + return NSMakeRange(clampedLocation, clampedLength); +} + - (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString { // TODO: Add undo behaviour diff --git a/Proton/Tests/Core/TextStorageTests.swift b/Proton/Tests/Core/TextStorageTests.swift index fb6ddf0c..a44c8075 100644 --- a/Proton/Tests/Core/TextStorageTests.swift +++ b/Proton/Tests/Core/TextStorageTests.swift @@ -131,4 +131,13 @@ class TextStorageTests: XCTestCase { XCTAssertEqual(attrs[NSAttributedString.Key("attr2")] as? Int, 2) XCTAssertEqual(attrs[NSAttributedString.Key("attr3")] as? Int, 3) } + + func testReturnsSubstringWithClampedRange() { + let textStorage = PRTextStorage() + let testString = NSAttributedString(string: "test string") + textStorage.replaceCharacters(in: .zero, with: testString) + + let substring = textStorage.attributedSubstring(from: NSRange(location: 5, length: 50)) + XCTAssertEqual(substring.string, "string") + } }