I'm using NSScanner to restrict a text field's value to numbers only, but I want to allow "-" - objective-c

I have an NSScanner in a custom NSNumberFormatter that scans for non-int values but I want it to skip "-" (dash)
- (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error {
if ([partialString length] == 0) {
return YES;
}
NSScanner *scanner = [NSScanner scannerWithString:partialString];
if (!([scanner scanInt:0] && [scanner isAtEnd])) {
return NO;
}
return YES;
}
I thought I had to use [scanner charactersToBeSkipped] but I don't know how that works

I came up with my own solution. Each time a character is typed, it checks if the first one is a dash. If it is, it skips the first character and checks if the rest of the string contains ints. If the first character is not a dash, it scans the whole string for ints.
- (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error {
if ([partialString length] == 0) {
return YES;
}
NSString *stringFromSecondChar;
NSString *firstChar = [partialString substringToIndex:1];
if ([partialString length] > 1 && [firstChar isEqualToString:#"-"]) {
stringFromSecondChar = [partialString substringFromIndex:1];
}
else {
stringFromSecondChar = partialString;
}
NSScanner *scanner = [NSScanner scannerWithString:stringFromSecondChar];
if ([partialString length] == 1 && [firstChar isEqualToString:#"-"]) {
return YES;
}
else if ([partialString length] >= 1 && [stringFromSecondChar containsString:#"-"]) {
return NO;
}
if ([firstChar isEqualToString:#"-"]) {
if (!([scanner scanInt:0] && [scanner isAtEnd])) {
return NO;
}
else {
return YES;
}
}
if (!([scanner scanInt:0] && [scanner isAtEnd])) {
return NO;
}
return YES;
}

Solution with regular expression:
- (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error {
if (partialString.length == 0)
return YES;
NSRange range = [partialString rangeOfString:#"^[-]?\\d*$" options:NSRegularExpressionSearch];
return (range.location != NSNotFound);
}
Solution with NSScanner:
- (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error {
if (partialString.length == 0 || [partialString isEqualToString:#"-"])
return YES;
NSScanner *scanner = [NSScanner scannerWithString:partialString];
return ([scanner scanInt:NULL] && [scanner isAtEnd]);
}

Related

NSPredicate - predicateWithFormat insecure

I have a predicate for query in core data base but i don't know what is the correct way to validate its params?
- (void) queryToDatabaseWithStoreId:(NSInteger) storeId {
[NSPredicate predicateWithFormat:#"store.storeId = %d", storeId];
}
My question is how can i validate storeId param or what i need to use for that vulnerability to dissapear?
And if i have a list:
- (void) queryToDataBaseWithListStore:(NSArray<Store *> *) storeList {
[NSPredicate predicateWithFormat:#"store.storeId IN %#", [storeList valueForObject:#"storeId"]];
}
https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/ValidatingInput.html#//apple_ref/doc/uid/TP40007246-SW3
I need avoid that:
The following commonly-used functions and methods are subject to format-string attacks:
Standard C
printf and other functions listed on the printf(3) manual page
sscanf and other functions listed on the scanf(3) manual page
syslog and vsyslog
Carbon
AEBuildDesc and vAEBuildDesc
AEBuildParameters and vAEBuildParameters
AEBuildAppleEvent and vAEBuildAppleEvent
Core Foundation
CFStringCreateWithFormat
CFStringCreateWithFormatAndArguments
CFStringAppendFormat
CFStringAppendFormatAndArguments
Cocoa
stringWithFormat:, initWithFormat:, and other NSString methods that take formatted strings as arguments
appendFormat: in the NSMutableString class
alertWithMessageText:defaultButton:alternateButton:otherButton:informativeTextWithFormat: in NSAlert
predicateWithFormat:, predicateWithFormat:arguments:, and predicateWithFormat:argumentArray: in NSPredicate
raise:format: and raise:format:arguments: in NSException
NSRunAlertPanel and other AppKit functions that create or return panels or sheets
What is the best way to avoid this attack?
I have programmed this class but i don't know if it is enough.
#implementation StringUtils
+ (BOOL) isEmpty:(id) text {
if ([text isKindOfClass:[NSNull class]]) {
return YES;
} else {
if (text) {
if ([text isKindOfClass:[NSString class]]) {
NSString *textStr = [NSString stringWithFormat:#"%#", text];
return [textStr isEqualToString:#""];
}
return YES;
} else {
return YES;
}
}
}
+ (NSString *) validateField:(id) text {
NSInteger numErrors = 0;
NSString *pattern = #"[^A-Za-z0-9-]+";
NSError *error = nil;
NSString *textValidated = #"";
if ([text isKindOfClass:[NSNumber class]]) {
textValidated = [text stringValue];
} else if ([text isKindOfClass:[NSString class]]) {
textValidated = text;
} else {
#try {
textValidated = [text stringValue];
} #catch (NSException *exception) {
numErrors=+1;
}
}
//Only numbers && chars && -
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
NSRange textRange = NSMakeRange(0, textValidated.length);
NSRange matchRange = [regex rangeOfFirstMatchInString:textValidated options:NSMatchingReportProgress range:textRange];
if (matchRange.location != NSNotFound) {
numErrors+=1;
}
//Not empty string
if ([StringUtils isEmpty:textValidated]) {
numErrors+=1;
}
if (numErrors == 0) {
return textValidated;
}
return #"";
}
+ (NSArray *) validateArrayFields:(NSArray *) list {
NSInteger *numErrors = 0;
for (id obj in list) {
if ([StringUtils isEmpty:[StringUtils validateField:obj]]) {
numErrors+=1;
}
}
if (numErrors == 0) {
return list;
}
return [[NSArray alloc] init];
}
#end
For use normal:
[NSPredicate predicateWithFormat:#"store.storeId = %#", [StringUtils validateField:storeId]];
For use with array:
[NSPredicate predicateWithFormat:#"store.storeId IN %#", [StringUtils validateArrayFields:storeId]];

Does anyone know two NSString_s with the same hashes?

I want to test some cases in my app with strings which have the same hash, and I can't find it =(
I've found two strings with the same MD5. here But their hash are different. And googling didn't help me =(
NSString(MD5) category
Little story about NSDictionary
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSString *string1 = [self fileContentWithName:#"message1" encoding:NSUnicodeStringEncoding];
NSString *string2 = [self fileContentWithName:#"message2" encoding:NSUnicodeStringEncoding];
if (string1 != nil) {
if (string1.hash == string2.hash) {
NSLog(#"Hashes are the same");
} else {
if ([[string1 MD5Hash] isEqualToString:[string2 MD5Hash]]) {
NSLog(#"MD5 hases are equalfor:");
NSLog(#"lenght = %3ld - %#", string1.length, string1);
NSLog(#"lenght = %3ld - %#", string2.length, string2);
if ([string1 isEqualToString:string2]) {
NSLog(#"Strings are equal too");
} else {
NSLog(#"But strings are not equal");
}
}
}
}
}
#pragma mark -
- (NSString*)fileContentWithName:(NSString*)name encoding:(NSStringEncoding)enc
{
NSString *txtFilePath1 = [[NSBundle mainBundle] pathForResource:name ofType:#"bin"];
NSError *error = nil;
NSString *txtFileContents1 = [NSString stringWithContentsOfFile:txtFilePath1 encoding:enc error:&error];
return txtFileContents1;
}

How to restrict numbers and special characters in objective-c [duplicate]

This question already has answers here:
Restrict NSTextField to only allow numbers
(10 answers)
Closed 8 years ago.
In textfield I want to restrict numbers like (1234567890) and special characters but I want to allow alphanumeric characters. How I am suppose to do this?
Use the UITextField delegate method
textField:shouldChangeCharactersInRange:replacementString:
To check the string that is about to be replaced, if you allow it then return yes if not then return no.
Here is some more information.
Apple UITextField Delegate
try following code
+ (BOOL)isNumber:(NSString *)value {
if ( (value == nil) || ([#"" isEqualToString:value]) ) {
return NO;
}
int l = [value length];
BOOL b = NO;
for (int i = 0; i < l; i++) {
NSString *str =
[[value substringFromIndex:i]
substringToIndex:1];
const char *c =
[str cStringUsingEncoding:
NSASCIIStringEncoding];
if ( c == NULL ) {
b = NO;
break;
}
if ((c[0] >= 0x30) && (c[0] <= 0x39)) {
b = YES;
} else {
b = NO;
break;
}
}
if (b) {
return YES;
} else {
return NO;
}
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ( (string != nil) && (string != #"") ) {
if (![self isNumber:string]) {
return NO;
}
}
return YES;
}
You need to write a NSFormatter and assign it to your text field. Here an example implementation of a such NSFormatter which uses a NSRegularExpression to validate the NSTextField contents.
#interface XXNameElementFormatter : NSFormatter
#end
#implementation HcNameElementFormatter {
NSRegularExpression *_re;
}
- (id)init {
self = [super init];
if (self) {
[self initRegularExpression];
}
return self;
}
- (void)awakeFromNib
{
[self initRegularExpression];
}
- (void)initRegularExpression
{
NSError *reError;
_re = [NSRegularExpression regularExpressionWithPattern:#"^[a-z]*$" options:NSRegularExpressionCaseInsensitive error:&reError];
NSAssert(_re != nil, #"Error in regular expression, error: %#", reError);
}
- (NSString *)stringForObjectValue:(id)obj
{
return obj;
}
- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error
{
*obj = string;
return YES;
}
- (BOOL)isPartialStringValid:(NSString *__autoreleasing *)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString *__autoreleasing *)error
{
NSParameterAssert(partialStringPtr != nil);
NSString *partialString = *partialStringPtr;
NSRange firstMatch = [_re rangeOfFirstMatchInString:*partialStringPtr options:0 range:NSMakeRange(0, partialString.length)];
return firstMatch.location != NSNotFound;
}
#end

Why would subclassing NSFormatter prevent me from editing NSTextField's input?

I'm trying to format two NSTextFields for SMPTE time codes, which have the format:
HH:MM:SS:FF. However, when the user switches the SMPTE code to drop-frame, the delimiter between SS and FF needs to switch to a ; (HH:MM:SS;FF). To do this, I've subclassed NSFormatter, and have it mostly working except for one very stubborn problem.
The text field accepts input just fine, but if I highlight-replace, backspace, delete, or insert any new characters into the text field, I get an NSBeep and I can't switch focus away from the text field. I can input new text if I delete the whole text field first, but not if I try to edit the existing input. Here are my implemented methods/overrides:
- (NSString*)stringForObjectValue:(id)obj
{
if ( ! [obj isKindOfClass:[NSNumber class]])
{
return nil;
}
NSMutableString *string = [NSMutableString stringWithString:#"00:00:00:00"];
int length = (int)[[obj stringValue] length];
int insertLocation = 9;
if (length == 1)
{
[string replaceCharactersInRange:NSMakeRange(10, 1) withString:[obj stringValue]];
}
else
{
while (length > 1)
{
NSString *temp = [[obj stringValue] substringFromIndex:length-2];
[string replaceCharactersInRange:NSMakeRange(insertLocation, 2) withString:temp];
obj = [NSNumber numberWithInt:[obj intValue]/100];
length -= 2;
insertLocation -= 3;
}
if (length == 1)
{
[string replaceCharactersInRange:NSMakeRange(insertLocation+1, 1) withString:[obj stringValue]];
}
}
return string;
}
- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error
{
int valueResult;
NSScanner *scanner;
BOOL returnValue = NO;
scanner = [NSScanner scannerWithString: string];
[scanner scanString:#":" intoString:NULL];
[scanner scanString:#";" intoString:NULL];
if ([scanner scanInt:&valueResult] && ([scanner isAtEnd])) {
returnValue = YES;
if (obj)
{
*obj = [NSNumber numberWithInt:valueResult];
}
}
return returnValue;
}
At least at this point, I don't need to validate the input during editing, only when editing is finished. I tried implementing isPartialStringValid and just returning YES, but that didn't seem to help either. Any help would be greatly appreciated!
Ok, I just solved it by doing some more testing. It appears as though why it was failing is because getObjectValue was receiving the string with the delimiters in them and was not correctly removing them. I simply replaced the method with this:
- (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error
{
NSString *newString = [string stringByReplacingOccurrencesOfString:#":" withString:#""];
if (obj)
{
*obj = [NSNumber numberWithInt:[newString intValue]];
return YES;
}
return NO;
}
Now it works perfectly.

How to extract the data I want in NSString?

I have a NSString like this:
456673\tSomething
But I would like to extract Something only.... ...
All the data must be in this format ....
xxxx\tyyyy
How can I split it bases on \t? thank you.
You may be looking for this instance method:
- (NSArray *)componentsSeparatedByString:(NSString *)separator
If that fails your needs and you want something more powerful, I can personally recommend RegexKitLite. RegexKitLite adds the power of regular expressions to NSString in the form of a category.
I've added a category to NSString for convenience (class: NSString+Utility):
- (NSString *)substringFromFirstOccurenceOfString:(NSString *)string {
NSRange range = [self rangeOfString:string];
if (range.location != NSIntegerMax) {
int index = range.location + range.length;
return [self substringFromIndex:index];
} else {
return self;
}
}
- (NSString *)substringFromLastOccurenceOfString:(NSString *)string {
NSRange range = [self rangeOfString:string options:NSBackwardsSearch];
if (range.location != NSIntegerMax) {
int index = range.location + range.length;
return [self substringFromIndex:index];
} else {
return self;
}
}
- (NSString *)substringToFirstOccurenceOfString:(NSString *)string {
NSRange range = [self rangeOfString:string];
if (range.location != NSIntegerMax) {
int index = range.location + range.length;
return [self substringToIndex:index];
} else {
return self;
}
}
- (NSString *)substringToLastOccurenceOfString:(NSString *)string {
NSRange range = [self rangeOfString:string options:NSBackwardsSearch];
if (range.location != NSIntegerMax) {
int index = range.location;
return [self substringToIndex:index];
} else {
return self;
}
}