blob: ac8c1965d4dd359aaca1262c437e68cd33a57636 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Include test fixture.
GEN_INCLUDE(['net_internals_test.js']);
// Anonymous namespace
(function() {
/*
* A valid hash that can be set for a domain.
* @type {string}
*/
var VALID_HASH = 'sha1/Guzek9lMwR3KeIS8wwS9gBvVtIg=';
/*
* An invalid hash that can't be set for a domain.
* @type {string}
*/
var INVALID_HASH = 'invalid';
/*
* Possible results of an HSTS query.
* @enum {number}
*/
var QueryResultType = {
SUCCESS: 0,
NOT_FOUND: 1,
ERROR: 2
};
/**
* A Task that waits for the results of an HSTS query. Once the results are
* received, checks them before completing. Does not initiate the query.
* @param {string} domain The domain expected in the returned results.
* @param {bool} stsSubdomains Whether or not the stsSubdomains flag is expected
* to be set in the returned results. Ignored on error and not found
* results.
* @param {bool} pkpSubdomains Whether or not the pkpSubdomains flag is expected
* to be set in the returned results. Ignored on error and not found
* results.
* @param {number} stsObserved The time the STS policy was observed.
* @param {number} pkpObserved The time the PKP policy was observed.
* @param {string} publicKeyHashes Expected public key hashes. Ignored on error
* error and not found results.
* @param {QueryResultType} queryResultType The expected result type of the
* results of the query.
* @extends {NetInternalsTest.Task}
*/
function CheckQueryResultTask(domain, stsSubdomains, pkpSubdomains,
stsObserved, pkpObserved, publicKeyHashes,
queryResultType) {
this.domain_ = domain;
this.stsSubdomains_ = stsSubdomains;
this.pkpSubdomains_ = pkpSubdomains;
this.stsObserved_ = stsObserved;
this.pkpObserved_ = pkpObserved;
this.publicKeyHashes_ = publicKeyHashes;
this.queryResultType_ = queryResultType;
NetInternalsTest.Task.call(this);
}
CheckQueryResultTask.prototype = {
__proto__: NetInternalsTest.Task.prototype,
/**
* Starts watching for the query results.
*/
start: function() {
g_browser.addHSTSObserver(this);
},
/**
* Callback from the BrowserBridge. Validates |result| and completes the
* task.
* @param {object} result Results from the query.
*/
onHSTSQueryResult: function(result) {
// Ignore results after |this| is finished.
if (!this.isDone()) {
expectEquals(this.domain_, $(HSTSView.QUERY_INPUT_ID).value);
// Each case has its own validation function because of the design of the
// test reporting infrastructure.
if (result.error != undefined) {
this.checkError_(result);
} else if (!result.result) {
this.checkNotFound_(result);
} else {
this.checkSuccess_(result);
}
this.running_ = false;
// Start the next task asynchronously, so it can add another HSTS observer
// without getting the current result.
window.setTimeout(this.onTaskDone.bind(this), 1);
}
},
/**
* On errors, checks the result.
* @param {object} result Results from the query.
*/
checkError_: function(result) {
expectEquals(QueryResultType.ERROR, this.queryResultType_);
expectEquals(result.error, $(HSTSView.QUERY_OUTPUT_DIV_ID).innerText);
},
/**
* Checks the result when the entry was not found.
* @param {object} result Results from the query.
*/
checkNotFound_: function(result) {
expectEquals(QueryResultType.NOT_FOUND, this.queryResultType_);
expectEquals('Not found', $(HSTSView.QUERY_OUTPUT_DIV_ID).innerText);
},
/**
* Checks successful results.
* @param {object} result Results from the query.
*/
checkSuccess_: function(result) {
expectEquals(QueryResultType.SUCCESS, this.queryResultType_);
expectEquals(this.stsSubdomains_, result.dynamic_sts_include_subdomains);
expectEquals(this.pkpSubdomains_, result.dynamic_pkp_include_subdomains);
// Disabled because of http://crbug.com/397639
// expectLE(this.stsObserved_, result.dynamic_sts_observed);
// expectLE(this.pkpObserved_, result.dynamic_pkp_observed);
// |public_key_hashes| is an old synonym for what is now
// |preloaded_spki_hashes|, which in turn is a legacy synonym for
// |static_spki_hashes|. Look for all three, and also for
// |dynamic_spki_hashes|.
if (typeof result.public_key_hashes === 'undefined')
result.public_key_hashes = '';
if (typeof result.preloaded_spki_hashes === 'undefined')
result.preloaded_spki_hashes = '';
if (typeof result.static_spki_hashes === 'undefined')
result.static_spki_hashes = '';
if (typeof result.dynamic_spki_hashes === 'undefined')
result.dynamic_spki_hashes = '';
var hashes = [];
if (result.public_key_hashes)
hashes.push(result.public_key_hashes);
if (result.preloaded_spki_hashes)
hashes.push(result.preloaded_spki_hashes);
if (result.static_spki_hashes)
hashes.push(result.static_spki_hashes);
if (result.dynamic_spki_hashes)
hashes.push(result.dynamic_spki_hashes);
expectEquals(this.publicKeyHashes_, hashes.join(','));
// Verify that the domain appears somewhere in the displayed text.
outputText = $(HSTSView.QUERY_OUTPUT_DIV_ID).innerText;
expectLE(0, outputText.search(this.domain_));
}
};
/**
* A Task to try and add an HSTS domain via the HTML form. The task will wait
* until the results from the automatically sent query have been received, and
* then checks them against the expected values.
* @param {string} domain The domain to send and expected to be returned.
* @param {bool} stsSubdomains Whether the HSTS subdomain checkbox should be
* selected. Also the corresponding expected return value, in the success
* case.
* @param {bool} pkpSubdomains Whether the pinning subdomain checkbox should be
* selected. Also the corresponding expected return value, in the success
* case. When publicKeyHashes is INVALID_HASH, the expected return value
* is false.
* @param {number} stsObserved The time the STS policy was observed.
* @param {number} pkpObserved The time the PKP policy was observed.
* @param {string} publicKeyHashes Public key hash to send. Also the
* corresponding expected return value, on success. When this is the string
* INVALID_HASH, an empty string is expected to be received instead.
* @param {QueryResultType} queryResultType Expected result type.
* @extends {CheckQueryResultTask}
*/
function AddTask(domain, stsSubdomains, pkpSubdomains, publicKeyHashes,
stsObserved, pkpObserved, queryResultType) {
this.requestedPublicKeyHashes_ = publicKeyHashes;
this.requestedPkpSubdomains_ = pkpSubdomains;
if (publicKeyHashes == INVALID_HASH) {
pkpSubdomains = false;
publicKeyHashes = '';
}
CheckQueryResultTask.call(this, domain, stsSubdomains, pkpSubdomains,
stsObserved, pkpObserved, publicKeyHashes,
queryResultType);
}
AddTask.prototype = {
__proto__: CheckQueryResultTask.prototype,
/**
* Fills out the add form, simulates a click to submit it, and starts
* listening for the results of the query that is automatically submitted.
*/
start: function() {
$(HSTSView.ADD_INPUT_ID).value = this.domain_;
$(HSTSView.ADD_STS_CHECK_ID).checked = this.stsSubdomains_;
$(HSTSView.ADD_PKP_CHECK_ID).checked = this.requestedPkpSubdomains_;
$(HSTSView.ADD_PINS_ID).value = this.requestedPublicKeyHashes_;
$(HSTSView.ADD_SUBMIT_ID).click();
CheckQueryResultTask.prototype.start.call(this);
}
};
/**
* A Task to query a domain and wait for the results. Parameters mirror those
* of CheckQueryResultTask, except |domain| is also the name of the domain to
* query.
* @extends {CheckQueryResultTask}
*/
function QueryTask(domain, stsSubdomains, pkpSubdomains, stsObserved,
pkpObserved, publicKeyHashes, queryResultType) {
CheckQueryResultTask.call(this, domain, stsSubdomains, pkpSubdomains,
stsObserved, pkpObserved, publicKeyHashes,
queryResultType);
}
QueryTask.prototype = {
__proto__: CheckQueryResultTask.prototype,
/**
* Fills out the query form, simulates a click to submit it, and starts
* listening for the results.
*/
start: function() {
CheckQueryResultTask.prototype.start.call(this);
$(HSTSView.QUERY_INPUT_ID).value = this.domain_;
$(HSTSView.QUERY_SUBMIT_ID).click();
}
};
/**
* Task that deletes a single domain, then queries the deleted domain to make
* sure it's gone.
* @param {string} domain The domain to delete.
* @param {QueryResultType} queryResultType The result of the query. Can be
* QueryResultType.ERROR or QueryResultType.NOT_FOUND.
* @extends {QueryTask}
*/
function DeleteTask(domain, queryResultType) {
expectNotEquals(queryResultType, QueryResultType.SUCCESS);
this.domain_ = domain;
QueryTask.call(this, domain, false, false, '', 0, 0, queryResultType);
}
DeleteTask.prototype = {
__proto__: QueryTask.prototype,
/**
* Fills out the delete form and simulates a click to submit it. Then sends
* a query.
*/
start: function() {
$(HSTSView.DELETE_INPUT_ID).value = this.domain_;
$(HSTSView.DELETE_SUBMIT_ID).click();
QueryTask.prototype.start.call(this);
}
};
/**
* Checks that querying a domain that was never added fails.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewQueryNotFound', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
var now = new Date().getTime() / 1000.0;
taskQueue.addTask(new QueryTask('somewhere.com', false, false, now, now, '',
QueryResultType.NOT_FOUND));
taskQueue.run();
});
/**
* Checks that querying a domain with an invalid name returns an error.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewQueryError', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
var now = new Date().getTime() / 1000.0;
taskQueue.addTask(new QueryTask('\u3024', false, false, now, now, '',
QueryResultType.ERROR));
taskQueue.run();
});
/**
* Deletes a domain that was never added.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewDeleteNotFound', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
taskQueue.addTask(new DeleteTask('somewhere.com', QueryResultType.NOT_FOUND));
taskQueue.run();
});
/**
* Deletes a domain that returns an error on lookup.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewDeleteError', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
taskQueue.addTask(new DeleteTask('\u3024', QueryResultType.ERROR));
taskQueue.run();
});
/**
* Adds a domain and then deletes it.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewAddDelete', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
var now = new Date().getTime() / 1000.0;
taskQueue.addTask(new AddTask('somewhere.com', false, false, VALID_HASH,
now, now, QueryResultType.SUCCESS));
taskQueue.addTask(new DeleteTask('somewhere.com', QueryResultType.NOT_FOUND));
taskQueue.run();
});
/**
* Tries to add a domain with an invalid name.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewAddFail', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
var now = new Date().getTime() / 1000.0;
taskQueue.addTask(new AddTask('0123456789012345678901234567890' +
'012345678901234567890123456789012345',
false, false, '', now, now,
QueryResultType.NOT_FOUND));
taskQueue.run();
});
/**
* Tries to add a domain with a name that errors out on lookup due to having
* non-ASCII characters in it.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewAddError', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
var now = new Date().getTime() / 1000.0;
taskQueue.addTask(new AddTask('\u3024', false, false, '', now, now,
QueryResultType.ERROR));
taskQueue.run();
});
/**
* Adds a domain with an invalid hash.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewAddInvalidHash', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
var now = new Date().getTime() / 1000.0;
taskQueue.addTask(new AddTask('somewhere.com', true, true, INVALID_HASH,
now, now, QueryResultType.SUCCESS));
taskQueue.addTask(new DeleteTask('somewhere.com', QueryResultType.NOT_FOUND));
taskQueue.run();
});
/**
* Adds the same domain twice in a row, modifying some values the second time.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewAddOverwrite', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
var now = new Date().getTime() / 1000.0;
taskQueue.addTask(new AddTask('somewhere.com', true, true, VALID_HASH,
now, now, QueryResultType.SUCCESS));
taskQueue.addTask(new AddTask('somewhere.com', false, false, '',
now, now, QueryResultType.SUCCESS));
taskQueue.addTask(new DeleteTask('somewhere.com', QueryResultType.NOT_FOUND));
taskQueue.run();
});
/**
* Adds two different domains and then deletes them.
*/
TEST_F('NetInternalsTest', 'netInternalsHSTSViewAddTwice', function() {
NetInternalsTest.switchToView('hsts');
taskQueue = new NetInternalsTest.TaskQueue(true);
var now = new Date().getTime() / 1000.0;
taskQueue.addTask(new AddTask('somewhere.com', false, false, VALID_HASH,
now, now, QueryResultType.SUCCESS));
taskQueue.addTask(new QueryTask('somewhereelse.com', false, false, now, now,
'', QueryResultType.NOT_FOUND));
taskQueue.addTask(new AddTask('somewhereelse.com', true, false, '',
now, now, QueryResultType.SUCCESS));
taskQueue.addTask(new QueryTask('somewhere.com', false, false, now, now,
VALID_HASH, QueryResultType.SUCCESS));
taskQueue.addTask(new DeleteTask('somewhere.com', QueryResultType.NOT_FOUND));
taskQueue.addTask(new QueryTask('somewhereelse.com', true, false, now, now,
'', QueryResultType.SUCCESS));
taskQueue.addTask(new DeleteTask('somewhereelse.com',
QueryResultType.NOT_FOUND));
taskQueue.run(true);
});
})(); // Anonymous namespace