FSEvents weirdness on OS X Leopard - macos

I want to monitor file-system events for a couple of directories on the mac. The directories I want to monitor might change at runtime, so using FSEvents here's what my app does:
creates a global callback function to handle callbacks
create a new FSEventStreamRef per folder, associating it with the callback created above and adding a context to the eventStream that helps me associate the change callback with this folder
Stuff seems to mostly work, but I've noticed some weirdness in when the callbacks are invoked and the 'eventPaths' values being sent to the callback.
For instance, if I've created StreamRefs for /Foo and /Bar, if I add a file in /Bar my callback is invoked almost immediately but the eventPaths points to a location in /Foo, and the context I associated with the StreamRef is also that of /Foo.
Or, say I'm monitoring /Foo and /Bar and then remove /Bar (by stopping and closing the StreamRef for /Bar correctly). I now create a new FSEventStreamRef for /Fee and associate with the same callback. Any changes I make to /Fee don't cause the callback to be invoked but changes to /Foo continue raising the callback.
Any example or documentation I've seen online only talks of monitoring a single folder. Is something busted with how I'm associating the single callbacks with multiple FSEventStreamRefs? It sounds like that shouldn't be a problem though...
Has anyone done something similar in a way that works reliably, or any suggestions for what I might try differently?
One thing I did attempt to do as I was experimenting with this is use a single FSEventStreamRef and pass it a CFArrayRef with all the paths I wanted, and when my watch list changes close and re-create a new FSEventStreamRef - this works even worse that the above.

Works for me. I emulated these characteristics:
One path per stream
One context per path/stream
One callback for all streams
Can you show the code that's failing?

Related

macOS Kauth API lack vnode rename prevention

In order to protect my project file from being deleted by unauthorized entities, I used kauth_listen_scope with KAUTH_SCOPE_VNODE option to add new callback function that prevent other from deleting those files.
However, it appears that it's possible to move those files to trash from Finder since it uses the rename system call. this command doesn't trigger vnode authorization event that can be prevented, only fileop event (but these are for detection only).
Perhaps anyone have an alternative about what can be done in order to prevent this action ?
thanks
So it appears that rename sys call also goes through vnode authorization.
However, it does not request authorization on the file itself, but on its container (parent directory).

Firefox Extension API - permissions.request may only be called from a user input handler?

