Skip to content

Commit

Permalink
new_audit: bring back redirects-http with passive https check (#13548)
Browse files Browse the repository at this point in the history
  • Loading branch information
brendankenny authored Apr 9, 2024
1 parent 65b6525 commit 9bd33f8
Show file tree
Hide file tree
Showing 15 changed files with 2,404 additions and 412 deletions.
1 change: 1 addition & 0 deletions cli/test/smokehouse/config/exclusions.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const exclusions = {
'redirects-client-paint-server', 'redirects-multiple-server',
'redirects-single-server', 'redirects-single-client',
'redirects-history-push-state', 'redirects-scripts',
'redirects-http',
// Disabled because these tests use settings that cannot be fully configured in
// DevTools (e.g. throttling method "provided").
'metrics-tricky-tti', 'metrics-tricky-tti-late-fcp', 'screenshot',
Expand Down
2 changes: 2 additions & 0 deletions cli/test/smokehouse/core-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import pwaRocks from './test-definitions/pwa-rocks.js';
import pwaSvgomg from './test-definitions/pwa-svgomg.js';
import redirectsClientPaintServer from './test-definitions/redirects-client-paint-server.js';
import redirectsHistoryPushState from './test-definitions/redirects-history-push-state.js';
import redirectsHttp from './test-definitions/redirects-http.js';
import redirectsMultipleServer from './test-definitions/redirects-multiple-server.js';
import redirectsScripts from './test-definitions/redirects-scripts.js';
import redirectsSelf from './test-definitions/redirects-self.js';
Expand Down Expand Up @@ -118,6 +119,7 @@ const smokeTests = [
pwaSvgomg,
redirectsClientPaintServer,
redirectsHistoryPushState,
redirectsHttp,
redirectsMultipleServer,
redirectsScripts,
redirectsSelf,
Expand Down
6 changes: 6 additions & 0 deletions cli/test/smokehouse/test-definitions/offline-online-only.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const config = {
],
onlyAudits: [
'is-on-https',
'redirects-http',
'viewport',
'user-timings',
'critical-request-chains',
Expand Down Expand Up @@ -43,6 +44,11 @@ const expectations = {
'is-on-https': {
score: 1,
},
'redirects-http': {
// localhost, so redirect check is n/a.
score: null,
scoreDisplayMode: 'notApplicable',
},
'geolocation-on-start': {
score: 1,
},
Expand Down
38 changes: 38 additions & 0 deletions cli/test/smokehouse/test-definitions/redirects-http.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @type {Smokehouse.ExpectedRunnerResult}
*/
const expectations = {
artifacts: {
URL: {
requestedUrl: 'http://jakearchibald.github.io/svgomg/',
mainDocumentUrl: 'https://jakearchibald.github.io/svgomg/',
finalDisplayedUrl: 'https://jakearchibald.github.io/svgomg/',
},
},
lhr: {
// Intentionally start out on http to test the redirect.
requestedUrl: 'http://jakearchibald.github.io/svgomg/',
finalDisplayedUrl: 'https://jakearchibald.github.io/svgomg/',
runWarnings: [
'The page may not be loading as expected because your test URL (http://jakearchibald.github.io/svgomg/) was redirected to https://jakearchibald.github.io/svgomg/. Try testing the second URL directly.',
],
audits: {
'redirects-http': {
score: 1,
},
},
},
};

export default {
id: 'redirects-http',
expectations,
};


75 changes: 75 additions & 0 deletions core/audits/redirects-http.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @license Copyright 2024 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/

import {Audit} from './audit.js';
import * as i18n from '../lib/i18n/i18n.js';
import UrlUtils from '../lib/url-utils.js';

const UIStrings = {
/** Title of a Lighthouse audit that provides detail on HTTP to HTTPS redirects. This descriptive title is shown to users when HTTP traffic is redirected to HTTPS. */
title: 'Redirects HTTP traffic to HTTPS',
/** Title of a Lighthouse audit that provides detail on HTTP to HTTPS redirects. This descriptive title is shown to users when HTTP traffic is not redirected to HTTPS. */
failureTitle: 'Does not redirect HTTP traffic to HTTPS',
/** Description of a Lighthouse audit that tells the user why they should direct HTTP traffic to HTTPS. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
description: 'Make sure that you redirect all HTTP ' +
'traffic to HTTPS in order to enable secure web features for all your users. [Learn more](https://developer.chrome.com/docs/lighthouse/pwa/redirects-http/).',
};

const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);

/**
* An audit for checking if a site starting on http redirects to https. The audit
* is marked not applicable if the requestedUrl is already https.
*/
class RedirectsHTTP extends Audit {
/**
* @return {LH.Audit.Meta}
*/
static get meta() {
return {
id: 'redirects-http',
title: str_(UIStrings.title),
failureTitle: str_(UIStrings.failureTitle),
description: str_(UIStrings.description),
requiredArtifacts: ['URL'],
supportedModes: ['navigation'],
};
}

/**
* @param {LH.Artifacts} artifacts
* @return {LH.Audit.Product}
*/
static audit(artifacts) {
if (!artifacts.URL.requestedUrl) {
throw new Error('Missing requestedUrl');
}

const requestedUrl = new URL(artifacts.URL.requestedUrl);
const finalDisplayedUrl = new URL(artifacts.URL.finalDisplayedUrl);

// Not applicable unless starting on http.
const startedInsecure = requestedUrl.protocol === 'http:';

// Relax requirements on localhost.
const isLocalhost = UrlUtils.isLikeLocalhost(finalDisplayedUrl.hostname);

if (!startedInsecure || isLocalhost) {
return {
score: null,
notApplicable: true,
};
}

const endedSecure = finalDisplayedUrl.protocol === 'https:';
return {
score: Number(endedSecure),
};
}
}

export default RedirectsHTTP;
export {UIStrings};
2 changes: 2 additions & 0 deletions core/config/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ const defaultConfig = {
],
audits: [
'is-on-https',
'redirects-http',
'viewport',
'metrics/first-contentful-paint',
'metrics/largest-contentful-paint',
Expand Down Expand Up @@ -574,6 +575,7 @@ const defaultConfig = {
auditRefs: [
// Trust & Safety
{id: 'is-on-https', weight: 5, group: 'best-practices-trust-safety'},
{id: 'redirects-http', weight: 1, group: 'best-practices-trust-safety'},
{id: 'geolocation-on-start', weight: 1, group: 'best-practices-trust-safety'},
{id: 'notification-on-start', weight: 1, group: 'best-practices-trust-safety'},
{id: 'csp-xss', weight: 0, group: 'best-practices-trust-safety'},
Expand Down
63 changes: 63 additions & 0 deletions core/test/audits/redirects-http-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @license Copyright 2024 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/

import {strict as assert} from 'assert';

import RedirectsHTTP from '../../audits/redirects-http.js';

describe('Security: HTTP->HTTPS audit', () => {
it('fails when no redirect detected', () => {
return assert.equal(RedirectsHTTP.audit({
URL: {
requestedUrl: 'http://example.com/',
finalDisplayedUrl: 'http://example.com/',
},
}).score, 0);
});

it('passes when redirect detected', () => {
return assert.equal(RedirectsHTTP.audit({
URL: {
requestedUrl: 'http://paulirish.com/',
finalDisplayedUrl: 'https://paulirish.com/',
},
}).score, 1);
});

it('not applicable on localhost', () => {
const product = RedirectsHTTP.audit({
URL: {
requestedUrl: 'http://localhost:8080/page.html',
finalDisplayedUrl: 'https://localhost:8080/page.html',
},
});

assert.equal(product.score, null);
assert.equal(product.notApplicable, true);
});

it('not applicable if requestedUrl is secure', () => {
const product = RedirectsHTTP.audit({
URL: {
requestedUrl: 'https://example.com/',
finalDisplayedUrl: 'https://example.com/',
},
});

assert.equal(product.score, null);
assert.equal(product.notApplicable, true);
});

it('throws if requestedUrl is missing', () => {
assert.throws(() => {
RedirectsHTTP.audit({
URL: {
finalDisplayedUrl: 'https://example.com/',
},
});
}, new Error('Missing requestedUrl'));
});
});
Loading

0 comments on commit 9bd33f8

Please sign in to comment.