• Friday, May 8, 2015 – William Donnelly
NOTE: Apparently there is a CSP ability to stop inline scripts from executing.
I have not come across any sites that use that feature and/or the browser I am
using does not support it. (currently Pale Moon 25.4.0) So this is not an
"absolute" fix. Unfortunately, CSP is taking away too much of the user's power
and control over their browser use. (unless the browser creators somehow
come up with a work-around — they need to develop a security model
that works for everyone involved without crippling the (power) user)
The increased implementation of Content Security Policy (CSP) in websites has been
heralded as "The End / Death of Bookmarklets". I created a solution in 2015 that
resurrects the ability to use (library-based) bookmarklets on CSP and https://
websites using a Greasemonkey userscript.
You can find more info about this issue here:
https://bugzilla.mozilla.org/show_bug.cgi?id=866522
(Bug 866522 - Bookmarklets affected by CSP (Content Security Policy))
After months of trying to figure out what was going on, and multiple attempts to find
a work-around or SOMETHING to "fix" it, I FINALLY found a solution to the problem.
The solution is fairly simple using a Greasemonkey userscript.
For some reason, people "at Greasemonkey" couldn't offer the solution I created,
or even hints or tips to get there.
I'm not sure what that was all about. I explained my problem and asked what I can
possibly do about it, and no one offered an answer, so I had to figure it out
for myself. (after some headbashing and such)
It's kind of "stupid", because, although you are limited in what URL
you can use to inject a script into certain CSP-protected documents,
you can insert ANY text DIRECTLY into the document. (now watch them
take that away from us, too) Of course, there is (still?) usually
some limit to the size of a saved bookmarklet, but I (needed to)
get around that. (plus the "squishing" and such needed in bookmarks,
which I've NEVER liked)
I use the @resource command to access my 56K JavaScript library
file, get the contents of it, and directly insert that .JS file document text
using the script object .text property instead of the .src property.
Note that, upon installation of the userscript, the library file is loaded
as a STATIC SNAPSHOT into the Greasemonkey directory that holds the userscript.
There are some issues with that and modifications/reload to the referenced source file.
So you MUST directly edit the snapshot file if you have changes. (that is, there is
no built-in mechanism to auto-reload that file if it has been modified at the source
location -- hopefully they will correct that sometime in the future -- there are
some "work-arounds" that are not very good, imo -- so I just "load it once" into
the userscript directory and edit that file from then on)
I also didn't want to load the library into every page, so I made
the bookmarklet "communicate" with the userscript and let it know
that it wants the library loaded, and then it executes the library
bookmarklet function after 1/5th of a second.
Below is the script I use to load my bookmarklets library now:
(with some names 'obfuscated')
// ==UserScript==
// @name Bookmarklet Library
// @namespace choose-a-namespace.com
// @description Loads bookmarklet library into page
// @include *
// @version 1.0
// @resource bmlib http://www.blah-blah-blah.com/bookmarkletlibrary.js
// @grant GM_getResourceText
// @grant unsafeWindow
// ==/UserScript==
// NOTE that the library file is a STATIC SNAPSHOT saved into the directory that contains this userscript
function checkLibraryLoadRequest()
{
if (typeof (unsafeWindow.Bm_bLibraryRequest) != 'undefined') { // value set as request from bookmarklet
if (typeof (unsafeWindow.Bm_bLibraryLoaded) == 'undefined') { // value set inside library script
var sBMLibSource = GM_getResourceText ('bmlib');
var oScript = document.createElement ('script');
oScript.type = 'text/javascript';
oScript.text = sBMLibSource;
// document.getElementsByTagName ('head')[0].appendChild (oScript);
document.body.appendChild (oScript);
}
} else {
setTimeout (checkLibraryLoadRequest, 100); // check every 100 ms
}
return;
} // checkLibraryLoadRequest
checkLibraryLoadRequest();
// EXAMPLE Bookmarklet shell/template:
// javascript:(function(){window.Bm_bLibraryRequest=true;setTimeout(function(){window['***FUNCTION-NAME***']();},200);})()
/**********************************************************************/
At the top of the library script file I have:
var Bm_bLibraryLoaded = true; // stops library from being loaded twice (just in case)
And then my bookmarklets all have the same format as the EXAMPLE noted at the
bottom of the userscript above.
I just change the "***FUNCTION-NAME***" value to the name of the function
I want to call in the library.
I kept the "communication" between the userscript and the
bookmarklet simple. So far it has worked. Although one time
it may not have, so you might want to either decrease the
"check for request time" to 50 and/or increase the
"execute bookmarklet function" time to 250.
I haven't noticed any noticeable lag time because it's
only about 1/5th of a second.
So far it works great. I can now execute my bookmarklets
from Twitter, Facebook, Google, and anywhere else, including all https:// "secure" websites.
So I suppose this will work until someone does something
else to screw us over.
btw -- I am using Pale Moon now ("old-style Firefox"),
so I don't know if there are any differences in Firefox, Chrome, etc.
But it should be fairly easily modified to work under any browser that has
a Greasemonkey-like add-on extension.
I haven't made all of this "easy to use / install" because I figure if you are a
bookmarklet person (well, a JavaScript programmer), you can easily do it and figure it out
on your own. People who are not power-users may have some issues. But the latter
must be getting their bookmarklets from someone, so that source will probably
have to be the one to set up their bookmarklets (and library) for their users.
NOTE that if people do this, they have to PREFIX their global variable names so
there is no conflict!
If you have questions or comments, you can contact the author at
whd1802Donnelly-House.net.
References:
Bookmarklets are Dead
The Slow Death of Bookmarklets
Github Content Security Policy
Content Security Policy 1.0 Lands In Firefox
CSP policy directives
|