Skip to content
This repository has been archived by the owner on Feb 1, 2019. It is now read-only.

Commit

Permalink
Bug 738521: More scrupulous Jetpack checks:
Browse files Browse the repository at this point in the history
   • Warn if the SDK version used is not the latest known release.
   • Warn if any Jetpack module is not from the SDK version the
     add-on claims to use.
  • Loading branch information
kmaglione committed Jul 17, 2012
1 parent dac38cb commit 6bb168e
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 26 deletions.
Binary file added tests/resources/jetpack/jetpack-1.8-outdated.xpi
Binary file not shown.
Binary file not shown.
60 changes: 40 additions & 20 deletions tests/test_jetpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
from validator.testcases.markup.markuptester import MarkupParser
import validator.testcases.jetpack as jetpack
from validator.errorbundler import ErrorBundle
from validator.xpi import XPIManager

def _do_test(xpi_package):
def _do_test(xpi_package, allow_old_sdk=True):

err = ErrorBundle()
jetpack.inspect_jetpack(err, xpi_package)
jetpack.inspect_jetpack(err, xpi_package, allow_old_sdk=allow_old_sdk)
return err

class MockXPI:
Expand Down Expand Up @@ -58,17 +59,13 @@ def test_bad_harnessoptions():
def test_pass_jetpack():
"""Test that a minimalistic Jetpack setup will pass."""

