Plugins:PluginDirectory/HowPluginDetectionWorks: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
 
(27 intermediate revisions by the same user not shown)
Line 1: Line 1:
Building a service to help keep plugins up to date is hard.   
Building a service to help identify plugins in need of upgrades is complicatedIn a nutshell, the process consists of:
 
* browser identification;
* a local client plugin scan;
* requests to a plugin directory search service;
* and a local comparison of versions.
 
This page seeks to provide details on how each of these steps work, as well as identifying shortcomings and oddities along the way.


__TOC__
__TOC__
Line 21: Line 28:
Most browsers expose data on installed plugins to client-side JavaScript via a
Most browsers expose data on installed plugins to client-side JavaScript via a
property named <code>navigator.plugins</code>.  (Microsoft Internet Explorer is
property named <code>navigator.plugins</code>.  (Microsoft Internet Explorer is
an exception to this rule, but more on that later.)
an exception to this rule, [[Plugins:PluginDirectory/HowPluginDetectionWorks#The_Internet_Explorer_problem|but more on that later]].)


=== navigator.plugins ===
=== navigator.plugins ===


The <code>navigator.plugins</code> property may be used as a list, with each
[https://developer.mozilla.org/En/DOM/Window.navigator.plugins The <code>navigator.plugins</code> property] may be used as a list, with each
element in the list representing an installed plugin.  Each of these elements
element in the list representing an installed plugin.  Each of these elements
generally exposes the following properties, each assigned by the plugin author
generally exposes the following properties, each assigned by the plugin author
Line 39: Line 46:
=== No reliable GUIDs or UUIDs ===
=== No reliable GUIDs or UUIDs ===


There is no reliable unique identifier, such as a GUID or UUID.
Across all plugins, there is no single reliable unique identifier - such as a GUID or UUID.


This means that we'll need to use a combination of all of the above properties
This means that we'll need to use a combination of all of the above properties
Line 49: Line 56:
acquisition of Macromedia by Adobe.
acquisition of Macromedia by Adobe.


=== Version is not directly exposed, scraped from name / description ===
=== Version is not directly exposed ===


A version property for plugins is not exposed by most browsers in
A version property for plugins is not exposed by most browsers in
<code>navigator.plugins</code>.
<code>navigator.plugins</code>.


Most plugins include some expression of a version in the name or
Luckily, most plugins include some expression of a version in the name or
description.  But, this version does not always exactly match the  
description.  But, this version does not always exactly match the  
'''fully qualified version number''' of the plugin.  Thus, there can be some
'''fully qualified version number''' of the plugin.  Thus, there can be some
discrepancy or fuzziness in version detection.
discrepancy or imprecision in version detection.


For example, use the '''Tools > Add-ons''' menu in Firefox to open the Add-ons dialog
For example, use the '''Tools > Add-ons''' menu in Firefox to open the Add-ons dialog
and view the Plugins tab. If you have "Shockwave Flash" installed, you may see
and view the Plugins tab. If you have "Shockwave for Director" installed, you may see
it reported as version 10.0.45.2.  However, this version is not usually available via
it reported as version 11.5.6.606.   
 
However, this version is not usually available via
<code>navigator.plugins</code>. Instead, client-side detection code has to
<code>navigator.plugins</code>. Instead, client-side detection code has to
inspect the name or description - which lists the version as "10.0 r45".
inspect the description. [https://bug546727.bugzilla.mozilla.org/attachment.cgi?id=427718 On Windows, this reads "''Adobe Shockwave for Director Netscape plug-in, version 11.5''".] We can extract the version "''11.5''" from that text.


So, suppose that there were two releases of Flash - say, versions 10.0.45.2 and
Now, suppose that there were two releases of Shockwave for Director - say, versions 11.5.6.606 and
10.0.45.6. Now suppose that they both listed "10.0 r45" as the version in the  
11.5.7.235. If they both list "11.5" as the version in the  
description. Since client-side code can't see the real '''fully qualified version number'''
description, we're stuck. Since client-side code can't see the real '''fully qualified version number'''
for either plugin, it can't tell a difference between the two. And, if it can't
for either plugin, it can't tell a difference between the two. And, if it can't
tell a difference, then there's no way to decide whether an upgrade is advisable.
tell a difference, then there's no way to decide whether an upgrade is advisable.
In practice, the Adobe Flash plugin in particular tends to update that version in
the description between siginificant updates - but not all plugins do.  So, plugin
version detection can be problematic and imprecise.


And, what's worse: Some plugins don't include a version number at all, neither in the the name nor description. (eg. Adobe Acrobat Reader has often presented this difficulty.) In this case, there's not much we can do besides shrug
And, what's worse: Some plugins don't include a version number at all, neither in the the name nor description. (eg. Adobe Acrobat Reader has often presented this difficulty.) In this case, there's not much we can do besides shrug
and report the version as undetectable.
and report the version as undetectable.
Our technique of last resort is to investigate custom hacks per-plugin to dig up version-related details. This tends to involve things like embedding a sample instance of the plugin and probing it for capabilities and information. But, this doesn't always work and can be very inefficient when it does. And, in the case of plugins with known vulnerabilities, this can expose the user to the very crashes and security problems that the plugin detection is meant to warn about.


On the bright side, Firefox 3.6 and above ''does'' offer a version property in  
On the bright side, Firefox 3.6 and above ''does'' offer a version property in  
<code>navigator.plugins</code>, which reflects the '''fully qualified version number'''
<code>navigator.plugins</code>, which reflects the '''fully qualified version number'''
as seen in the Add-ons dialog. In this case, we can detect plugin versions with
as seen in the Add-ons dialog. In this case, we will be able to detect plugin versions with
great certainty. Hopefully more browsers follow suit in this, and the plugin
greater certainty. Hopefully more browsers follow suit in this, and the plugin
version detection story will get better.
version detection story will get better.
('''Note:''' As of this writing, [https://www.mozilla.com/en-US/plugincheck/ the Plugin Check page] has not yet been revised to take advantage of the new version property in Firefox 3.6.  So, the limitations described above still apply.)
==== The Internet Explorer problem ====
If you're using Microsoft Internet Explorer, none of the above applies.
On IE, the <code>navigator.plugins</code> property is empty, and no plugin information is exposed. Thus, the only technique left available is to [http://developer.apple.com/internet/webcontent/detectplugins.html attempt to embed sample instances of known popular plugins and probe them for info/capabilities]. An error in embedding a plugin tells us it's not installed, but there's no way to get a full list of what ''is'' installed.
Since this approach to plugin detection is so radically different than what works on most other browsers, we've been slow to work on support for IE.


== Server-side plugin search ==
== Server-side plugin search ==
Line 183: Line 200:
Since multiple plugins can claim to handle the same set of MIME times passed
Since multiple plugins can claim to handle the same set of MIME times passed
to the search API, multiple plugin results may be returned.
to the search API, multiple plugin results may be returned.
(By the way: Another goal of the plugin directory is to help a browser find plugins to suggest for handling an unknown MIME type, so returning multiple plugins for a set of MIME types helps serve that goal.)


=== Plugin name matching via aliases ===
=== Plugin name matching via aliases ===
Line 216: Line 235:
; newer : The installed plugin appears to be newer than what the directory knows about
; newer : The installed plugin appears to be newer than what the directory knows about
; should_disable : The installed plugin matches the latest known version, but that version has a known vulnerability and should be disabled until an update is made available.
; should_disable : The installed plugin matches the latest known version, but that version has a known vulnerability and should be disabled until an update is made available.
; maybe_outdated: Version detection for this plugin is imprecise, and matches both the latest version as well as a version known to be out of date.  Manual inspection is advised.
; maybe_outdated: [[Plugins:PluginDirectory/HowPluginDetectionWorks#Version_is_not_directly_exposed.2C_scraped_from_name_.2F_description|Version detection for this plugin is imprecise]], and matches both the latest version as well as a version known to be out of date.  Manual inspection is advised.
; maybe_vulnerable : Version detection for this plugin is imprecise, and matches both the latest version as well as a version with a known vulnerability.  Manual inspection is advised.
; maybe_vulnerable : [[Plugins:PluginDirectory/HowPluginDetectionWorks#Version_is_not_directly_exposed.2C_scraped_from_name_.2F_description|Version detection for this plugin is imprecise]], and matches both the latest version as well as a version with a known vulnerability.  Manual inspection is advised.


If the installed plugin doesn't match or exceed the version of the latest known
If the installed plugin doesn't match or exceed the version of the latest known

Latest revision as of 14:57, 19 February 2010

Building a service to help identify plugins in need of upgrades is complicated. In a nutshell, the process consists of:

  • browser identification;
  • a local client plugin scan;
  • requests to a plugin directory search service;
  • and a local comparison of versions.

This page seeks to provide details on how each of these steps work, as well as identifying shortcomings and oddities along the way.

Browser identification

The first step in the process is identifying the user's browser. This consists of determining values for the following properties:

appID
A unique identifier for the browser application (eg. {ec8030f7-c20a-464f-9b0e-13a3a9e97384} for Firefox, Safari for Safari)
appRelease
The official release version number for the browser (eg. 3.5.8, 4.0.4)
appVersion
A more specific indicator of the release build (eg. 20100202, 531.21.10)
clientOS
An identifier describing the OS on which the browser runs (eg. Windows NT 6.1, Linux i686, Intel Mac OS X 10.6)
chromeLocale
The primary locale / language chosen by the user (eg. en-US, zn-CH)

Although most plugins are compatible with a wide range of values for the above properties, it is possible for plugins to assert compatibility with any permutation of these identifying properties.

Client plugin scan

Most browsers expose data on installed plugins to client-side JavaScript via a property named navigator.plugins. (Microsoft Internet Explorer is an exception to this rule, but more on that later.)

navigator.plugins

The navigator.plugins property may be used as a list, with each element in the list representing an installed plugin. Each of these elements generally exposes the following properties, each assigned by the plugin author or vendor:

name
A short name for the plugin
description
A longer full-text description
filename
The filename of the binary library containing the plugin's executable code
mimetypes
A list of MIME media types the plugin claims to handle

You might notice that this list is missing a few particularly useful details:

No reliable GUIDs or UUIDs

Across all plugins, there is no single reliable unique identifier - such as a GUID or UUID.

This means that we'll need to use a combination of all of the above properties to uniquely identify a plugin, any of which has the potential to change between plugin versions.

For example: The Adobe Flash plugin has gone through many changes throughout its lifespan, both through changes in the name of the product itself to the acquisition of Macromedia by Adobe.

Version is not directly exposed

A version property for plugins is not exposed by most browsers in navigator.plugins.

Luckily, most plugins include some expression of a version in the name or description. But, this version does not always exactly match the fully qualified version number of the plugin. Thus, there can be some discrepancy or imprecision in version detection.

For example, use the Tools > Add-ons menu in Firefox to open the Add-ons dialog and view the Plugins tab. If you have "Shockwave for Director" installed, you may see it reported as version 11.5.6.606.

However, this version is not usually available via navigator.plugins. Instead, client-side detection code has to inspect the description. On Windows, this reads "Adobe Shockwave for Director Netscape plug-in, version 11.5". We can extract the version "11.5" from that text.

Now, suppose that there were two releases of Shockwave for Director - say, versions 11.5.6.606 and 11.5.7.235. If they both list "11.5" as the version in the description, we're stuck. Since client-side code can't see the real fully qualified version number for either plugin, it can't tell a difference between the two. And, if it can't tell a difference, then there's no way to decide whether an upgrade is advisable.

And, what's worse: Some plugins don't include a version number at all, neither in the the name nor description. (eg. Adobe Acrobat Reader has often presented this difficulty.) In this case, there's not much we can do besides shrug and report the version as undetectable.

Our technique of last resort is to investigate custom hacks per-plugin to dig up version-related details. This tends to involve things like embedding a sample instance of the plugin and probing it for capabilities and information. But, this doesn't always work and can be very inefficient when it does. And, in the case of plugins with known vulnerabilities, this can expose the user to the very crashes and security problems that the plugin detection is meant to warn about.

On the bright side, Firefox 3.6 and above does offer a version property in navigator.plugins, which reflects the fully qualified version number as seen in the Add-ons dialog. In this case, we will be able to detect plugin versions with greater certainty. Hopefully more browsers follow suit in this, and the plugin version detection story will get better.

(Note: As of this writing, the Plugin Check page has not yet been revised to take advantage of the new version property in Firefox 3.6. So, the limitations described above still apply.)

The Internet Explorer problem

If you're using Microsoft Internet Explorer, none of the above applies.

On IE, the navigator.plugins property is empty, and no plugin information is exposed. Thus, the only technique left available is to attempt to embed sample instances of known popular plugins and probe them for info/capabilities. An error in embedding a plugin tells us it's not installed, but there's no way to get a full list of what is installed.

Since this approach to plugin detection is so radically different than what works on most other browsers, we've been slow to work on support for IE.

Server-side plugin search

One of the goals of the Plugin Directory is to provide a service allowing clients to check whether installed plugins are up-to-date or in need of an upgrade, by performing a search on directory data.

The first thing the service requires are all the details revealed by browser identification. This allows the search to be restricted to just those plugins asserting compatibility with the user's browser.

The second thing the service requires are details revealed by the client plugin scan. One of the most unique properties of a plugin is the set of MIME types it claims to handle, so sending that along will further restrict the search to plugins that likely contain a given installed plugin.

(By the way: While it might be useful to send along other details - such as name, description, or filename - these change more often than the set of MIME types, and don't actually help narrow down the search enough to be worth the trouble.)

PFS2 search API

The above is a plain-language description of the PFS2 search API. This is a simple HTTP GET service which responds with a JSON data structure, given the following URL parameters:

mimetype
A space-separated list of mimetypes for a plugin. (Note: space can be encoded as + in URLs.)
detection
An identifier for the form of version detection available to the client (eg. 'original' for name/description scraping, 'version_available' for Fx 3.6 and above where a real version is available)
clientOS
The client's OS (eg. navigator.oscpu, "Windows NT 5.1", "Intel Mac OS X 10.5")
chromeLocale
The client's locale (eg. navigator.language)
appID
The client's app ID (eg. Firefox is "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
appRelease
Client app release version (eg. "3.5.3")
appVersion
Client's application build version (eg. "20090824085414")
callback
Function wrapper for JSON output, (see also: JSONP technique)

Example PFS2 request / response

So, suppose you wanted to search for the Flash plugin. On the Plugin Directory staging server, you could perform an HTTP GET like the following, using cURL (line breaks added for clarity):

   curl -s 'http://plugins.stage.mozilla.com/en-us/pfs/v2?
       appID=%7Bec8030f7-c20a-464f-9b0e-13a3a9e97384%7D&
       appRelease=3.5.8&
       appVersion=20100202152834&
       clientOS=Intel+Mac+OS+X+10.6&
       chromeLocale=en-US&
       detection=original&
       mimetype=application%2Fx-shockwave-flash+application%2Ffuturesplash'

This might result in the following JSON, for example:

   [
       {
           "releases": {
               "others": [], 
               "latest": {
                   "app_release": "*", 
                   "fetched": "2010-02-18T19:06:27-08:00", 
                   "locale": "*", 
                   "app_id": "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}", 
                   "detection_type": "original", 
                   "guid": "{89977581-9028-4be0-b151-7c4f9bcd3211}", 
                   "xpi_location": "http://fpdownload.macromedia.com/get/flashplayer/xpi/current/flashplayer-mac.xpi", 
                   "pfs_id": "adobe-flash-player", 
                   "version": "10.0.32.0", 
                   "app_version": "*", 
                   "status": "latest", 
                   "os_name": "mac", 
                   "vendor": "Adobe", 
                   "detected_version": "10.0.32.0", 
                   "name": "Adobe Flash Player", 
                   "created": "2010-02-19T08:09:18+00:00", 
                   "url": "http://www.adobe.com/go/getflashplayer", 
                   "modified": "2010-02-19T08:11:10+00:00", 
                   "license_url": "http://www.adobe.com/go/eula_flashplayer"
               }
           }, 
           "aliases": {
               "regex": [
                   ".*Flash.*"
               ], 
               "literal": [
                   "Adobe Flash Player", 
                   "Shockwave Flash"
               ]
           }
       }
   ]

A more detailed description of this exchange is available in the wiki page on the PFS2 API.

Client-side plugin matching

A single search request is made to the PFS2 API for each plugin detected. Since multiple plugins can claim to handle the same set of MIME times passed to the search API, multiple plugin results may be returned.

(By the way: Another goal of the plugin directory is to help a browser find plugins to suggest for handling an unknown MIME type, so returning multiple plugins for a set of MIME types helps serve that goal.)

Plugin name matching via aliases

So, the next step is to look at each of these results and use the "aliases" data stucture to match against the detected plugin's name. If the name matches either one of the literal strings values, or one of the regular expressions, then that search result is paired up with the detected plugin.

Note that this alias matching system helps account for name changes between plugin releases - eg. Shockwave Flash to Adobe Flash Player, as a hypothetical example.

For example: Assume that a plugin named "QuickTime Plug-in" was detected, which claims to handle the MIME type "video/mpeg".

In response to a search including a MIME type of "video/mpeg", the search API might respond with results including the plugins "VLC Multimedia Plug-in" and "QuickTime Plug-in". We can use the aliases of the "QuickTime Plug-in" result to pair it up with our detected plugin, and go from there.

Version comparison

Once we have a search result paired with a detected installed plugin, we can perform version comparisons.

For quick access, the latest release of a plugin is pulled out of the general list of plugin releases known by the directory. A comparison between this release and the detected plugin can result in one of the following results:

unknown
No version was detected for the installed plugin, so no comparison to the directory is possible. This status is listed here, but it's actually determined much earlier in the process and prevents a request to the search API.
current
The installed plugin is the up-to-date with respect to the directory
newer
The installed plugin appears to be newer than what the directory knows about
should_disable
The installed plugin matches the latest known version, but that version has a known vulnerability and should be disabled until an update is made available.
maybe_outdated
Version detection for this plugin is imprecise, and matches both the latest version as well as a version known to be out of date. Manual inspection is advised.
maybe_vulnerable
Version detection for this plugin is imprecise, and matches both the latest version as well as a version with a known vulnerability. Manual inspection is advised.

If the installed plugin doesn't match or exceed the version of the latest known release, then we know an upgrade is advised. But, we want to report on the urgency of the upgrade.

So, we step through the other known releases, looking for vulnerable versions. This will then result in one of the following status flags:

outdated
The installed plugin should be upgraded, but is not an immediate danger
vulnerable
The installed plugin matches a version with a known vulnerability, and should be updated immediately