/// <summary> /// Searches the package manager metadata to figure out the source code repository. /// </summary> /// <param name="purl">The <see cref="PackageURL"/> that we need to find the source code repository.</param> /// <param name="metadata">The json representation of this package's metadata.</param> /// <remarks>If no version specified, defaults to latest version.</remarks> /// <returns> /// A dictionary, mapping each possible repo source entry to its probability/empty dictionary /// </returns> protected override async Task <Dictionary <PackageURL, double> > SearchRepoUrlsInPackageMetadata(PackageURL purl, string metadata) { Dictionary <PackageURL, double> mapping = new(); try { string?version = purl.Version; if (string.IsNullOrEmpty(version)) { version = (await EnumerateVersionsAsync(purl)).First(); } NuspecReader? nuspecReader = GetNuspec(purl.Name, version); RepositoryMetadata?repositoryMetadata = nuspecReader?.GetRepositoryMetadata(); if (repositoryMetadata != null && GitHubProjectManager.IsGitHubRepoUrl(repositoryMetadata.Url, out PackageURL? githubPurl)) { if (githubPurl != null) { mapping.Add(githubPurl, 1.0F); } } return(mapping); } catch (Exception ex) { Logger.Debug(ex, $"Error fetching/parsing NuGet repository metadata: {ex.Message}"); } // If nothing worked, return the default empty dictionary return(mapping); }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously protected async Task <Dictionary <PackageURL, double> > SearchRepoUrlsInPackageMetadata(PackageURL purl, #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously JsonDocument contentJSON) { Dictionary <PackageURL, double>?mapping = new(); if (purl.Name is string purlName && (purlName.StartsWith('_') || npm_internal_modules.Contains(purlName))) { // url = 'https://github.com/nodejs/node/tree/master/lib' + package.name, mapping.Add(new PackageURL(purl.Type, purl.Namespace, purl.Name, null, null, "node/tree/master/lib"), 1.0F); return(mapping); } // if a version is provided, search that JSONElement, otherwise, just search the latest // version, which is more likely best maintained // TODO: If the latest version JSONElement doesnt have the repo infor, should we search all elements // on that chance that one of them might have it? JsonElement?versionJSON = string.IsNullOrEmpty(purl?.Version) ? GetLatestVersionElement(contentJSON) : GetVersionElement(contentJSON, new Version(purl.Version)); if (versionJSON is JsonElement notNullVersionJSON) { try { if (!notNullVersionJSON.TryGetProperty("repository", out JsonElement repository)) { return(mapping); } if (repository.ValueKind == JsonValueKind.Object) { string?repoType = OssUtilities.GetJSONPropertyStringIfExists(repository, "type")?.ToLower(); string?repoURL = OssUtilities.GetJSONPropertyStringIfExists(repository, "url"); // right now we deal with only github repos if (repoType == "git" && repoURL is not null) { PackageURL gitPURL = GitHubProjectManager.ParseUri(new Uri(repoURL)); // we got a repository value the author specified in the metadata - so no // further processing needed mapping.Add(gitPURL, 1.0F); return(mapping); } } } catch (KeyNotFoundException) { /* continue onwards */ } catch (UriFormatException) { /* the uri specified in the metadata invalid */ } } return(mapping); }
/// <summary> /// Updates the <see cref="Repository"/> for this package version in the <see cref="PackageMetadata"/>. /// </summary> /// <param name="metadata">The <see cref="PackageMetadata"/> object to update with the values for this version.</param> private async Task UpdateMetadataRepository(PackageMetadata metadata) { NuspecReader? nuspecReader = GetNuspec(metadata.Name !, metadata.PackageVersion !); RepositoryMetadata?repositoryMetadata = nuspecReader?.GetRepositoryMetadata(); if (repositoryMetadata != null && GitHubProjectManager.IsGitHubRepoUrl(repositoryMetadata.Url, out PackageURL? githubPurl)) { Repository ghRepository = new() { Type = "github" }; await ghRepository.ExtractRepositoryMetadata(githubPurl !); metadata.Repository ??= new List <Repository>(); metadata.Repository.Add(ghRepository); } }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously protected override async Task <Dictionary <PackageURL, double> > SearchRepoUrlsInPackageMetadata(PackageURL purl, string metadata) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { Dictionary <PackageURL, double> mapping = new(); if (purl.Name?.StartsWith('_') ?? false) // TODO: there are internal modules which do not start with _ { // TODO: internal modules could also be in https://github.com/python/cpython/tree/master/Modules/ mapping.Add(new PackageURL(purl.Type, purl.Namespace, purl.Name, null, null, "cpython/tree/master/Lib/"), 1.0F); return(mapping); } if (string.IsNullOrEmpty(metadata)) { return(mapping); } JsonDocument contentJSON = JsonDocument.Parse(metadata); List <string> possibleProperties = new() { "homepage", "home_page" }; JsonElement infoJSON; try { infoJSON = contentJSON.RootElement.GetProperty("info"); } catch (Exception) { return(mapping); } foreach (JsonProperty property in infoJSON.EnumerateObject()) { // there are a couple of possibilities where the repository url might be present - check all of them try { if (possibleProperties.Contains(property.Name.ToLower())) { string homepage = property.Value.ToString() ?? string.Empty; IEnumerable <PackageURL>?packageUrls = GitHubProjectManager.ExtractGitHubPackageURLs(homepage); // if we were able to extract a github url, return if (packageUrls != null && packageUrls.Any()) { mapping.Add(packageUrls.First(), 1.0F); return(mapping); } } else if (property.Name.Equals("project_urls")) { if (property.Value.TryGetProperty("Source", out JsonElement jsonElement)) { string?sourceLoc = jsonElement.GetString(); if (sourceLoc != null) { IEnumerable <PackageURL>?packageUrls = GitHubProjectManager.ExtractGitHubPackageURLs(sourceLoc); if (packageUrls != null && packageUrls.Any()) { mapping.Add(packageUrls.First(), 1.0F); return(mapping); } } } } } catch (Exception) { continue; /* try the next property */ } } return(mapping); }