harnessoptions = {"sdkVersion": "foo",
harnessoptions = {"sdkVersion": "1.8-dev",
"jetpackID": "",
"manifest": {}}

with open("tests/resources/bootstrap.js") as bootstrap_file:
bootstrap = bootstrap_file.read()
with open("jetpack/addon-sdk/packages/test-harness/lib/"
"harness.js") as harness_file:
harness = harness_file.read()
err = _do_test(MockXPI({"bootstrap.js": bootstrap,
"components/harness.js": harness,
"harness-options.json":
json.dumps(harnessoptions)}))
print err.print_summary(verbose=True)
Expand All @@ -80,16 +77,11 @@ def test_pass_jetpack():
assert pretested_files
assert "bootstrap.js" in pretested_files

# Even though harness.js is no longer in Jetpack 1.4+ add-ons, we should
# make sure that we don't start throwing errors for it for older Jetpack
# add-ons.
assert "components/harness.js" in pretested_files


def test_missing_elements():
"""Test that missing elements in harness-options will fail."""

harnessoptions = {"sdkVersion": "foo",
harnessoptions = {"sdkVersion": "1.8-dev",
"jetpackID": ""}

with open("tests/resources/bootstrap.js") as bootstrap_file:
Expand All @@ -104,7 +96,7 @@ def test_missing_elements():
def test_skip_safe_files():
"""Test that missing elements in harness-options will fail."""

harnessoptions = {"sdkVersion": "foo",
harnessoptions = {"sdkVersion": "1.8-dev",
"jetpackID": "",
"manifest": {}}

Expand All @@ -131,7 +123,7 @@ def test_pass_manifest_elements():

harnessoptions = {
"jetpackID": "foobar",
"sdkVersion": "foo",
"sdkVersion": "1.8-dev",
"manifest": {
"bootstrap.js":
{"requirements": {},
Expand Down Expand Up @@ -166,7 +158,7 @@ def test_ok_resource():

harnessoptions = {
"jetpackID": "foobar",
"sdkVersion": "foo",
"sdkVersion": "1.8-dev",
"manifest": {
"resource://bootstrap.js":
{"requirements": {},
Expand All @@ -192,7 +184,7 @@ def test_bad_resource():
bootstrap_hash = hashlib.sha256(bootstrap).hexdigest()

harnessoptions = {
"sdkVersion": "foo",
"sdkVersion": "1.8-dev",
"jetpackID": "foobar",
"manifest":
{"http://foo.com/bar/bootstrap.js":
Expand All @@ -219,7 +211,7 @@ def test_missing_manifest_elements():
bootstrap_hash = hashlib.sha256(bootstrap).hexdigest()

harnessoptions = {
"sdkVersion": "foo",
"sdkVersion": "1.8-dev",
"jetpackID": "foobar",
"manifest":
{"resource://bootstrap.js":
Expand All @@ -244,7 +236,7 @@ def test_mismatched_hash():
"""

harnessoptions = {
"sdkVersion": "foo",
"sdkVersion": "1.8-dev",
"jetpackID": "foobar",
"manifest":
{"resource://bootstrap.js":
Expand Down Expand Up @@ -278,7 +270,7 @@ def test_mismatched_db_hash():
bootstrap_hash = hashlib.sha256(bootstrap).hexdigest()

harnessoptions = {
"sdkVersion": "foo",
"sdkVersion": "1.8-dev",
"jetpackID": "foobar",
"manifest":
{"resource://bootstrap.js":
Expand Down Expand Up @@ -306,6 +298,19 @@ def test_mismatched_db_hash():
"resources/bootstrap.js"])


def test_mismatched_module_version():
"""
Tests that add-ons using modules from a version of the SDK
other than the version they claim.
"""

xpi = XPIManager("tests/resources/jetpack/jetpack-1.8-pretending-1.8.1.xpi")
err = _do_test(xpi)

assert err.failed()
assert any(w["id"][2] == "mismatched_version" for w in err.warnings)


def test_absolute_uris_in_js():
"""
Test that a warning is thrown for absolute URIs within JS files.
Expand Down Expand Up @@ -363,3 +368,18 @@ def test_bad_sdkversion():
json.dumps(harnessoptions)}))
assert err.failed() and err.errors


def test_outdated_sdkversion():
"""
Tests that add-ons using a version other than the latest release
are warned against, but module hashes are still recognized.
"""

xpi = XPIManager("tests/resources/jetpack/jetpack-1.8-outdated.xpi")
err = _do_test(xpi, allow_old_sdk=False)

assert err.failed()
# Make sure we don't have any version mismatch warnings
eq_(len(err.warnings), 1)
eq_(err.warnings[0]["id"][2], "outdated_version")

106 changes: 106 additions & 0 deletions tests/test_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from nose.tools import eq_

from validator.version import Version, VersionPart

def test_versionpart_stringify():
"""Tests that VersionPart objects stringify to their original string."""

PART = "0b0b"
eq_(str(VersionPart(PART)), PART)

def test_version_stringify():
"""Tests that Version objects stringify to their original string."""

PART = "0b0b"
VERSION = ".".join((PART, PART, PART, PART))
eq_(str(Version(VERSION)), VERSION)

def test_versionpart_eq():
"""Tests that VersionPart objects equal themselves."""

PART = "0b0b"
a = VersionPart(PART)
b = VersionPart(PART)
eq_(a, a)
eq_(a, b)
assert not (a < b)
assert not (a > b)

def test_version_eq():
"""Tests that Version objects equal themselves."""

PART = "0b0b"
VERSION = ".".join((PART, PART, PART, PART))
a = Version(VERSION)
b = Version(VERSION)
eq_(a, a)
eq_(a, b)
assert not (a < b)
assert not (a > b)

def test_nullstring_greater_than_string():
"""
Tests that null strings in versions are greater than non-null
strings.
"""

assert VersionPart("1") > VersionPart("1a")
assert VersionPart("1a1") > VersionPart("1a1a")

def test_number_part_comparison():
"""
Tests that number comparisons work as expected in VersionParts.
"""

assert VersionPart("1") < VersionPart("2")
assert VersionPart("3") < VersionPart("20")

assert VersionPart("a1") < VersionPart("a2")
assert VersionPart("a3") < VersionPart("a20")

assert VersionPart("1a1") < VersionPart("1a2")
assert VersionPart("1a3") < VersionPart("1a20")

def test_number_part_comparison():
"""
Tests that string comparisons work as expected in VersionParts.
"""

assert VersionPart("a") < VersionPart("b")
assert VersionPart("1a") < VersionPart("1b")
assert VersionPart("1a1a") < VersionPart("1b1b")
assert VersionPart("1a1a20") < VersionPart("1b1b3")

def test_nullpart_less_than_part():
"""
Tests that null version parts in versions are less than non-null
parts.
"""

assert Version("1") < Version("1.0")

def test_greater_part():
"""
Tests that a greater VersionPart works as expected in Version
comparisons.
"""

assert VersionPart("1") < VersionPart("2")
assert Version("1.1") < Version("1.2")

def test_asterisk_greater_than_charcode():
"""
'*' is greater than any other value in version parts. Test that
it is greater than a character with a greater charcode than
itself.
"""

assert "," > "*"
assert VersionPart("*") > VersionPart(",")

def test_magical_plus_equals_plusone_pre_nonsense():
"""
Test that the magical behavior where 1+ == 2pre is preserved.
"""

eq_(VersionPart("1+"), VersionPart("2pre"))
56 changes: 50 additions & 6 deletions validator/testcases/jetpack.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
from collections import defaultdict
import hashlib
import json
import os

import validator.decorator as decorator
from validator.constants import PACKAGE_EXTENSION
from validator.version import Version
from content import FLAGGED_FILES


SAFE_FILES = (".jpg", ".ico", ".png", ".gif", ".txt")

EMPTY_FILE_SHA256SUMS = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",

@decorator.register_test(tier=1, expected_type=PACKAGE_EXTENSION)
def inspect_jetpack(err, xpi_package):
def inspect_jetpack(err, xpi_package, allow_old_sdk=False):
"""
If the add-on is a Jetpack extension, its contents should be tested to
ensure that none of the Jetpack libraries have been tampered with.
Expand Down Expand Up @@ -84,15 +87,31 @@ def inspect_jetpack(err, xpi_package):
jetpack_data = open(os.path.join(os.path.dirname(__file__),
"jetpack_data.txt"))
# Parse the jetpack data into something useful.
jetpack_hash_table = {}
for line in [x.split() for x in jetpack_data]:
jetpack_hash_table[line[-1]] = tuple(line[:-1])
jetpack_hash_table = defaultdict(dict)
latest_jetpack = None
for path, version_str, hash in map(str.split, jetpack_data):
version = Version(version_str)
if version.is_release and (not latest_jetpack or
version > latest_jetpack):
latest_jetpack = version
jetpack_hash_table[hash][version_str] = path

if not allow_old_sdk and Version(sdk_version) != latest_jetpack:
err.warning(
err_id=("testcases_jetpack", "inspect_jetpack",
"outdated_version"),
warning="Outdated version of Add-on SDK",
description="You are using version %s of the Add-on SDK, "
"which is outdated. Please upgrade to version "
"%s and repack your add-on"
% (sdk_version, latest_jetpack))

# Prepare a place to store mentioned hashes.
found_hashes = set()

loaded_modules = []
tested_files = {}
file_hashes = {}
unknown_files = []
mandatory_module_elements = ("moduleName", "packageName", "requirements",
"sectionName", "docsSHA256", "jsSHA256")
Expand Down Expand Up @@ -145,6 +164,7 @@ def inspect_jetpack(err, xpi_package):
continue

blob_hash = hashlib.sha256(xpi_package.read(zip_path)).hexdigest()
file_hashes[zip_path] = blob_hash

# Make sure that the module's hash matches what the manifest says.
if blob_hash != module["jsSHA256"]:
Expand Down Expand Up @@ -187,8 +207,9 @@ def inspect_jetpack(err, xpi_package):
filename in FLAGGED_FILES):
continue

blob = xpi_package.read(filename)
blob_hash = hashlib.sha256(blob).hexdigest()
blob_hash = (file_hashes.get(filename, None) or
hashlib.sha256(xpi_package.read(filename)).hexdigest())
file_hashes[filename] = blob_hash

if blob_hash not in jetpack_hash_table:
unknown_files.append(filename)
Expand All @@ -201,6 +222,29 @@ def inspect_jetpack(err, xpi_package):
if blob_hash in found_hashes:
found_hashes.discard(blob_hash)

for zip_path, versions in tested_files.items():
if sdk_version in versions:
tested_files[zip_path] = sdk_version, versions[sdk_version]
else:
# This isn't the version it claims to be. Go with the latest
# Jetpack version we know about that contains it.
version = str(max(map(Version, versions.keys())))
tested_files[zip_path] = version, versions[version]

if (file_hashes[zip_path] not in EMPTY_FILE_SHA256SUMS and
not zip_path.endswith("/")):
err.warning(
err_id=("testcases_jetpack",
"inspect_jetpack",
"mismatched_version"),
warning="Jetpack module version mismatch",
description=["A file in the Jetpack add-on does not match "
"the SDK version specified in harness-options"
".json.",
"Module: %s" % zip_path,
"Versions: %s/%s" % (sdk_version, version)],
filename=zip_path)

# We've got hashes left over
if found_hashes:
err.warning(
Expand Down
Loading

0 comments on commit 6bb168e

Please sign in to comment.