/// <summary>
        /// Analyzes a single Solution file for NuGet package references in referenced project files
        /// </summary>
        /// <param name="solutionFilePath"></param>
        /// <returns></returns>
        public async Task <HashSet <NugetPackage> > GetSolutionNugetPackages(string solutionFilePath)
        {
            if (!_fileSystem.File.Exists(solutionFilePath))
            {
                Console.Error.WriteLine($"Solution file \"{solutionFilePath}\" does not exist");
                return(new HashSet <NugetPackage>());
            }

            Console.WriteLine();
            Console.WriteLine($"» Solution: {solutionFilePath}");
            Console.WriteLine("  Getting projects");

            var packages = new HashSet <NugetPackage>();

            var projectPaths = await GetSolutionProjectReferencesAsync(solutionFilePath).ConfigureAwait(false);

            if (projectPaths.Count == 0)
            {
                Console.Error.WriteLine("  No projects found");
            }
            else
            {
                Console.WriteLine($"  {projectPaths.Count} project(s) found");
            }

            foreach (var projectFilePath in projectPaths)
            {
                Console.WriteLine();
                var projectPackages = await _projectFileService.GetProjectNugetPackagesAsync(projectFilePath).ConfigureAwait(false);

                packages.UnionWith(projectPackages);
            }

            return(packages);
        }
        /// <summary>
        /// Analyzes a single Solution file for NuGet package references in referenced project files
        /// </summary>
        /// <param name="solutionFilePath"></param>
        /// <returns></returns>
        public async Task <HashSet <NugetPackage> > GetSolutionNugetPackages(string solutionFilePath, string baseIntermediateOutputPath, bool excludeTestProjects)
        {
            if (!_fileSystem.File.Exists(solutionFilePath))
            {
                Console.Error.WriteLine($"Solution file \"{solutionFilePath}\" does not exist");
                return(new HashSet <NugetPackage>());
            }

            Console.WriteLine();
            Console.WriteLine($"» Solution: {solutionFilePath}");
            Console.WriteLine("  Getting projects");

            var packages = new HashSet <NugetPackage>();

            var projectPaths = await GetSolutionProjectReferencesAsync(solutionFilePath).ConfigureAwait(false);

            if (projectPaths.Count == 0)
            {
                Console.Error.WriteLine("  No projects found");
            }
            else
            {
                Console.WriteLine($"  {projectPaths.Count} project(s) found");
            }

            // Process first all productive projects, then test projects (scope order)
            var projectQuery = from p in projectPaths orderby ProjectFileService.IsTestProject(p) select p;

            var directReferencePackages = new HashSet <NugetPackage>();

            foreach (var projectFilePath in projectQuery)
            {
                Console.WriteLine();
                var projectPackages = await _projectFileService.GetProjectNugetPackagesAsync(projectFilePath, baseIntermediateOutputPath, excludeTestProjects).ConfigureAwait(false);

                directReferencePackages.UnionWith(projectPackages.Where(p => p.IsDirectReference));
                packages.UnionWith(projectPackages);
            }

            // Ensure packages which were discovered later are reflected as direct references in the final list.
            foreach (var directPackage in directReferencePackages)
            {
                packages.TryGetValue(directPackage, out var package);
                package.IsDirectReference = true;
            }

            return(packages);
        }
