From 4eca1c3c27151dfa4a9e0767bf47f4d11d2e3a89 Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Tue, 10 Apr 2018 15:16:30 +0200 Subject: [PATCH] Minimal hostname validation on package uploads. --- app/lib/frontend/backend.dart | 2 ++ app/lib/shared/urls.dart | 31 +++++++++++++++++++++++++++++++ app/test/shared/urls_test.dart | 27 +++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/app/lib/frontend/backend.dart b/app/lib/frontend/backend.dart index 0a61ec8f64..d4f8de56ec 100644 --- a/app/lib/frontend/backend.dart +++ b/app/lib/frontend/backend.dart @@ -16,6 +16,7 @@ import 'package:uuid/uuid.dart'; import '../shared/name_tracker.dart'; import '../shared/package_memcache.dart'; +import '../shared/urls.dart' as urls; import '../shared/utils.dart'; import 'model_properties.dart'; @@ -675,6 +676,7 @@ Future parseAndValidateUpload( if (!nameTracker.accept(pubspec.name)) { throw new Exception('Package name is too similar to another package.'); } + urls.syntaxCheckHomepageUrl(pubspec.homepage); String exampleFilename; for (String candidate in exampleFileCandidates(pubspec.name)) { diff --git a/app/lib/shared/urls.dart b/app/lib/shared/urls.dart index 848be4f33a..c0a9576b2c 100644 --- a/app/lib/shared/urls.dart +++ b/app/lib/shared/urls.dart @@ -58,6 +58,37 @@ String analysisTabUrl(String package) { : pkgPageUrl(package, fragment: fragment); } +final _invalidHostNames = const [ + '..', + '...', + 'example.com', + 'example.org', + 'example.net', + 'www.example.com', + 'www.example.org', + 'www.example.net', + 'none', +]; + +void syntaxCheckHomepageUrl(String url) { + Uri uri; + try { + uri = Uri.parse(url); + } catch (_) { + throw new Exception('Unable to parse homepage URL: $url'); + } + if (!uri.hasScheme || !uri.scheme.startsWith('http')) { + throw new Exception( + 'Use http:// or https:// URL schemes for homepage URL: $url'); + } + if (uri.host == null || + uri.host.isEmpty || + !uri.host.contains('.') || + _invalidHostNames.contains(uri.host)) { + throw new Exception('Homepage URL has no valid host: $url'); + } +} + // TODO: lib/shared/analyzer_client.dart // TODO: lib/shared/configuration.dart diff --git a/app/test/shared/urls_test.dart b/app/test/shared/urls_test.dart index b12a17921c..4682a0346f 100644 --- a/app/test/shared/urls_test.dart +++ b/app/test/shared/urls_test.dart @@ -36,4 +36,31 @@ void main() { 'https://pub.dartlang.org/documentation/foo_bar/1.0.0/'); }); }); + + group('homepage syntax check', () { + test('no url is not accepted', () { + expect(() => syntaxCheckHomepageUrl(null), throwsException); + }); + + test('example urls that are accepted', () { + syntaxCheckHomepageUrl('http://github.com/user/repo/'); + syntaxCheckHomepageUrl('https://github.com/user/repo/'); + syntaxCheckHomepageUrl('http://some.domain.com'); + }); + + test('urls without valid scheme are not accepted', () { + expect(() => syntaxCheckHomepageUrl('github.com/x/y'), throwsException); + expect(() => syntaxCheckHomepageUrl('ftp://github.com/x/y'), + throwsException); + }); + + test('urls without valid host are not accepted', () { + expect(() => syntaxCheckHomepageUrl('http://none/x/'), throwsException); + expect(() => syntaxCheckHomepageUrl('http://example.com/x/'), + throwsException); + expect( + () => syntaxCheckHomepageUrl('http://localhost/x/'), throwsException); + expect(() => syntaxCheckHomepageUrl('http://.../x/'), throwsException); + }); + }); }