Cocoa run AppleScript that already contains double

2019-02-20 20:35发布

问题:

I have a Cocoa application that need admin permission to execute a cmd, but in some extreme case like filename containing double quote, this code does not work.

 NSString *fileName = @"~/Documents/My\" File/";
 NSString *cmd = [NSString stringWithFormat:@"chown -R '%@' '%@';NSUserName(), fileName];
 NSString *cmd_execute = [NSString stringWithFormat:@"do shell script
 \"%@\" with administrator privileges",cmd_execute]; 
//This line is the problem, our %@ contains double quote, so cocoa 
//cannot interpret correctly.

回答1:

Here is the correct way to pass arguments into an AppleScript handler using NSAppleScript:

// load/compile AppleScript
NSAppleScript *scpt = [[NSAppleScript alloc] initWithSource:
        @"on joinText(a, b)\n"
        @"  return a & b\n"
        @"end addTo"];

NSString *arg1 = @"Hello ";
NSString *arg2 = @"World!";

// pack positional parameters
NSAppleEventDescriptor *params = [NSAppleEventDescriptor listDescriptor];
[params insertDescriptor: [NSAppleEventDescriptor descriptorWithString: arg1] atIndex: 1];
[params insertDescriptor: [NSAppleEventDescriptor descriptorWithString: arg2] atIndex: 2];

// build Apple event to invoke user-defined handler in script
NSAppleEventDescriptor *eventDesc = [NSAppleEventDescriptor appleEventWithEventClass: 'ascr'
                                                            eventID: 'psbr'
                                                            targetDescriptor: [NSAppleEventDescriptor nullDescriptor]
                                                            returnID: 0
                                                            transactionID: 0];
[eventDesc setDescriptor: params forKeyword: '----'];
[eventDesc setDescriptor: [NSAppleEventDescriptor descriptorWithString: @"joinText"] forKeyword: 'snam'];

 // invoke handler
 NSDictionary *errorInfo = nil;
 NSAppleEventDescriptor *resultDesc = [scpt executeAppleEvent: eventDesc error: &errorInfo];
 if (resultDesc)
     NSLog(@"Result: %@\n", [resultDesc stringValue]);
 else
     NSLog(@"Error: %@\n", errorInfo);

This avoids the need to munge and compile AS scripts on the fly - which, as your original ObjC code demonstrates, is a rich opportunity to introduce bugs and security holes into your program due to inadequate sanitization.

...

And here is the correct way to assemble shell script strings in AppleScript:

do shell script ("chown -R " & quoted form of username & " " & quoted form of filepath)

AppleScript's do shell script command doesn't provide a safe mechanism for passing arguments (it's deficient in a lot of other ways too), but AS strings have a quoted form property that at least returns a quoted and escaped version of the text suitable for concatenating into a shell script string.

...

Of course, calling into shell via do shell script AppleScript just to run chmod with escalated priveleges is not a little hacky in itself, though this is mostly Apple's fault for not providing a simple official API for doing so (e.g. by adding a 'run with admin privileges' option to NSUserScriptTask).

Apple do provide a sample project, SMJobBless that shows how to add and run privileged processes using the Service Management framework and launchd. But it's a sufficiently complex solution that a lot of folks will (knowingly or unknowingly) cut corners or do the wrong thing entirely, which rather defeats the point of providing a secure way of doing things in the first place. But you'll need to take that up with Apple.