Skip to content
Memmie Lenglet edited this page Jun 14, 2017 · 18 revisions

Policy enforced on a resource SHOULD NOT interfere with the operation of user-agent features like addons, extensions, or bookmarklets. These kinds of features generally advance the user’s priority over page authors, as espoused in [HTML-DESIGN].

Content Security Policy Level 3

But implementations are incorrect:

  • Firefox: bookmarklets are executed as non-privileged script, blocked by CSP (unsafe-inline script). And document.execCommand("cut") (or "copy" or "past") is not allowed, will log the following error: document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler..
  • Chrome: execute bookmarklet as non-privileged script, but ignoring CSP directives. However subsequent resource (injected scripts by the bookmarklet. Ex: document.createElement("script")) are still subject to CSP, CORS check and mixed content restriction (HTTPS vs HTTP)

See also:

Workarounds test

CSP violation reports

Doesn't work because reports don't provide the URI (blocked-uri) like javascript:... but self (at least Firefox):

{
  "csp-report": {
    "document-uri": "http://example.com/signup.html",
    "referrer": "",
    "blocked-uri": "http://example.com/css/style.css",
    "violated-directive": "style-src cdn.example.com",
    "original-policy": "default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports"
  }
}

As you can see, the report includes the full path to the violating resource in blocked-uri. This is not always the case. For example, when the signup.html would attempt to load CSS from http://anothercdn.example.com/stylesheet.css, the browser would not include the full path but only the origin (http://anothercdn.example.com). The CSP specification gives an explanation of this odd behaviour. In summary, this is done to prevent leaking sensitive information about cross-origin resources. Note, that even the specification got this part wrong in its example on violation reports (blocked-uri should be http://evil.example.com).

Content Security Policy (CSP) - HTTP | MDN

Register a CSP violation report and handle violation generated by bookmarklets.

Intercept the report-uri /my-custom-resport-url request or use the securitypolicyviolation DOM event (only implemented in Chrome) or CSP3 report-to

Bookrmarklet source-hash to CSP

It doesn't work because CSP don't allow to specify a source-hash for javascript URIs (CSP2):

Add source hashes of all bookmarklets to the CSP script-src directive

if at least one nonce-source or hash-source is present in the list of allowed script sources:

  • [...]
  • Whenever the user agent would execute script contained in a javascript URL, instead the user agent MUST NOT execute the script, and MUST report a violation.

Content Security Policy Level 2

The inline script restrictions imposed by CSP include script valued attributes (commonly used for DOM Level 0 event handlers, e.g. onclick); hash-source and nonce-source cannot help you with these. Currently CSP does not provide mechanisms to apply directives to such script valued attributes but let’s see what the future brings!

CSP for the web we have | Mozilla Security Blog

See the add-csp-source-hash-idea branch

Custom protocol

It's not ideal, because the user net to update all her/his bookmarklets to use this custom protocol and she/he want to execute the bookmarklet for the current document, not to navigate to an other document/application, then go back. And it's hard to implement.

Create a custom protocol. Ex: bookmarklet::

  • register it with navigator.registerProtocolHandler() to point Point to an empty document (like about:blank?%s or https://bookmarklet/%s) that the WebExtension could handle for execute the bookmarklet source
  • or create a native application handle the protocol (more complexe)

Note: (as current implementation) the protocol name need to be prefixed by web+ if it's not from the provided list

Context menu

The only one working solution, but require a specific UI (context menu). Still face issues with mixed content restrictions (HTTPS vs HTTP) and CSP applied on injected subresource

Recreate the bookmarks hierarchy, but with only bookmarklets in a context menu.

Navigation event

It's doesn't work. WebExtension API webNavigation.onBeforeNavigate (in Firefox and Chrome) don't receive any event when navigate to javascript: URIs. In fact only ftp://, http(s):// and custom protocols (registered or not) are handled, but not javascript:, data: nor moz-extension://

Listen to javascript: URIs navigation.

Toggle CSP

It doesn't work, because we can only rewrite CSP via headers, before the document be parsed, not after:

Modifications to the content attribute of a meta element after the element has been parsed will be ignored.

Content Security Policy Level 2

It's not recommended, because this open the possibility to a malicious script to be executed (disable the author's CSP rules)

Allow temporary 'unsafe-inline' scripts sources.

Use WebExtension page action.

Other

Note: WebDeveloper tools network tab display the original header (as defined before addons rewrite it).

Note: Does browsers have an internal limit (client side) for header length?

Firefox call stack from bookmark click to javascript: URI execution:

menupopup#BMB_bookmarksPopup.onclick - browser/base/content/browser.xul
↳ BookmarksEventHandler.onClick - browser/base/content/browser-places.js
  ↳ BookmarksEventHandler.onCommand - browser/base/content/browser-places.js
    ↳ PlacesUIUtils.openNodeWithEvent - browser/components/places/PlacesUIUtils.jsm
      ↳ PlacesUIUtils._openNodeIn - browser/components/places/PlacesUIUtils.jsm
        ↳ openUILinkIn - browser/base/content/utilityOverlay.js
          ↳ openLinkIn - browser/base/content/utilityOverlay.js
            ↳ loadURIWithFlags - toolkit/content/widgets/browser.xml
              ↳ loadURIWithOptions - toolkit/content/widgets/browser.xml
                ↳ RemoteWebNavigation#loadURIWithOptions - toolkit/components/remotebrowserutils/RemoteWebNavigation.js
                  ↳ _sendMessage → browser.messageManager.sendAsyncMessage "WebNavigation:LoadURI" - toolkit/components/remotebrowserutils/RemoteWebNavigation.js
                    ↳ receiveMessage "WebNavigation:LoadURI" - toolkit/content/browser-child.js
                      ↳ loadURI - toolkit/content/browser-child.js
                        ↳ nsDocShell::LoadURIWithOptions - docshell/base/nsDocShell.cpp
                          ↳ nsDocShell::LoadURI - docshell/base/nsDocShell.cpp
                            ↳ nsDocShell::InternalLoad - docshell/base/nsDocShell.cpp
                              ↳ nsDocShell::DoURILoad - docshell/base/nsDocShell.cpp where channel = nsJSChannel dom/jsurl/nsJSProtocolHandler.cpp; uriLoader = do_GetService("@mozilla.org/uriloader;1"
                                ↳ nsDocShell::DoChannelLoad - docshell/base/nsDocShell.cpp
                                  ↳ nsURILoader::OpenURI - uriloader/base/nsURILoader.cpp
                                    ↳ nsURILoader::OpenChannel - uriloader/base/nsURILoader.cpp
                                      ↳ nsJSChannel::GetURI - dom/jsurl/nsJSProtocolHandler.cpp
                                        ↳ ??
                                          ↳ nsJSChannel::AsyncOpen - dom/jsurl/nsJSProtocolHandler.cpp
                                            ↳ nsJSChannel::EvaluateScript - dom/jsurl/nsJSProtocolHandler.cpp