# HTTP Observatory API Documentation
**Last Updated:** 2016-03-23
**Author:** april@mozilla.com
This document explains the HTTP Observatory API, which is used to test the state of security for websites on the public internet.
## Protocol Overview
The HTTP Observatory API is based on HTTP and JSON. All requests are either done via POST or GET requests, and all responses are in the JSON format.
## Protocol Calls
The primary endpoint of the HTTP Observatory is [https://http-observatory.security.mozilla.org/api/v1](https://http-observatory.security.mozilla.org/api/v1).
### Invoke assessment
Used to invoke a new scan of a website. By default, the HTTP Observatory will return a cached site result if the site has been scanned anytime in the previous 24 hours. Regardless of the value of `rescan`, a site can not be scanned at a frequency greater than every three minutes. It will return a single [scan object](#scan) on success.
**API Call:** `analyze`
**API Method:** `POST`
Parameters:
* `host` hostname (required)
POST parameters:
* `hidden` setting to "true" will hide a scan from public results returned by `getRecentScans`
* `rescan` setting to "true" forces a rescan of a site
Examples:
* `/api/v1/analyze?host=www.mozilla.org`
* `/api/v1/analyze?host=www.privatesite.net`
* `hidden=true&rescan=true` (POST data)
### Retrieve assessment
This is used to retrieve the results of an existing, ongoing, or completed scan. Returns a [scan object](#scan) on success.
**API Call:** `analyze`
**API Method:** `GET`
Parameters:
* `host` hostname (required)
Example:
* `/api/v1/analyze?host=www.mozilla.org`
### Retrieve test results
Each scan consists of a variety of subtests, including Content Security Policy, Subresource Integrity, etc. The results of all these tests can be retrieved once the scan's state has been placed in the `FINISHED` state. It will return a single [tests object](#tests).
**API Call:** `getScanResults`
**API Method:** `GET`
Parameters:
* `scan` scan_id number from the [scan object](#scan)
Example:
* `/api/v1/getScanResults?scan=123456`
### Retrieve recent scans
Retrieve the ten most recent scans that fall within a given score range. Maps hostnames to scores, returning a [recent scans object](#recent-scans).
**API Call:** `getRecentScans`
**API Method:** `GET`
Parameters:
* `min` minimum score
* `max` maximum score
Examples:
* `/api/v1/getRecentScans?max=20` (ten most recent "F" tests)
* `/api/v1/getRecentScans?min=90` (ten most recent "A" or better tests)
### Retrieve host's scan history
This returns all public scans made to a website. Including its grade, and score. Returning a [host history object](#host-history).
**API Call:** `getHostHistory`
**API Method:** `GET`
Parameters:
* `host` hostname (required)
Examples:
* `/api/v1/getHostHistory?host=mozilla.org` (scan history for mozilla.org)
### Retrieve overall grade distribution
This returns each possible grade in the HTTP Observatory, as well as how many scans have fallen into that grade. Returns a [grade distribution object](#grade-distribution) object.
**API Call:** `getGradeDistribution`
**API Method:** `GET`
Example:
* `/api/v1/getGradeDistribution`
### ~~Retrieve scanner states~~ `DEPRECATED`
This returns the state of the scanner. It can be useful for determining how busy the HTTP Observatory is. Returns a [Scanner state object](#scanner-state).
**API Call:** `getScannerStates`
**API Method:** `GET`
Example:
* `/api/v1/getScannerStates`
## Response Objects
### Grade distribution
Example:
```json
{
"A+": 3,
"A": 6,
"A-": 2,
"B+": 8,
"B": 76,
"B-": 79,
"C+": 80,
"C": 88,
"C-": 86,
"D+": 60,
"D": 110,
"D-": 215,
"F": 46770
}
```
### Recent scans
Example:
```json
{
"site1.mozilla.org": "A",
"site2.mozilla.org": "B-",
"site3.mozilla.org": "C+",
"site4.mozilla.org": "F",
"site5.mozilla.org": "F",
"site6.mozilla.org": "B",
"site7.mozilla.org": "F",
"site8.mozilla.org": "B+",
"site9.mozilla.org": "A+",
"site0.mozilla.org": "A-"
}
```
### Host history
Example:
```json
[
{
"end_time": "Thu, 22 Sep 2016 23:24:28 GMT",
"end_time_unix_timestamp": 1474586668,
"grade": "C",
"scan_id": 1711106,
"score": 50
},
{
"end_time": "Thu, 09 Feb 2017 01:30:47 GMT",
"end_time_unix_timestamp": 1486603847,
"grade": "B+",
"scan_id": 3292839,
"score": 80
},
{
"end_time": "Fri, 10 Feb 2017 02:30:08 GMT",
"end_time_unix_timestamp": 1486693808,
"grade": "A",
"scan_id": 3302879,
"score": 90
}
]
```
### Scan
* `end_time` timestamp for when the scan completed
* `grade` final grade assessed upon a completed scan
* `hidden` whether the scan results are unlisted on the recent results page
* `response_headers` the entirety of the HTTP response headers
* `scan_id` unique ID number assigned to the scan
* `score` final score assessed upon a completed (`FINISHED`) scan
* `likelihood_indicator` Mozilla risk likelihod indicator that is the equivalent of the grade [https://wiki.mozilla.org/Security/Standard_Levels] (https://wiki.mozilla.org/Security/Standard_Levels)
* `start_time` timestamp for when the scan was first requested
* `state` the current state of the scan
* `tests_failed` the number of subtests that were assigned a fail result
* `tests_passed` the number of subtests that were assigned a passing result
* `tests_quantity` the total number of tests available and assessed at the time of the scan
The scan can exist in one of six states:
* `ABORTED` aborted for internal technical reasons
* `FAILED` failed to complete, typically due to the site being unavailable or timing out
* `FINISHED` completed successfully
* `PENDING` issued by the API but not yet picked up by a scanner instance
* `STARTING` assigned to a scanning instance
* `RUNNING` currently in the process of scanning a website
Example:
```json
{
"end_time": "Tue, 22 Mar 2016 21:51:41 GMT",
"grade": "A",
"hidden": false,
"response_headers": { ... },
"scan_id": 1,
"score": 90,
"likelihood_indicator": "LOW",
"start_time": "Tue, 22 Mar 2016 21:51:40 GMT",
"state": "FINISHED",
"tests_failed": 2,
"tests_passed": 9,
"tests_quantity": 11
}
```
### Scanner state
Example:
```json
{
"ABORTED": 10,
"FAILED": 281,
"FINISHED": 46240,
"PENDING": 122,
"STARTING": 96,
"RUNNING": 128,
}
```
### Tests
The tests object contains one test object for each test conducted by the HTTP Observatory. Each test object is contains the following values:
* `expectation` the expectation for a test result going in
* `name` the name of the test; this should be the same as the parent object's name
* `output` artifacts related to the test; these can vary widely between tests and are not guaranteed to be stable over time.
* `data` generally as close to the raw output of the test as is possible. For example, in the strict-transport-security test, `output -> data` contains the raw `Strict-Transport-Security` header
* `????` other values under `output` have keys that vary; for example, the `strict-transport-security` test has a `includeSubDomains` key that is either set to `True` or `False`. Similarly, the `redirection` test contains a `route` key that contains an array of the URLs that were redirected to. See example below for more available keys.
* `pass` whether the test passed or failed; a test that meets or exceeds the expectation will be marked as passed
* `result` result of the test
* `score_description` short description describing what `result` means
* `score_modifier` how much the result of the test affected the final score; should range between +5 and -50
Example:
```json
{
"content-security-policy": {
"expectation": "csp-implemented-with-no-unsafe",
"name": "content-security-policy",
"output": {
"data": {
"connect-src": [
"'self'",
"https://sentry.prod.mozaws.net"
],
"default-src": [
"'self'"
],
"font-src": [
"'self'",
"https://addons.cdn.mozilla.net"
],
"frame-src": [
"'self'",
"https://ic.paypal.com",
"https://paypal.com",
"https://www.google.com/recaptcha/",
"https://www.paypal.com"
],
"img-src": [
"'self'",
"data:",
"blob:",
"https://www.paypal.com",
"https://ssl.google-analytics.com",
"https://addons.cdn.mozilla.net",
"https://static.addons.mozilla.net",
"https://ssl.gstatic.com/",
"https://sentry.prod.mozaws.net"
],
"media-src": [
"https://videos.cdn.mozilla.net"
],
"object-src": [
"'none'"
],
"report-uri": [
"/__cspreport__"
],
"script-src": [
"'self'",
"https://addons.mozilla.org",
"https://www.paypalobjects.com",
"https://apis.google.com",
"https://www.google.com/recaptcha/",
"https://www.gstatic.com/recaptcha/",
"https://ssl.google-analytics.com",
"https://addons.cdn.mozilla.net"
],
"style-src": [
"'self'",
"'unsafe-inline'",
"https://addons.cdn.mozilla.net"
]
}
},
"pass": false,
"result": "csp-implemented-with-unsafe-inline-in-style-src-only",
"score_description": "Content Security Policy (CSP) implemented with unsafe-inline inside style-src directive",
"score_modifier": -5
},
"cookies": {
"expectation": "cookies-secure-with-httponly-sessions",
"name": "cookies",
"output": {
"data": {
"sessionid": {
"domain": ".addons.mozilla.org",
"expires": null,
"httponly": true,
"max-age": null,
"path": "/",
"port": null,
"secure": true
}
}
},
"pass": true,
"result": "cookies-secure-with-httponly-sessions",
"score_description": "All cookies use the Secure flag and all session cookies use the HttpOnly flag",
"score_modifier": 0
},
"cross-origin-resource-sharing": {
"expectation": "cross-origin-resource-sharing-not-implemented",
"name": "cross-origin-resource-sharing",
"output": {
"data": {
"acao": null,
"clientaccesspolicy": null,
"crossdomain": null
}
},
"pass": true,
"result": "cross-origin-resource-sharing-not-implemented",
"score_description": "Content is not visible via cross-origin resource sharing (CORS) files or headers",
"score_modifier": 0
},
"redirection": {
"expectation": "redirection-to-https",
"name": "redirection",
"output": {
"destination": "https://addons.mozilla.org/en-US/firefox/",
"redirects": true,
"route": [
"http://addons.mozilla.org/",
"https://addons.mozilla.org/",
"https://addons.mozilla.org/en-US/firefox/"
],
"status_code": 200
},
"pass": true,
"result": "redirection-to-https",
"score_description": "Initial redirection is to https on same host, final destination is https",
"score_modifier": 0
},
"strict-transport-security": {
"expectation": "hsts-implemented-max-age-at-least-six-months",
"name": "strict-transport-security",
"output": {
"data": "max-age=31536000",
"includeSubDomains": false,
"max-age": 31536000,
"preload": false,
"preloaded": false
},
"pass": true,
"result": "hsts-implemented-max-age-at-least-six-months",
"score_description": "HTTP Strict Transport Security (HSTS) header set to a minimum of six months (15768000)",
"score_modifier": 0
},
"subresource-integrity": {
"expectation": "sri-implemented-and-external-scripts-loaded-securely",
"name": "subresource-integrity",
"output": {
"data": {
"https://addons.cdn.mozilla.net/static/js/impala-min.js?build=552decc-56eadb2f": {
"crossorigin": null,
"integrity": null
},
"https://addons.cdn.mozilla.net/static/js/preload-min.js?build=552decc-56eadb2f": {
"crossorigin": null,
"integrity": null
}
}
},
"pass": false,
"result": "sri-not-implemented-but-external-scripts-loaded-securely",
"score_description": "Subresource Integrity (SRI) not implemented, but all external scripts are loaded over https",
"score_modifier": -5
},
"x-content-type-options": {
"expectation": "x-content-type-options-nosniff",
"name": "x-content-type-options",
"output": {
"data": "nosniff"
},
"pass": true,
"result": "x-content-type-options-nosniff",
"score_description": "X-Content-Type-Options header set to \"nosniff\"",
"score_modifier": 0
},
"x-frame-options": {
"expectation": "x-frame-options-sameorigin-or-deny",
"name": "x-frame-options",
"output": {
"data": "DENY"
},
"pass": true,
"result": "x-frame-options-sameorigin-or-deny",
"score_description": "X-Frame-Options (XFO) header set to SAMEORIGIN or DENY",
"score_modifier": 0
},
"x-xss-protection": {
"expectation": "x-xss-protection-disabled",
"name": "x-xss-protection",
"output": {
"data": "1; mode=block"
},
"pass": true,
"result": "x-xss-protection-enabled-mode-block",
"score_description": "X-XSS-Protection header set to \"1; mode=block\"",
"score_modifier": 0
}
}
```