I'm using the Firefox permissions API documented HERE
I'm having a problem with the request method, wherein all of my permissions requests result in:
Error: permissions.request may only be called from a user input handler
You can produce this in firefox by debugging any addon or extension and entering browser.permissions.request({origins: ["https://google.com/*"]}) into the console.
I find it hard to swallow that a permissions request must always have a user input event callback in the parent stack trace. I'm using Vue.js, and my Permissions are due to user interaction, but my user interactions are decoupled from the events they trigger.
What counts as a user input handler?
Why does it work like this?
Is there a good work-around?
Is there a good work-around"
I'd like to add onto Andrew's answer with some code examples.
As it turns out, promise chains destroy the browser's notion of what is and isn't triggered by a user input handler. Take the code below, for example:
document.getElementById('foo').addEventListener('click', event => {
browser.permissions.request({origins: ["https://google.com/*"]})
})
This code works as expected. I originally assumed that it was Vue.js's unique event handling framework that was eating my "browser events", such as when you do <div #click="somefunc"></div>. This actually works just fine, as long as you put your permissions request in somefunc.
Now it gets fun. If you replace your permissions request with a promise that resolves and then does a permissions request, VIOLA!
Promise.resolve('foobar').then(foobar => {
browser.permissions.request({origins: ["https://google.com/*"]})
})
Results in:
Error: permissions.request may only be called from a user input handler
Why does this happen?
I'm going to guess it has to do with stack traces. Firefox can't detect that a permission came from a stack with a user input event at the root if the permissions request happens in a promise chain.
I consider this to be a pretty egregious design choice. My app is large (>4K LoC) and to keep it simple I rely on promise chains to keep the spaghetti away. This has crippled my ability to write clean code, and as a result, I've moved from asking for optional_permissions and then prompting the user for permissions only when needed to just being overly permissive at the time of installation.
GG, Firefox.
What counts as a user input handler?
A DOM event handler that corresponds to user input (e.g., target.addEventHandler("click", ...) or a WebExtension event listener that corresponds to user input (e.g., browser.browserAction.onClicked.addListener(...)
Why does it work like this?
Partly for basic UX (if a user is not directly interacting with an extension and a prompt for the extension suddenly prompts up, it can easily confuse them), but also to avoid clickjacking attacks where the prompt is put up at a carefully chosen moment when the user is likely to be expecting some unrelated prompt.
Is there a good work-around?
I think just organizing your code so that you request permissions from a user input handler is probably your best bet.

How to make a Finder Sync Extension change badges in response to outside events

I have a Finder Sync Extension that will display a badge on a file based on the state of a local database. It's straightforward enough to query this database in the requestBadgeIdentifierForURL function, but what if I want the badge to change for a Finder item that's already visible if the state of that database has changed (which can be via a notification through any variety of mechanisms). The documentation (https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html) would seem to imply this is possible with this statement:
You might also want to track these URLs, in order to update their
badges whenever their state changes.
The only ways I can imagine this would be possible (and most seem wrong) would be:
call setBadgeIdentifier:forURL from another application that is aware of the change
Launch a thread in the init function of my extension which listens for notifications and calls setBadgeIdentifier:forURL when it receives them
Call some OS API that prompts Finder that the extension should be triggered via requestBadgeIdentifierForURL.
Only the last one seems feasible, and could be managed via the extension informing the outside resource what needs refreshing via the beginObservingDirectoryAtURL/endObservingDirectoryAtURL callbacks, but i don't know what mechanism could do this.

How to export a constructor from privileged scope to a less-privileged scope in Firefox Add-on SDK

I'm trying to write a Firefox extension which hooks into WebAudio and adds a 'master-gain' to all AudioContexts.
I am trying to do this by overriding the AudioContext constructor in the window namespace. This way I can return a GainNode (which is internally connected to the destination) when the user tries to access destination. It's a hack, but I think it might be useful.
I'm currently struggling at exporting my newAudioContext constructor from the addon script(privileged scope) into the page script (less-privileged).
I tried...
unsafeWindow.AudioContext = cloneInto(newAudioContext,unsafeWindow);
But I still get the original AudioContext in the page script.
I also tried
exportFunction(newAudioContext, unsafeWindow, {defineAs: "AudioContext"});
but that exports it as a function and not as a constructor.
I understand that structured cloning has limitations, but are there any other ways I can override the window.AudioContext from an AddOn?
If you need to run more complex code (e.g. object construction) in the unprivileged context you can simply import a script into the target window (after waiving xrays) through the mozIJSSubScriptLoader.
Any function that needs to call into privileged code can be patched into its prototype from the chrome side after the script has been loaded.
You can do this before DOM parsing - and thus before any content script execution - by listening to the DOMWindowCreated event.
You will have to do this from a frame script, since the addon-sdk's page-mod sandboxes don't have enough privileges to access the script loader.
Keeping interaction with the unsafe window to a minimum, i.e. either running code wholly in the privileged environment with xrays or completely in the untrusted environment with the minimal amount of glue methods between those two seems like good security hygiene anyway.
Of course you should be aware that content code will be able to pick apart and modify any classes you create in content. It is untrusted after all.

NSDocumentController not quitting app properly on review unsaved documents in Cocoa/Objective-C

I've been banging my head on walls trying to solve this.
My app uses a subclass of NSDocument, NSDocumentController and also NSWindowController. The issue is that when I create a document and make a modification, and also if I create more than one document the same way, if I choose Quit while documents are unsaved, I get the proper dialogs to ask wether I want to Review unsaved docs, Cancel or Not save, but whenever I select to Review and then save a document, I get to save, but after saving the first document, first, that document is NOT closed, and I am not asked to save the others. On top of this, My App's Quit menu item becomes grayed out.
I created another skeleton app to do the same thing, and of course, all works normally. I checked my connections in the nib file, and cannot see anything wrong, or different from the test app. Although there is a lot more in my app.
I am not currently overriding the reviewUnsavedDocumentsWithAlertTitle:cancellable:delegate:didReviewAllSelector:contextInfo: method, but when I do, I get the same problem, since I call Super's version of it after doing some special clean-up work. But again, not overriding this yields the same result.
One thing I do notice, is that in my own App, I get the following messages in the Log as I run it, which I do not get in my test app:
[Switching to process 9997 thread 0x7667]
[Switching to process 9997 thread 0x903]
Which makes it look like I am doing something multi-threaded here without knowing. My test app doesn't switch to another thread.
I'm not sure how to check if all my connexions are ok and why these threads are being switched to and where they come from.
Since I get the error wether or not I override the NSDocumentController methods like the one above or the closeAllDocumentsWithDelegate:didCloseAllSelector:contextInfo: I imagine the error comes from somewhere else.
Maybe my NSDocument subclass's saving methods are creating the issue, but they return the appropriate values once the save has occurred, I get no error and the files are all fine.
I'd like to override the -didCloseAllSelector or -didReviewAllSelector, but I'm a bit challenged with this. I'm not sure how to build the method, since this:
- (void)documentController:(NSDocumentController *)controller
didCloseAll:(BOOL)didCloseAll contextInfo:(void *)contextInfo;
{
NSLog(#"All documents closed");
}
is never called. Is this a symptom? Tried the same for the -didReviewAllSelecter which never gets called.
Finally, I was overriding a method in my "NSDocument" subclass which I should not have. Removing this fixed it.
I guess you have to be really careful what you override!
The method was writeToFile:ofType:originalFile:saveOperation: which I no longer had to do.
Not sure exactly what was wrong, but it may help others to know to look there.

Resources