Let mobile aliases redirect to their canonical site

A search result on m., mobile. or amp. may redirect to its exact parent domain (m.example.com to example.com), a common canonical redirect. Other parent climbs stay refused, so shared hosting parents like github.io remain out of reach.
This commit is contained in:
cogwheel 2026-09-29 14:05:21 +05:30
parent 48219b385e
commit 0131c4fe4b
2 changed files with 30 additions and 3 deletions

View file

@ -284,8 +284,12 @@ final class OnDeviceWebToolSession {
/// Whether a redirect from [origin] stays on its site: the same host, its
/// `www` twin, or one of its subdomains (`http→https` and path changes are
/// fine). Never a parent domain: `victim.github.io` must not reach
/// `github.io`, whose other subdomains belong to other people.
/// fine).
///
/// A parent domain is off limits (`victim.github.io` must not reach
/// `github.io`, whose other subdomains belong to other people), except the
/// canonical redirect from a mobile or AMP alias (`m.example.com` to
/// `example.com`), and then only to that exact parent.
bool isSameSiteRedirect(Uri origin, Uri to) {
String bare(Uri uri) {
final host = uri.host.toLowerCase();
@ -294,9 +298,15 @@ bool isSameSiteRedirect(Uri origin, Uri to) {
final site = bare(origin);
final target = bare(to);
return target == site || target.endsWith('.$site');
if (target == site || target.endsWith('.$site')) return true;
final dot = site.indexOf('.');
return dot > 0 &&
_canonicalAliasLabels.contains(site.substring(0, dot)) &&
target == site.substring(dot + 1);
}
const Set<String> _canonicalAliasLabels = {'m', 'mobile', 'amp'};
/// `http(s)` links in [text], e.g. the user's message, for the fetch
/// allow-list. Trailing punctuation is dropped, except a closing bracket
/// that the URL itself opened (`…/wiki/Dart_(programming_language)`).

View file

@ -199,6 +199,23 @@ void main() {
);
});
test('a mobile alias may redirect to its canonical parent only', () {
final mobile = Uri.parse('https://m.example.com/article');
expect(
isSameSiteRedirect(mobile, Uri.parse('https://example.com/article')),
isTrue,
);
expect(
isSameSiteRedirect(mobile, Uri.parse('https://www.example.com/a')),
isTrue,
);
// Only the exact parent, never its other subdomains.
expect(
isSameSiteRedirect(mobile, Uri.parse('https://other.example.com/')),
isFalse,
);
});
test('links the user wrote are read as written', () {
expect(
extractWebUrls(