Skip to content

Commit

Permalink
Remove fallback URL resolver. (dart-lang#8454)
Browse files Browse the repository at this point in the history
  • Loading branch information
isoos authored Jan 10, 2025
1 parent 9f6100a commit c4bea03
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 314 deletions.
31 changes: 11 additions & 20 deletions app/lib/package/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:pana/models.dart';
import 'package:pub_dev/service/download_counts/backend.dart';
import 'package:pub_dev/service/download_counts/download_counts.dart';
import 'package:pub_dev/shared/markdown.dart';
import 'package:pub_semver/pub_semver.dart';

import '../package/model_properties.dart';
Expand Down Expand Up @@ -1071,11 +1070,7 @@ class PackageLinks {
/// The link to `CONTRIBUTING.md` in the git repository (when the repository is verified).
final String? contributingUrl;

/// The inferred base URL that can be used to link files from.
final String? _baseUrl;

PackageLinks._(
this._baseUrl, {
PackageLinks._({
this.homepageUrl,
String? documentationUrl,
this.repositoryUrl,
Expand All @@ -1093,12 +1088,7 @@ class PackageLinks {
}) {
repositoryUrl ??= urls.inferRepositoryUrl(homepageUrl);
issueTrackerUrl ??= urls.inferIssueTrackerUrl(repositoryUrl);
final baseUrl = urls.inferBaseUrl(
homepageUrl: homepageUrl,
repositoryUrl: repositoryUrl,
);
return PackageLinks._(
baseUrl,
homepageUrl: homepageUrl,
documentationUrl: documentationUrl,
repositoryUrl: repositoryUrl,
Expand Down Expand Up @@ -1156,12 +1146,7 @@ class PackagePageData {
return inferred;
}

final baseUrl = urls.inferBaseUrl(
homepageUrl: result.homepageUrl ?? inferred.homepageUrl,
repositoryUrl: result.repositoryUrl ?? inferred.repositoryUrl,
);
return PackageLinks._(
baseUrl,
homepageUrl: result.homepageUrl ?? inferred.homepageUrl,
repositoryUrl: result.repositoryUrl ?? inferred.repositoryUrl,
issueTrackerUrl: result.issueTrackerUrl ?? inferred.issueTrackerUrl,
Expand All @@ -1170,10 +1155,16 @@ class PackagePageData {
);
}();

/// The verified repository (or homepage).
late final urlResolverFn =
scoreCard.panaReport?.result?.repository?.resolveUrl ??
fallbackUrlResolverFn(packageLinks._baseUrl);
/// The URL resolver using a verified repository
/// (unless the verification explicitly failed).
late final urlResolverFn = () {
final result = scoreCard.panaReport?.result;
final status = result?.repositoryStatus;
if (status == RepositoryStatus.failed) {
return null;
}
return result?.repository?.resolveUrl;
}();

PackageView toPackageView() {
return _view ??= PackageView.fromModel(
Expand Down
90 changes: 1 addition & 89 deletions app/lib/shared/markdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

import 'package:logging/logging.dart';
import 'package:markdown/markdown.dart' as m;
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
import 'package:sanitize_html/sanitize_html.dart';

import 'urls.dart' show getRepositoryUrl, UriExt;
import 'urls.dart' show UriExt;

/// Resolves [reference] relative to a repository URL.
typedef UrlResolverFn = String Function(
Expand Down Expand Up @@ -313,67 +312,6 @@ class _TaskListRewriteNodeVisitor implements m.NodeVisitor {
void visitText(m.Text text) {}
}

bool _isAbsolute(String url) => url.contains(':');

String _rewriteAbsoluteUrl(String url) {
final uri = Uri.parse(url);
if (uri.host == 'github.com') {
final segments = uri.pathSegments;
if (segments.length > 3 && segments[2] == 'blob') {
final newSegments = List.of(segments);
newSegments[2] = 'raw';
return uri.replace(pathSegments: newSegments).toString();
}
}
return url;
}

String _rewriteRelativeUrl({
required String baseUrl,
required String url,
required String? baseDir,
}) {
final uri = Uri.parse(url);
final linkPath = uri.path;
final linkFragment = uri.fragment;
if (linkPath.isEmpty) {
return url;
}
String newUrl;
if (linkPath.startsWith('/')) {
newUrl = Uri.parse(baseUrl).replace(path: linkPath).toString();
} else {
final adjustedLinkPath = p.normalize(p.join(baseDir ?? '.', linkPath));
final repoUrl = getRepositoryUrl(baseUrl, adjustedLinkPath);
if (repoUrl == null) {
return url;
}
newUrl = repoUrl;
}
if (linkFragment.isNotEmpty) {
newUrl = '$newUrl#$linkFragment';
}
return newUrl;
}

/// Returns null if the [url] looks invalid.
String? _pruneBaseUrl(String? url) {
if (url == null) return null;
try {
final Uri uri = Uri.parse(url);
if (uri.scheme != 'http' && uri.scheme != 'https') {
return null;
}
if (uri.host.isEmpty || !uri.host.contains('.')) {
return null;
}
return uri.toString();
} catch (e) {
// url is user-provided, may be malicious, ignoring errors.
}
return null;
}

/// Group corresponding changelog nodes together, if it matches the following
/// pattern:
/// - version identifiers are the only content in a single line
Expand Down Expand Up @@ -447,29 +385,3 @@ Version? _extractVersion(String? text) {
return null;
}
}

// TODO: remove after repository verification is launched
UrlResolverFn? fallbackUrlResolverFn(String? providedBaseUrl) {
final baseUrl = _pruneBaseUrl(providedBaseUrl);
if (baseUrl == null) {
return null;
}
return (
String url, {
bool? isEmbeddedObject,
String? relativeFrom,
}) {
String newUrl = url;
if (!_isAbsolute(newUrl)) {
newUrl = _rewriteRelativeUrl(
url: newUrl,
baseUrl: baseUrl,
baseDir: relativeFrom == null ? null : p.dirname(relativeFrom),
);
}
if ((isEmbeddedObject ?? false) && _isAbsolute(newUrl)) {
newUrl = _rewriteAbsoluteUrl(newUrl);
}
return newUrl;
};
}
106 changes: 0 additions & 106 deletions app/lib/shared/urls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,6 @@ const httpsApiDartDev = 'https://api.dart.dev/';
/// rejected and the URL mustn't be displayed.
const trustedUrlSchemes = <String>['http', 'https', 'mailto'];

/// Extensions that are considered to be images.
final _imageExtensions = <String>{
'.apng',
'.avif',
'.gif',
'.jpg',
'.jpeg',
'.png',
'.svg',
'.webp',
};

/// Common repository URL replacement patterns.
const _repositoryReplacePrefixes = {
'http://github.com': 'https://github.com',
'https://www.github.com': 'https://github.com',
'https://www.gitlab.com': 'https://gitlab.com',
};

/// Hostnames that are trusted in user-generated content (and don't get rel="ugc").
const trustedTargetHost = [
'api.dart.dev',
Expand Down Expand Up @@ -294,42 +275,6 @@ String? inferIssueTrackerUrl(String? baseUrl) {
return null;
}

/// Infer base URL that can be used to link files from.
String? inferBaseUrl({String? homepageUrl, String? repositoryUrl}) {
var baseUrl = repositoryUrl ?? homepageUrl;
if (baseUrl == null) return null;

// In a few cases people specify only a deep repository URL for their
// package (e.g. a monorepo for multiple packages). While we default the
// base URL to be the repository URL, this check allows us to use the deep
// URL for linking.
if (homepageUrl != null && homepageUrl.startsWith(baseUrl)) {
baseUrl = homepageUrl;
}

// URL cleanup
final prefixReplacements = const <String, String>{
'http://github.com/': 'https://github.com/',
'http://www.github.com/': 'https://github.com/',
'https://www.github.com/': 'https://github.com/',
'http://gitlab.com/': 'https://gitlab.com/',
'http://www.gitlab.com/': 'https://gitlab.com/',
'https://www.gitlab.com/': 'https://gitlab.com/',
};
for (final prefix in prefixReplacements.keys) {
if (baseUrl!.startsWith(prefix)) {
baseUrl = baseUrl.replaceFirst(prefix, prefixReplacements[prefix]!);
}
}
if ((baseUrl!.startsWith('https://github.com/') ||
baseUrl.startsWith('https://gitlab.com/')) &&
baseUrl.endsWith('.git')) {
baseUrl = baseUrl.substring(0, baseUrl.length - 4);
}

return baseUrl;
}

/// Infer the hosting/service provider for a given URL.
String? inferServiceProviderName(String? url) {
if (url == null) {
Expand Down Expand Up @@ -383,57 +328,6 @@ String myPublishersUrl() => '/my-publishers';
String myActivityLogUrl() => '/my-activity-log';
String createPublisherUrl() => '/create-publisher';

/// Returns an URL that is likely the downloadable URL of the given path.
String? getRepositoryUrl(
String? repository,
String relativePath, {
String branch = 'master',
}) {
if (repository == null || repository.isEmpty) return null;
for (final key in _repositoryReplacePrefixes.keys) {
if (repository!.startsWith(key)) {
repository =
repository.replaceFirst(key, _repositoryReplacePrefixes[key]!);
}
}
try {
final uri = Uri.parse(repository!);
final segments = List.of(uri.pathSegments);
while (segments.isNotEmpty && segments.last.isEmpty) {
segments.removeLast();
}

if (repository.startsWith('https://github.com/') ||
repository.startsWith('https://gitlab.com/')) {
if (segments.length >= 2 &&
segments[1].endsWith('.git') &&
segments[1].length > 4) {
segments[1] = segments[1].substring(0, segments[1].length - 4);
}

final extension = p.extension(relativePath).toLowerCase();
final isRaw = _imageExtensions.contains(extension);
final typeSegment = isRaw ? 'raw' : 'blob';

if (segments.length < 2) {
return null;
} else if (segments.length == 2) {
final newUrl = uri.replace(pathSegments: segments).toString();
return p.url.join(newUrl, typeSegment, branch, relativePath);
} else if (segments[2] == 'tree' || segments[2] == 'blob') {
segments[2] = typeSegment;
final newUrl = uri.replace(pathSegments: segments).toString();
return p.url.join(newUrl, relativePath);
} else {
return null;
}
}
} catch (_) {
return null;
}
return null;
}

/// Parses [url] and returns the [Uri] object only if the result Uri is valid
/// (e.g. is relative or has recognized scheme).
Uri? parseValidUrl(String? url) {
Expand Down
Loading

0 comments on commit c4bea03

Please sign in to comment.