Beispiel #3
0
        async Task <int> OnExecuteAsync()
        {
            if (version)
            {
                Console.WriteLine(Assembly.GetExecutingAssembly().GetName().Version?.ToString());
                return(0);
            }

            Console.WriteLine();

            // check parameter values
            if (string.IsNullOrEmpty(SolutionOrProjectFile))
            {
                Console.Error.WriteLine($"A path is required");
                return((int)ExitCode.SolutionOrProjectFileParameterMissing);
            }

            if (string.IsNullOrEmpty(outputDirectory))
            {
                Console.Error.WriteLine($"The output directory is required");
                return((int)ExitCode.OutputDirectoryParameterMissing);
            }

            if ((string.IsNullOrEmpty(githubUsername) ^ string.IsNullOrEmpty(githubToken)) ||
                (string.IsNullOrEmpty(githubUsernameDeprecated) ^ string.IsNullOrEmpty(githubTokenDeprecated)))
            {
                Console.Error.WriteLine($"Both GitHub username and token are required");
                return((int)ExitCode.GitHubParameterMissing);
            }

            dotnetCommandService.TimeoutMilliseconds = dotnetCommandTimeout;
            projectFileService.DisablePackageRestore = disablePackageRestore;

            // retrieve nuget package cache paths
            var packageCachePathsResult = dotnetUtilsService.GetPackageCachePaths();

            if (!packageCachePathsResult.Success)
            {
                Console.Error.WriteLine("Unable to find local package cache locations...");
                Console.Error.WriteLine(packageCachePathsResult.ErrorMessage);
                return((int)ExitCode.LocalPackageCacheError);
            }

            Console.WriteLine("Found the following local nuget package cache locations:");
            foreach (var path in packageCachePathsResult.Result)
            {
                Console.WriteLine($"    {path}");
            }

            // instantiate services

            var           fileDiscoveryService = new FileDiscoveryService(Program.fileSystem);
            GithubService githubService        = null;

            if (!(disableGithubLicenses || disableGithubLicensesDeprecated))
            {
                // GitHubService requires its own HttpClient as it adds a default authorization header
                if (!string.IsNullOrEmpty(githubBearerToken))
                {
                    githubService = new GithubService(new HttpClient(), githubBearerToken);
                }
                else if (!string.IsNullOrEmpty(githubBearerTokenDeprecated))
                {
                    githubService = new GithubService(new HttpClient(), githubBearerTokenDeprecated);
                }
                else if (!string.IsNullOrEmpty(githubUsername))
                {
                    githubService = new GithubService(new HttpClient(), githubUsername, githubToken);
                }
                else if (!string.IsNullOrEmpty(githubUsernameDeprecated))
                {
                    githubService = new GithubService(new HttpClient(), githubUsernameDeprecated, githubTokenDeprecated);
                }
                else
                {
                    githubService = new GithubService(new HttpClient());
                }
            }
            var nugetService = new NugetService(
                Program.fileSystem,
                packageCachePathsResult.Result,
                githubService,
                Program.httpClient,
                baseUrl,
                disableHashComputation);

            var packages = new HashSet <NugetPackage>();

            // determine what we are analyzing and do the analysis
            var fullSolutionOrProjectFilePath = Program.fileSystem.Path.GetFullPath(SolutionOrProjectFile);

            var topLevelComponent = new Component
            {
                // name is set below
                Version = string.IsNullOrEmpty(setVersion) ? "0.0.0" : setVersion,
                Type    = setType == Component.Classification.Null ? Component.Classification.Application : setType,
            };

            try
            {
                if (SolutionOrProjectFile.ToLowerInvariant().EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
                {
                    packages = await solutionFileService.GetSolutionNugetPackages(fullSolutionOrProjectFilePath, baseIntermediateOutputPath, excludetestprojects).ConfigureAwait(false);

                    topLevelComponent.Name = fileSystem.Path.GetFileNameWithoutExtension(SolutionOrProjectFile);
                }
                else if (Utils.IsSupportedProjectType(SolutionOrProjectFile) && scanProjectReferences)
                {
                    packages = await projectFileService.RecursivelyGetProjectNugetPackagesAsync(fullSolutionOrProjectFilePath, baseIntermediateOutputPath, excludetestprojects).ConfigureAwait(false);

                    topLevelComponent.Name = fileSystem.Path.GetFileNameWithoutExtension(SolutionOrProjectFile);
                }
                else if (Utils.IsSupportedProjectType(SolutionOrProjectFile))
                {
                    packages = await projectFileService.GetProjectNugetPackagesAsync(fullSolutionOrProjectFilePath, baseIntermediateOutputPath, excludetestprojects).ConfigureAwait(false);

                    topLevelComponent.Name = fileSystem.Path.GetFileNameWithoutExtension(SolutionOrProjectFile);
                }
                else if (Program.fileSystem.Path.GetFileName(SolutionOrProjectFile).ToLowerInvariant().Equals("packages.config", StringComparison.OrdinalIgnoreCase))
                {
                    packages = await packagesFileService.GetNugetPackagesAsync(fullSolutionOrProjectFilePath).ConfigureAwait(false);

                    topLevelComponent.Name = fileSystem.Path.GetDirectoryName(fullSolutionOrProjectFilePath);
                }
                else if (fileSystem.Directory.Exists(fullSolutionOrProjectFilePath))
                {
                    packages = await packagesFileService.RecursivelyGetNugetPackagesAsync(fullSolutionOrProjectFilePath).ConfigureAwait(false);

                    topLevelComponent.Name = fileSystem.Path.GetDirectoryName(fullSolutionOrProjectFilePath);
                }
                else
                {
                    Console.Error.WriteLine($"Only .sln, .csproj, .vbproj, and packages.config files are supported");
                    return((int)ExitCode.InvalidOptions);
                }
            }
            catch (DotnetRestoreException)
            {
                return((int)ExitCode.DotnetRestoreFailed);
            }

            if (!string.IsNullOrEmpty(setName))
            {
                topLevelComponent.Name = setName;
            }

            // get all the components and depdency graph from the NuGet packages
            var components         = new HashSet <Component>();
            var dependencies       = new List <Dependency>();
            var directDependencies = new Dependency {
                Dependencies = new List <Dependency>()
            };
            var transitiveDepencies = new HashSet <string>();

            try
            {
                var bomRefLookup = new Dictionary <(string, string), string>();
                foreach (var package in packages)
                {
                    var component = await nugetService.GetComponentAsync(package).ConfigureAwait(false);

                    if (component != null &&
                        (component.Scope != Component.ComponentScope.Excluded || !excludeDev)
                        )
                    {
                        components.Add(component);
                    }
                    bomRefLookup[(component.Name.ToLower(CultureInfo.InvariantCulture), (component.Version.ToLower(CultureInfo.InvariantCulture)))] = component.BomRef;
        async Task <int> OnExecuteAsync()
        {
            Console.WriteLine();

            // check parameter values
            if (string.IsNullOrEmpty(SolutionOrProjectFile))
            {
                Console.Error.WriteLine($"A path is required");
                return((int)ExitCode.SolutionOrProjectFileParameterMissing);
            }

            if (string.IsNullOrEmpty(outputDirectory))
            {
                Console.Error.WriteLine($"The output directory is required");
                return((int)ExitCode.OutputDirectoryParameterMissing);
            }

            if ((string.IsNullOrEmpty(githubUsername) ^ string.IsNullOrEmpty(githubToken)) ||
                (string.IsNullOrEmpty(githubUsernameDeprecated) ^ string.IsNullOrEmpty(githubTokenDeprecated)))
            {
                Console.Error.WriteLine($"Both GitHub username and token are required");
                return((int)ExitCode.GitHubParameterMissing);
            }

            dotnetCommandService.TimeoutMilliseconds = dotnetCommandTimeout;

            // retrieve nuget package cache paths
            var packageCachePathsResult = dotnetUtilsService.GetPackageCachePaths();

            if (!packageCachePathsResult.Success)
            {
                Console.Error.WriteLine("Unable to find local package cache locations...");
                Console.Error.WriteLine(packageCachePathsResult.ErrorMessage);
                return((int)ExitCode.LocalPackageCacheError);
            }

            Console.WriteLine("Found the following local nuget package cache locations:");
            foreach (var path in packageCachePathsResult.Result)
            {
                Console.WriteLine($"    {path}");
            }

            // instantiate services

            var           fileDiscoveryService = new FileDiscoveryService(Program.fileSystem);
            GithubService githubService        = null;

            if (!(disableGithubLicenses || disableGithubLicensesDeprecated))
            {
                // GitHubService requires its own HttpClient as it adds a default authorization header
                if (!string.IsNullOrEmpty(githubBearerToken))
                {
                    githubService = new GithubService(new HttpClient(), githubBearerToken);
                }
                else if (!string.IsNullOrEmpty(githubBearerTokenDeprecated))
                {
                    githubService = new GithubService(new HttpClient(), githubBearerTokenDeprecated);
                }
                else if (!string.IsNullOrEmpty(githubUsername))
                {
                    githubService = new GithubService(new HttpClient(), githubUsername, githubToken);
                }
                else if (!string.IsNullOrEmpty(githubUsernameDeprecated))
                {
                    githubService = new GithubService(new HttpClient(), githubUsernameDeprecated, githubTokenDeprecated);
                }
                else
                {
                    githubService = new GithubService(new HttpClient());
                }
            }
            var nugetService = new NugetService(
                Program.fileSystem,
                packageCachePathsResult.Result,
                githubService,
                Program.httpClient,
                baseUrl);

            var packages = new HashSet <NugetPackage>();

            // determine what we are analyzing and do the analysis
            var fullSolutionOrProjectFilePath = Program.fileSystem.Path.GetFullPath(SolutionOrProjectFile);
            var attr = Program.fileSystem.File.GetAttributes(fullSolutionOrProjectFilePath);

            try
            {
                if (SolutionOrProjectFile.ToLowerInvariant().EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
                {
                    packages = await solutionFileService.GetSolutionNugetPackages(fullSolutionOrProjectFilePath).ConfigureAwait(false);
                }
                else if (Core.Utils.IsSupportedProjectType(SolutionOrProjectFile) && scanProjectReferences)
                {
                    packages = await projectFileService.RecursivelyGetProjectNugetPackagesAsync(fullSolutionOrProjectFilePath).ConfigureAwait(false);
                }
                else if (Core.Utils.IsSupportedProjectType(SolutionOrProjectFile))
                {
                    packages = await projectFileService.GetProjectNugetPackagesAsync(fullSolutionOrProjectFilePath).ConfigureAwait(false);
                }
                else if (Program.fileSystem.Path.GetFileName(SolutionOrProjectFile).ToLowerInvariant().Equals("packages.config", StringComparison.OrdinalIgnoreCase))
                {
                    packages = await packagesFileService.GetNugetPackagesAsync(fullSolutionOrProjectFilePath).ConfigureAwait(false);
                }
                else if (attr.HasFlag(FileAttributes.Directory))
                {
                    packages = await packagesFileService.RecursivelyGetNugetPackagesAsync(fullSolutionOrProjectFilePath).ConfigureAwait(false);
                }
                else
                {
                    Console.Error.WriteLine($"Only .sln, .csproj, .vbproj, and packages.config files are supported");
                    return((int)ExitCode.InvalidOptions);
                }
            }
            catch (DotnetRestoreException)
            {
                return((int)ExitCode.DotnetRestoreFailed);
            }

            // get all the components from the NuGet packages
            var components = new HashSet <Component>();

            try
            {
                foreach (var package in packages)
                {
                    var component = await nugetService.GetComponentAsync(package).ConfigureAwait(false);

                    if (component != null &&
                        (component.Scope != "excluded" || !excludeDev)
                        )
                    {
                        components.Add(component);
                    }
                }
            }
            catch (InvalidGitHubApiCredentialsException)
            {
                return((int)ExitCode.InvalidGitHubApiCredentials);
            }
            catch (GitHubApiRateLimitExceededException)
            {
                return((int)ExitCode.GitHubApiRateLimitExceeded);
            }
            catch (GitHubLicenseResolutionException)
            {
                return((int)ExitCode.GitHubLicenseResolutionFailed);
            }

            // create the BOM
            Console.WriteLine();
            Console.WriteLine("Creating CycloneDX BOM");
            var bom = new Bom();

            if (!(noSerialNumber || noSerialNumberDeprecated))
            {
                bom.SerialNumber = "urn:uuid:" + System.Guid.NewGuid().ToString();
            }
            bom.Components = components;

            var bomContents = BomService.CreateDocument(bom, json);

            // check if the output directory exists and create it if needed
            var bomPath = Program.fileSystem.Path.GetFullPath(outputDirectory);

            if (!Program.fileSystem.Directory.Exists(bomPath))
            {
                Program.fileSystem.Directory.CreateDirectory(bomPath);
            }

            // write the BOM to disk
            var bomFilename = Program.fileSystem.Path.Combine(bomPath, json ? "bom.json" : "bom.xml");

            Console.WriteLine("Writing to: " + bomFilename);
            Program.fileSystem.File.WriteAllText(bomFilename, bomContents);

            return(0);
        }