Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new_audit: bring back redirects-http with passive https check #13548

Merged
merged 10 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -52,6 +52,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 @@ -116,6 +117,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'],
adamraine marked this conversation as resolved.
Show resolved Hide resolved
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', () => {
adamraine marked this conversation as resolved.
Show resolved Hide resolved
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
Loading